Skip to content

Commit

Permalink
feat: add AccordionSkeleton and SkeletonText components (carbon-desig…
Browse files Browse the repository at this point in the history
…n-system#178)

* feat: add AccordionSkeleton and SkeletonText components

* feat: update CvSkeletonText component

* chore: turned CvAccordionSkeleton to a functional component

* chore: turn CvAccordionItemcSkeleton to a functional component

* chore: use correct notation for functional components

* chore: revert functional in CvAccordionItemSkeleton
  • Loading branch information
sabov authored and dcwarwick committed Mar 18, 2019
1 parent 3eb9591 commit e8814d1
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/components/cv-accordion/_cv-accordion-item-skeleton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<li
class="bx--accordion__item"
:class="{ 'bx--accordion__item--active': open }"
>
<button type="button" class="bx--accordion__heading">
<svg
class="bx--accordion__arrow"
width="8"
height="12"
viewBox="0 0 8 12"
fill-rule="evenodd"
>
<path d="M0 10.6L4.7 6 0 1.4 1.4 0l6.1 6-6.1 6z"></path>
</svg>
<cv-skeleton-text class="bx--accordion__title"></cv-skeleton-text>
</button>
<div class="bx--accordion__content">
<slot></slot>
</div>
</li>
</template>

<script>
import CvSkeletonText from '../cv-skeleton-text/cv-skeleton-text';
export default {
name: 'CvAccordionItemSkeleton',
components: {
CvSkeletonText,
},
props: {
open: { type: Boolean, default: false },
},
};
</script>

<style lang="scss"></style>
27 changes: 27 additions & 0 deletions src/components/cv-accordion/cv-accordion-skeleton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<ul class="bx--accordion bx--skeleton">
<cv-accordion-item-skeleton :open="true">
<cv-skeleton-text width="90%"></cv-skeleton-text>
<cv-skeleton-text width="80%"></cv-skeleton-text>
<cv-skeleton-text width="95%"></cv-skeleton-text>
</cv-accordion-item-skeleton>
<cv-accordion-item-skeleton></cv-accordion-item-skeleton>
<cv-accordion-item-skeleton></cv-accordion-item-skeleton>
<cv-accordion-item-skeleton></cv-accordion-item-skeleton>
</ul>
</template>

<script>
import CvSkeletonText from '../cv-skeleton-text/cv-skeleton-text';
import CvAccordionItemSkeleton from './_cv-accordion-item-skeleton';
export default {
name: 'CvAccordionSkeleton',
components: {
CvSkeletonText,
CvAccordionItemSkeleton,
},
};
</script>

<style lang="scss"></style>
21 changes: 21 additions & 0 deletions src/components/cv-accordion/cv-accordion-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import knobsHelper from '../../_storybook/utils/knobs-helper';
import CvAccordionNotesMD from './cv-accordion-notes.md';
import CvAccordion from './cv-accordion';
import CvAccordionItem from './cv-accordion-item';
import CvAccordionSkeleton from './cv-accordion-skeleton';

const stories = storiesOf('CvAccordion', module);
stories.addDecorator(withKnobs).addDecorator(withNotes);
Expand Down Expand Up @@ -117,3 +118,23 @@ for (const story of storySet) {
}
);
}

const templateString = `<cv-accordion-skeleton></cv-accordion-skeleton>`;
stories.add(
'skeleton',
() => ({
components: { SvTemplateView, CvAccordionSkeleton },
template: `
<sv-template-view
sv-margin
sv-position="center"
sv-source='${templateString.trim()}'>
<template slot="component">${templateString}</template>
</sv-template-view>
`,
props: {},
}),
{
notes: { markdown: CvAccordionNotesMD },
}
);
21 changes: 21 additions & 0 deletions src/components/cv-skeleton-text/cv-skeleton-text-notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# CvSkeletonText

A utility component that used as a progressive loading state while the user waits for content to load.

## Usage

```html
<cv-skeleton-text
:heading="true"
:width="90%"
:paragraph="true"
:lineCount="3"
></cv-skeleton-text>
```

## Attributes

- heading: generates skeleton text at a larger size. Optional. Default - false.
- width: width (in px or %) of single line of text or max-width of paragraph lines. Optional. Default - 100%.
- paragraph: will generate multiple lines of text. Optional. Default - false.
- lineCount: the number of lines in a paragraph. Optional. Default - 3.
90 changes: 90 additions & 0 deletions src/components/cv-skeleton-text/cv-skeleton-text-story.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { storiesOf } from '@storybook/vue';
import { withKnobs, boolean, text, number } from '@storybook/addon-knobs';
import { withNotes } from '@storybook/addon-notes';

import SvTemplateView from '../../_storybook/views/sv-template-view/sv-template-view';
import knobsHelper from '../../_storybook/utils/knobs-helper';

import CvSkeletonTextNotesMD from './cv-skeleton-text-notes.md';
import CvSkeletonText from './cv-skeleton-text';

const stories = storiesOf('CvSkeletonText', module);
stories.addDecorator(withKnobs);
stories.addDecorator(withNotes);

const preKnobs = {
heading: {
group: 'attr',
type: boolean,
config: ['Skeleton text at a larger size (heading)', false], // consts.CONFIG
prop: {
name: 'heading',
type: Boolean,
},
},
paragraph: {
group: 'attr',
type: boolean,
config: ['Use multiple lines of text (paragraph)', false], // consts.CONFIG
prop: {
name: 'paragraph',
type: Boolean,
},
},
lineCount: {
group: 'attr',
type: number,
config: ['The number of lines in a paragraph (lineCount)', 3],
prop: { name: 'line-count', type: Number },
},
width: {
group: 'attr',
type: text,
config: [
'Width (in px or %) of single line of text or max-width of paragraph lines (width)',
'100%',
], // consts.CONFIG
prop: { name: 'width', type: String },
},
};

const variants = [
{ name: 'default' },
{ name: 'minimal', excludes: ['heading', 'width', 'paragraph', 'lineCount'] },
];

const storySet = knobsHelper.getStorySet(variants, preKnobs);

for (const story of storySet) {
stories.add(
story.name,
() => {
const settings = story.knobs();

// ----------------------------------------------------------------

const templateString = `
<cv-skeleton-text${settings.group.attr}></cv-skeleton-text>
`;

// ----------------------------------------------------------------

const templateViewString = `
<sv-template-view
sv-margin
sv-source='${templateString.trim()}'>
<template slot="component">${templateString}</template>
</sv-template-view>
`;

return {
components: { CvSkeletonText, SvTemplateView },
template: templateViewString,
props: settings.props,
};
},
{
notes: { markdown: CvSkeletonTextNotesMD },
}
);
}
65 changes: 65 additions & 0 deletions src/components/cv-skeleton-text/cv-skeleton-text.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<template>
<div :style="{ width: width }">
<p
class="bx--skeleton__text"
:class="{ 'bx--skeleton__heading': heading }"
:style="{ width: line.width }"
:key="index"
v-for="(line, index) in lines"
></p>
</div>
</template>

<script>
export default {
name: 'CvSkeletonText',
props: {
width: { type: String, default: '100%' },
heading: { type: Boolean, default: false },
lineCount: { type: Number, default: 3 },
paragraph: { type: Boolean, default: false },
},
methods: {
getRandomInt: function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
calcWidth: function() {
let width = this.width;
if (this.paragraph) {
const { num, unit } = this.widthObj;
if (unit === '%') {
const randomWidth = this.getRandomInt(0, 75) + 'px';
width = `calc(${width} - ${randomWidth})`;
} else if (unit === 'px') {
width = this.getRandomInt(num - 75, num) + 'px';
}
}
return width;
},
},
computed: {
widthObj: function() {
const widthObj = { num: parseInt(this.width, 10) };
if (this.width.includes('px')) {
widthObj.unit = 'px';
}
if (this.width.includes('%')) {
widthObj.unit = '%';
}
return widthObj;
},
lines: function() {
return Array.from(
{
length: this.paragraph ? this.lineCount : 1,
},
() => ({
width: this.calcWidth(),
})
);
},
},
};
</script>

<style lang="scss"></style>

0 comments on commit e8814d1

Please sign in to comment.