diff --git a/.changeset/swift-garlics-wave.md b/.changeset/swift-garlics-wave.md new file mode 100644 index 0000000000..30048d22dc --- /dev/null +++ b/.changeset/swift-garlics-wave.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/skeleton-loader': minor +--- + +Adds `ListSkeleton` component diff --git a/.changeset/warm-avocados-enjoy.md b/.changeset/warm-avocados-enjoy.md new file mode 100644 index 0000000000..52d76236e0 --- /dev/null +++ b/.changeset/warm-avocados-enjoy.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/skeleton-loader': minor +--- + +Adds `enableAnimations` prop diff --git a/packages/skeleton-loader/package.json b/packages/skeleton-loader/package.json index 7301b42002..d27dfd70a6 100644 --- a/packages/skeleton-loader/package.json +++ b/packages/skeleton-loader/package.json @@ -35,7 +35,8 @@ "@leafygreen-ui/lib": "^13.3.0", "@leafygreen-ui/palette": "^4.0.9", "@leafygreen-ui/tokens": "^2.5.2", - "@leafygreen-ui/typography": "^18.3.0" + "@leafygreen-ui/typography": "^18.3.0", + "lodash": "^4.17.21" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^3.1.12" diff --git a/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.stories.tsx b/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.stories.tsx index a669924eb6..16a11c76ce 100644 --- a/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.stories.tsx +++ b/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.stories.tsx @@ -1,24 +1,32 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; -import { StoryFn } from '@storybook/react'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; import { CardSkeleton } from '..'; export default { title: 'Components/SkeletonLoader', component: CardSkeleton, + parameters: { + default: null, + controls: { exclude: ['darkMode', 'ref'] }, + generate: { + storyNames: ['Card'], + combineArgs: { + darkMode: [false, true], + }, + decorator: Instance => ( +
+ +
+ ), + }, + }, + args: { + enableAnimations: false, + }, argTypes: { - darkMode: storybookArgTypes.darkMode, + enableAnimations: { control: 'boolean' }, }, - decorators: [ - (Story: StoryFn) => ( -
- -
- ), - ], -}; +} satisfies StoryMetaType; -export const Card: StoryFn = props => ( - -); +export const Card: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.tsx b/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.tsx index b19ec74179..20a4faabcd 100644 --- a/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.tsx +++ b/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.tsx @@ -11,6 +11,7 @@ import { CardSkeletonProps } from '.'; export function CardSkeleton({ darkMode: darkModeProp, + enableAnimations, className, ...rest }: CardSkeletonProps) { @@ -22,7 +23,11 @@ export function CardSkeleton({ className={cx(rootStyles, className)} aria-busy > - + ); } diff --git a/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.types.ts b/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.types.ts index 468ccf27d6..70c3adc6bd 100644 --- a/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.types.ts +++ b/packages/skeleton-loader/src/CardSkeleton/CardSkeleton.types.ts @@ -1,5 +1,8 @@ import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib'; +import { SharedSkeletonProps } from '../Skeleton/Skeleton.types'; + export interface CardSkeletonProps - extends HTMLElementProps<'div'>, + extends SharedSkeletonProps, + HTMLElementProps<'div'>, DarkModeProps {} diff --git a/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.stories.tsx b/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.stories.tsx index bdf2a4df42..3fd60fe674 100644 --- a/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.stories.tsx +++ b/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.stories.tsx @@ -1,24 +1,32 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; -import { StoryFn } from '@storybook/react'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; import { CodeSkeleton } from '..'; export default { title: 'Components/SkeletonLoader', component: CodeSkeleton, + parameters: { + default: null, + controls: { exclude: ['darkMode', 'ref'] }, + generate: { + storyNames: ['Code'], + combineArgs: { + darkMode: [false, true], + }, + decorator: Instance => ( +
+ +
+ ), + }, + }, + args: { + enableAnimations: false, + }, argTypes: { - darkMode: storybookArgTypes.darkMode, + enableAnimations: { control: 'boolean' }, }, - decorators: [ - (Story: StoryFn) => ( -
- -
- ), - ], -}; +} satisfies StoryMetaType; -export const Code: StoryFn = props => ( - -); +export const Code: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.tsx b/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.tsx index 94bf5f58b0..995d646e3b 100644 --- a/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.tsx +++ b/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.tsx @@ -5,13 +5,14 @@ import LeafyGreenProvider, { useDarkMode, } from '@leafygreen-ui/leafygreen-provider'; -import { Skeleton } from '..'; +import { Size, Skeleton } from '..'; import { lineStyles, rootStyles } from './CodeSkeleton.styles'; import { CodeSkeletonProps } from '.'; export function CodeSkeleton({ darkMode: darkModeProp, + enableAnimations, className, ...rest }: CodeSkeletonProps) { @@ -19,9 +20,21 @@ export function CodeSkeleton({ return (
- - - + + +
); diff --git a/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.types.ts b/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.types.ts index 7b19f842ca..521236d8ec 100644 --- a/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.types.ts +++ b/packages/skeleton-loader/src/CodeSkeleton/CodeSkeleton.types.ts @@ -1,5 +1,8 @@ import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib'; +import { SharedSkeletonProps } from '../Skeleton/Skeleton.types'; + export interface CodeSkeletonProps - extends DarkModeProps, + extends SharedSkeletonProps, + DarkModeProps, HTMLElementProps<'div'> {} diff --git a/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.stories.tsx b/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.stories.tsx index 23a0ec2c19..26f2d50fb4 100644 --- a/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.stories.tsx +++ b/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.stories.tsx @@ -1,24 +1,32 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; -import { StoryFn } from '@storybook/react'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; import { FormSkeleton } from '.'; export default { title: 'Components/SkeletonLoader', component: FormSkeleton, + parameters: { + controls: { exclude: ['darkMode', 'ref'] }, + default: null, + generate: { + storyNames: ['Form'], + combineArgs: { + darkMode: [false, true], + }, + decorator: Instance => ( +
+ +
+ ), + }, + }, + args: { + enableAnimations: false, + }, argTypes: { - darkMode: storybookArgTypes.darkMode, + enableAnimations: { control: 'boolean' }, }, - decorators: [ - (Story: StoryFn) => ( -
- -
- ), - ], -}; +} satisfies StoryMetaType; -export const Form: StoryFn = props => ( - -); +export const Form: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.tsx b/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.tsx index de041e1ea0..52f35566dd 100644 --- a/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.tsx +++ b/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.tsx @@ -12,6 +12,7 @@ import { FormSkeletonProps } from '.'; export function FormSkeleton({ darkMode: darkModeProp, + enableAnimations, className, ...rest }: FormSkeletonProps) { @@ -19,11 +20,17 @@ export function FormSkeleton({ return (
- - - - - + + + + +
); diff --git a/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.types.ts b/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.types.ts index aaac25198d..73610ac3a5 100644 --- a/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.types.ts +++ b/packages/skeleton-loader/src/FormSkeleton/FormSkeleton.types.ts @@ -1,5 +1,8 @@ import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib'; +import { SharedSkeletonProps } from '../Skeleton/Skeleton.types'; + export interface FormSkeletonProps - extends DarkModeProps, + extends SharedSkeletonProps, + DarkModeProps, HTMLElementProps<'div'> {} diff --git a/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.spec.tsx b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.spec.tsx new file mode 100644 index 0000000000..6e8c3a5cae --- /dev/null +++ b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.spec.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import { ListSkeleton } from '.'; + +describe('packages/skeleton-list', () => { + test('renders', () => { + const { queryByTestId } = render(); + expect(queryByTestId('lg-skeleton-list')).toBeInTheDocument(); + }); + + test('renders `count` items', () => { + const { queryAllByTestId } = render(); + const listItems = queryAllByTestId('lg-skeleton-list_item'); + expect(listItems.length).toBe(3); + }); +}); diff --git a/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.stories.tsx b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.stories.tsx new file mode 100644 index 0000000000..86886cef7b --- /dev/null +++ b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; + +import { ListSkeleton } from '.'; + +export default { + title: 'Components/SkeletonLoader', + component: ListSkeleton, + parameters: { + default: null, + controls: { exclude: ['darkMode', 'ref', 'size'] }, + generate: { + storyNames: ['List'], + combineArgs: { + darkMode: [false, true], + bulletsOnly: [false, true], + }, + decorator: Instance => ( +
+ +
+ ), + }, + }, + args: { + enableAnimations: false, + count: 5, + }, + argTypes: { + enableAnimations: { control: 'boolean' }, + count: { control: 'number' }, + }, +} satisfies StoryMetaType; + +export const List: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.styles.ts b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.styles.ts new file mode 100644 index 0000000000..cc3c11c18e --- /dev/null +++ b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.styles.ts @@ -0,0 +1,40 @@ +import { css } from '@leafygreen-ui/emotion'; +import { spacing } from '@leafygreen-ui/tokens'; + +export const skeletonListWrapperStyles = css` + width: 100%; + padding: 0; + margin: 0; +`; + +export const getSkeletonListItemStyles = ( + index = 0, + bulletsOnly?: boolean, +) => css` + list-style: none; + margin-block: ${spacing[300]}px; + + width: ${getWidth(index, bulletsOnly)}; +`; + +const getWidth = (index = 0, bulletsOnly?: boolean) => { + if (bulletsOnly) { + return spacing[400] + 'px'; + } + + /** + * The first item will take up 100% of the available width. + * Subsequent items will take up 1/4 less space, + * until the item is 50% the available width. + * Then repeat + * + * ---------- + * ------- + * ----- + * ---------- + * ------- + * ... etc + */ + const offset = 25 * (index % 3); + return 100 - offset + '%'; +}; diff --git a/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.tsx b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.tsx new file mode 100644 index 0000000000..8d42fc41b3 --- /dev/null +++ b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import range from 'lodash/range'; + +import LeafyGreenProvider, { + useDarkMode, +} from '@leafygreen-ui/leafygreen-provider'; +import { Size } from '@leafygreen-ui/tokens'; + +import { Skeleton } from '../Skeleton'; + +import { + getSkeletonListItemStyles, + skeletonListWrapperStyles, +} from './ListSkeleton.styles'; +import { ListSkeletonProps } from './ListSkeleton.types'; + +export function ListSkeleton({ + darkMode: darkModeProp, + enableAnimations, + count = 5, + bulletsOnly, + ...rest +}: ListSkeletonProps) { + const { darkMode } = useDarkMode(darkModeProp); + + return ( + +
    + {range(count).map(i => ( +
  • + +
  • + ))} +
+
+ ); +} + +ListSkeleton.displayName = 'ListSkeleton'; diff --git a/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.types.ts b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.types.ts new file mode 100644 index 0000000000..cb1ea3c77a --- /dev/null +++ b/packages/skeleton-loader/src/ListSkeleton/ListSkeleton.types.ts @@ -0,0 +1,18 @@ +import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib'; + +import { SharedSkeletonProps } from '../Skeleton/Skeleton.types'; + +export interface ListSkeletonProps + extends SharedSkeletonProps, + DarkModeProps, + HTMLElementProps<'ul'> { + /** + * Defines the number of skeleton list items to render + */ + count?: number; + /** + * Defines whether to render the full list item, or only a "bullet" skeleton. + * (A "bullet" skeleton renders as just a 16x16 rounded rectangle) + */ + bulletsOnly?: boolean; +} diff --git a/packages/skeleton-loader/src/ListSkeleton/index.ts b/packages/skeleton-loader/src/ListSkeleton/index.ts new file mode 100644 index 0000000000..faf10e90eb --- /dev/null +++ b/packages/skeleton-loader/src/ListSkeleton/index.ts @@ -0,0 +1,2 @@ +export { ListSkeleton } from './ListSkeleton'; +export { ListSkeletonProps } from './ListSkeleton.types'; diff --git a/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.stories.tsx b/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.stories.tsx index f5bf769512..288660c760 100644 --- a/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.stories.tsx +++ b/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.stories.tsx @@ -1,25 +1,33 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; -import { StoryFn } from '@storybook/react'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; -import { ParagraphSkeleton } from '.'; +import { ParagraphSkeleton } from './ParagraphSkeleton'; export default { title: 'Components/SkeletonLoader', component: ParagraphSkeleton, + parameters: { + controls: { exclude: ['darkMode', 'ref'] }, + default: null, + generate: { + storyNames: ['Paragraph'], + combineArgs: { + darkMode: [false, true], + withHeader: [true, false], + }, + decorator: Instance => ( +
+ +
+ ), + }, + }, + args: { + enableAnimations: false, + }, argTypes: { - darkMode: storybookArgTypes.darkMode, - withHeader: { control: 'boolean' }, + enableAnimations: { control: 'boolean' }, }, - decorators: [ - (Story: StoryFn) => ( -
- -
- ), - ], -}; +} satisfies StoryMetaType; -export const Paragraph: StoryFn = props => ( - -); +export const Paragraph: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.tsx b/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.tsx index e4501fc64b..4ca91e933a 100644 --- a/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.tsx +++ b/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.tsx @@ -5,7 +5,7 @@ import LeafyGreenProvider, { useDarkMode, } from '@leafygreen-ui/leafygreen-provider'; -import { Skeleton } from '..'; +import { Size, Skeleton } from '..'; import { headerStyles, @@ -16,8 +16,9 @@ import { import { ParagraphSkeletonProps } from '.'; export function ParagraphSkeleton({ - withHeader = false, darkMode: darkModeProp, + enableAnimations, + withHeader = false, className, ...rest }: ParagraphSkeletonProps) { @@ -27,13 +28,26 @@ export function ParagraphSkeleton({
{withHeader && ( )} - - - + + +
); diff --git a/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.types.ts b/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.types.ts index 3250f59ee5..dec6ce1e75 100644 --- a/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.types.ts +++ b/packages/skeleton-loader/src/ParagraphSkeleton/ParagraphSkeleton.types.ts @@ -1,7 +1,10 @@ import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib'; +import { SharedSkeletonProps } from '../Skeleton/Skeleton.types'; + export interface ParagraphSkeletonProps - extends DarkModeProps, + extends SharedSkeletonProps, + DarkModeProps, HTMLElementProps<'div'> { /** * Determines whether the header skeleton should be rendered diff --git a/packages/skeleton-loader/src/Skeleton/Skeleton.stories.tsx b/packages/skeleton-loader/src/Skeleton/Skeleton.stories.tsx index 7d403b59fc..befd59aa7c 100644 --- a/packages/skeleton-loader/src/Skeleton/Skeleton.stories.tsx +++ b/packages/skeleton-loader/src/Skeleton/Skeleton.stories.tsx @@ -1,27 +1,36 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; -import { StoryFn } from '@storybook/react'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; import { Size, Skeleton } from '.'; export default { title: 'Components/SkeletonLoader', component: Skeleton, - argTypes: { - darkMode: storybookArgTypes.darkMode, - size: { - control: 'select', - options: Object.values(Size), - defaultValue: Size.Default, + parameters: { + controls: { exclude: ['darkMode', 'ref', 'size'] }, + default: null, + generate: { + storyNames: ['Basic'], + combineArgs: { + darkMode: [false, true], + size: Object.values(Size), + }, + args: { + enableAnimations: false, + }, + decorator: Instance => ( +
+ +
+ ), }, }, - decorators: [ - (Story: StoryFn) => ( -
- -
- ), - ], -}; + args: { + enableAnimations: false, + }, + argTypes: { + enableAnimations: { control: 'boolean' }, + }, +} satisfies StoryMetaType; -export const Basic: StoryFn = props => ; +export const Basic: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/Skeleton/Skeleton.styles.ts b/packages/skeleton-loader/src/Skeleton/Skeleton.styles.ts index f3de567f7a..04abffa4df 100644 --- a/packages/skeleton-loader/src/Skeleton/Skeleton.styles.ts +++ b/packages/skeleton-loader/src/Skeleton/Skeleton.styles.ts @@ -5,27 +5,42 @@ import { spacing } from '@leafygreen-ui/tokens'; import { Size } from './Skeleton.types'; -export const rootStyles = css` +interface SkeletonStyleArgs { + enableAnimations: boolean; +} +export const getSkeletonBaseStyles = ({ + enableAnimations, +}: SkeletonStyleArgs) => css` width: 100%; - border-radius: 6px; - animation: bgslide 1.5s infinite linear; + border-radius: ${spacing[150]}px; + background-position: 50vw 0; - @keyframes bgslide { - to { - background-position: 100vw 0; + ${enableAnimations && + css` + animation: SkeletonShimmer 1.5s infinite linear; + + @keyframes SkeletonShimmer { + to { + background-position: 100vw 0; + } } - } + `} +`; + +export const rootStyles = css` + width: 100%; + border-radius: 6px; `; export const sizeStyles: Record = { [Size.Small]: css` - height: ${spacing[3]}px; + height: ${spacing[400]}px; `, [Size.Default]: css` - height: ${spacing[5]}px; + height: ${spacing[800]}px; `, [Size.Large]: css` - height: ${spacing[5] + spacing[3]}px; + height: ${spacing[1200]}px; `, }; diff --git a/packages/skeleton-loader/src/Skeleton/Skeleton.tsx b/packages/skeleton-loader/src/Skeleton/Skeleton.tsx index f27bf8c613..80d5dd1b2f 100644 --- a/packages/skeleton-loader/src/Skeleton/Skeleton.tsx +++ b/packages/skeleton-loader/src/Skeleton/Skeleton.tsx @@ -3,11 +3,16 @@ import React from 'react'; import { cx } from '@leafygreen-ui/emotion'; import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; -import { rootStyles, sizeStyles, themeStyles } from './Skeleton.styles'; +import { + getSkeletonBaseStyles, + sizeStyles, + themeStyles, +} from './Skeleton.styles'; import { Size } from './Skeleton.types'; import { SkeletonProps } from '.'; export function Skeleton({ + enableAnimations = true, size = Size.Default, darkMode, className, @@ -17,7 +22,7 @@ export function Skeleton({ return (
{ +export interface SharedSkeletonProps { + /** + * Defines whether the loading "shimmer" animation renders + * + * @default true + */ + enableAnimations?: boolean; +} + +export interface SkeletonProps + extends SharedSkeletonProps, + DarkModeProps, + HTMLElementProps<'div'> { /** * Determines the height of the skeleton * @default "default" diff --git a/packages/skeleton-loader/src/SkeletonLoader.story.tsx b/packages/skeleton-loader/src/SkeletonLoader.story.tsx index 26eb49d51e..ae4b917bc1 100644 --- a/packages/skeleton-loader/src/SkeletonLoader.story.tsx +++ b/packages/skeleton-loader/src/SkeletonLoader.story.tsx @@ -1,16 +1,16 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; -import { StoryFn } from '@storybook/react'; +import { storybookArgTypes, StoryType } from '@lg-tools/storybook-utils'; import { css } from '@leafygreen-ui/emotion'; -import { DarkModeProps } from '@leafygreen-ui/lib'; import { spacing } from '@leafygreen-ui/tokens'; import { Body, InlineCode } from '@leafygreen-ui/typography'; +import { SharedSkeletonProps } from './Skeleton/Skeleton.types'; import { CardSkeleton, CodeSkeleton, FormSkeleton, + ListSkeleton, ParagraphSkeleton, Skeleton, TableSkeleton, @@ -20,6 +20,10 @@ export default { title: 'Components/SkeletonLoader', argTypes: { darkMode: storybookArgTypes.darkMode, + enableAnimations: { control: 'boolean' }, + }, + args: { + enableAnimations: true, }, parameters: { default: 'LiveExample', @@ -46,44 +50,26 @@ const labelStyles = css` margin-top: ${spacing[5]}px; `; -export const LiveExample: StoryFn = (props: DarkModeProps) => ( +const skeletonComponents = { + Skeleton, + CardSkeleton, + CodeSkeleton, + FormSkeleton, + ListSkeleton, + ParagraphSkeleton, + TableSkeleton, +}; + +export const LiveExample: StoryType = (args: SharedSkeletonProps) => (
-
- - - Skeleton - -
-
- - - ParagraphSkeleton - -
-
- - - CardSkeleton - -
-
- - - FormSkeleton - -
-
- - - TableSkeleton - -
-
- - - CodeSkeleton - -
+ {Object.entries(skeletonComponents).map(([name, SkeletonVariant]) => ( +
+ + + {name} + +
+ ))}
); LiveExample.parameters = { diff --git a/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.stories.tsx b/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.stories.tsx index f94ed16345..35bb74e46e 100644 --- a/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.stories.tsx +++ b/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.stories.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { storybookArgTypes } from '@lg-tools/storybook-utils'; +import { StoryMetaType, StoryType } from '@lg-tools/storybook-utils'; import { StoryFn } from '@storybook/react'; import { TableSkeleton } from '..'; @@ -7,12 +7,27 @@ import { TableSkeleton } from '..'; export default { title: 'Components/SkeletonLoader', component: TableSkeleton, + parameters: { + default: null, + controls: { exclude: ['darkMode', 'ref'] }, + generate: { + storyNames: ['Table'], + combineArgs: { + darkMode: [false, true], + columnLabels: [undefined, ['Column 1', 'Column 2', 'Column 3', '']], + }, + }, + }, + args: { + enableAnimations: false, + numCols: 4, + numRows: 5, + }, argTypes: { - darkMode: storybookArgTypes.darkMode, - baseFontSize: storybookArgTypes.updatedBaseFontSize, columnLabels: { control: 'none' }, numCols: { control: 'number' }, numRows: { control: 'number' }, + enableAnimations: { control: 'boolean' }, }, decorators: [ (Story: StoryFn) => ( @@ -21,17 +36,6 @@ export default {
), ], -}; - -const TableTemplate: StoryFn = props => ( - -); - -export const TableWithoutLabels: StoryFn = - TableTemplate.bind({}); +} satisfies StoryMetaType; -export const TableWithLabels: StoryFn = - TableTemplate.bind({}); -TableWithLabels.args = { - columnLabels: ['Column 1', 'Column 2', 'Column 3', ''], -}; +export const Table: StoryType = () => <>; diff --git a/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.tsx b/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.tsx index 2a3cc38680..db1838ecfd 100644 --- a/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.tsx +++ b/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.tsx @@ -22,6 +22,7 @@ import { TableSkeletonProps } from '.'; export function TableSkeleton({ darkMode: darkModeProp, baseFontSize: baseFontSizeProp = BaseFontSize.Body1, + enableAnimations, numRows = 5, numCols = 4, columnLabels, @@ -45,7 +46,7 @@ export function TableSkeleton({ {columnLabels[i]} ) : ( - + )} ))} @@ -57,6 +58,7 @@ export function TableSkeleton({ {[...Array(numCols)].map((_, j) => ( diff --git a/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.types.ts b/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.types.ts index e2e1d60ce8..b75d4aa9e3 100644 --- a/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.types.ts +++ b/packages/skeleton-loader/src/TableSkeleton/TableSkeleton.types.ts @@ -1,8 +1,11 @@ import { DarkModeProps, HTMLElementProps } from '@leafygreen-ui/lib'; import { BaseFontSize } from '@leafygreen-ui/tokens'; +import { SharedSkeletonProps } from '../Skeleton/Skeleton.types'; + export interface TableSkeletonProps - extends DarkModeProps, + extends SharedSkeletonProps, + DarkModeProps, HTMLElementProps<'table'> { /** * Base font size diff --git a/packages/skeleton-loader/src/index.ts b/packages/skeleton-loader/src/index.ts index 9619280092..2f9723f666 100644 --- a/packages/skeleton-loader/src/index.ts +++ b/packages/skeleton-loader/src/index.ts @@ -1,6 +1,7 @@ export { CardSkeleton, type CardSkeletonProps } from './CardSkeleton'; export { CodeSkeleton, type CodeSkeletonProps } from './CodeSkeleton'; export { FormSkeleton, type FormSkeletonProps } from './FormSkeleton'; +export { ListSkeleton, type ListSkeletonProps } from './ListSkeleton'; export { ParagraphSkeleton, type ParagraphSkeletonProps,