Skip to content

Commit

Permalink
refactor(skeleton): refacto skeleton API
Browse files Browse the repository at this point in the history
  • Loading branch information
soykje committed Nov 6, 2024
1 parent fca17d4 commit e6c96f9
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 235 deletions.
6 changes: 3 additions & 3 deletions e2e/a11y/pages/Skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import React from 'react'

export const A11ySkeleton = () => (
<section>
<Skeleton gap="lg">
<Skeleton className="gap-lg">
<Skeleton.Rectangle height={128} />

<Skeleton.Group gap="lg">
<div className="flex flex-wrap gap-lg">
<Skeleton.Circle size={64} />
<Skeleton.Line gap="md" lines={3} />
</Skeleton.Group>
</div>
</Skeleton>
</section>
)
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/components/skeleton/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"build": "vite build"
},
"dependencies": {
"@spark-ui/radio-group": "^6.3.8",
"@spark-ui/internal-utils": "^6.3.8",
"@spark-ui/visually-hidden": "^6.3.8",
"class-variance-authority": "0.7.0"
Expand Down
31 changes: 10 additions & 21 deletions packages/components/skeleton/src/Skeleton.doc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ import { Skeleton } from '@spark-ui/skeleton'
of={Skeleton}
description="A container for all Skeleton parts."
subcomponents={{
'Skeleton.Group': {
of: Skeleton.Group,
description: 'A wrapper to group Skeleton components, for advanced layouts.',
},
'Skeleton.Circle': {
of: Skeleton.Circle,
description: 'A specific Skeleton component, for circular content.',
Expand All @@ -55,27 +51,20 @@ import { Skeleton } from '@spark-ui/skeleton'

<Canvas of={stories.Default} />

### Size
### Sizing

For `<Skeleton.Rectangle />` component you can use `width|height` props to define the size.

For `<Skeleton.Circle />` component you can use `size` prop to define the size.

For `<Skeleton.Line />` component you can use `lines` prop to define the number of lines, which will be equivalent to size in this specific usecase.

<Canvas of={stories.Size} />
<Canvas of={stories.RectangleSize} />

### Gap

Use `gap` property to set gaps between group items, or lines.

<Canvas of={stories.Gap} />
For `<Skeleton.Circle />` component you can use `size` prop to define the size.

### Alignment
<Canvas of={stories.CircleSize} />

Use `alignment` property to set alignments for group items, or lines.
For `<Skeleton.Line />` component you can use `lines` prop to define the number of lines, which will
be equivalent to size in this specific usecase. To handle spacing between these lines, use `gap` specific prop.

<Canvas of={stories.Alignment} />
<Canvas of={stories.LineSize} />

### Animation

Expand All @@ -85,11 +74,11 @@ Use `isAnimated` prop display an animation on your `<Skeleton />` content.

## Advanced usage

### Group
### Composing layouts

For advanced `<Skeleton />` layouts you can group items to fit your specific needs. You can then define gaps, direction and alignment properties for this group, using dedicated properties `gap`, `direction` and `alignment`.
For specific needs you may want to build an entire layout using skeleton items (eg. cards, ...). You may then simply add your own solution (such as a specific markup using flexbox or grid CSS modules) in your implementation.

<Canvas of={stories.Group} />
<Canvas of={stories.Layout} />

## Accessibility

Expand Down
144 changes: 29 additions & 115 deletions packages/components/skeleton/src/Skeleton.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { StoryLabel } from '@docs/helpers/StoryLabel'
import { RadioGroup } from '@spark-ui/radio-group'
import { Meta, StoryFn } from '@storybook/react'
import { useState } from 'react'

import { Skeleton } from '.'
import { SkeletonGroupProps } from './SkeletonGroup'

const meta: Meta<typeof Skeleton> = {
title: 'Experimental/Skeleton',
Expand All @@ -14,140 +11,57 @@ const meta: Meta<typeof Skeleton> = {
export default meta

export const Default: StoryFn = _args => (
<Skeleton>
<Skeleton label="Loading...">
<Skeleton.Rectangle height={128} />
</Skeleton>
)

export const Size: StoryFn = _args => (
<div className="grid grid-cols-2 gap-xl md:grid-cols-3">
<div>
<Skeleton gap="xl">
<Skeleton.Circle size={32} />
<Skeleton.Circle size={64} />
<Skeleton.Circle size={128} />
</Skeleton>
</div>

<div>
<Skeleton gap="xl">
<Skeleton.Rectangle height={32} />
<Skeleton.Rectangle height={64} />
<Skeleton.Rectangle height={128} />
</Skeleton>
</div>

<div>
<Skeleton gap="xl">
<Skeleton.Line />
<Skeleton.Line lines={3} />
<Skeleton.Line lines={6} />
</Skeleton>
</div>
</div>
export const CircleSize: StoryFn = _args => (
<Skeleton label="Loading...">
<Skeleton.Circle size={128} />
</Skeleton>
)

const gaps: ExcludeNull<SkeletonGroupProps['gap']>[] = ['sm', 'md', 'lg', 'xl', '2xl', '3xl']
export const Gap: StoryFn = _args => {
return (
<div className="grid grid-cols-2 gap-xl md:grid-cols-3">
{gaps.map(gap => (
<div key={gap}>
<StoryLabel>{gap}</StoryLabel>
<Skeleton key={`skeleton_${gap}`}>
<Skeleton.Line gap={gap} lines={3} />
</Skeleton>
</div>
))}
</div>
)
}

const alignments: ExcludeNull<SkeletonGroupProps['align']>[] = ['start', 'center', 'end']
export const Alignment: StoryFn = _args => (
<div className="grid grid-cols-2 gap-xl md:grid-cols-3">
{alignments.map(alignment => (
<div key={alignment}>
<StoryLabel>{alignment}</StoryLabel>
<Skeleton gap="lg">
<Skeleton.Line gap="md" align={alignment} lines={3} />
</Skeleton>
</div>
))}
</div>
export const RectangleSize: StoryFn = _args => (
<Skeleton label="Loading...">
<Skeleton.Rectangle width="50%" height={64} />
</Skeleton>
)

export const Group: StoryFn = _args => {
const [alignment, setAlignment] = useState<SkeletonGroupProps['align']>('start')

return (
<div>
<RadioGroup
className="mb-lg flex gap-md"
value={alignment}
orientation="horizontal"
onValueChange={value => setAlignment(value as SkeletonGroupProps['align'])}
>
<RadioGroup.Radio value="start">Start</RadioGroup.Radio>
<RadioGroup.Radio value="center">Center</RadioGroup.Radio>
<RadioGroup.Radio value="end">End</RadioGroup.Radio>
</RadioGroup>

<div className="grid grid-cols-2 gap-xl">
<div>
<StoryLabel>with direction `row` (default)</StoryLabel>
<Skeleton gap="lg">
<Skeleton.Group gap="lg" align={alignment}>
<Skeleton.Rectangle height={128} />
<Skeleton.Rectangle height={48} />
<Skeleton.Rectangle height={64} />
</Skeleton.Group>
</Skeleton>
</div>

<div>
<StoryLabel>with direction `column`</StoryLabel>
<Skeleton gap="lg">
<Skeleton.Group gap="lg" direction="column" align={alignment}>
<Skeleton.Rectangle height={32} width="90%" />
<Skeleton.Rectangle height={32} width="75%" />
<Skeleton.Rectangle height={32} />
</Skeleton.Group>
</Skeleton>
</div>
</div>

<div className="mt-2xl">
<StoryLabel>with specific gaps</StoryLabel>
<Skeleton gap="xl">
<Skeleton.Rectangle height={128} />

<Skeleton.Group gap="xl">
<Skeleton.Circle size={64} />

<Skeleton.Line gap="md" lines={3} />
</Skeleton.Group>
</Skeleton>
</div>
</div>
)
}
export const LineSize: StoryFn = _args => (
<Skeleton label="Loading...">
<Skeleton.Line lines={5} gap="md" />
</Skeleton>
)

export const Animation: StoryFn = _args => (
<div className="grid grid-cols-2 gap-xl">
<div>
<StoryLabel>with animation (default)</StoryLabel>

<Skeleton gap="lg">
<Skeleton label="Loading..." className="gap-lg">
<Skeleton.Rectangle height={128} />
</Skeleton>
</div>

<div>
<StoryLabel>without animation</StoryLabel>
<Skeleton isAnimated={false} gap="lg">
<Skeleton label="Loading..." isAnimated={false} className="gap-lg">
<Skeleton.Rectangle height={128} />
</Skeleton>
</div>
</div>
)

export const Layout: StoryFn = _args => (
<Skeleton label="Loading..." className="flex flex-col gap-xl">
<div className="flex w-1/2 gap-lg">
<Skeleton.Circle size={40} />
<Skeleton.Line lines={2} />
</div>

<Skeleton.Rectangle height={64} />

<Skeleton.Line lines={5} />
</Skeleton>
)
2 changes: 1 addition & 1 deletion packages/components/skeleton/src/Skeleton.styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cva, VariantProps } from 'class-variance-authority'

export const skeletonStyles = cva(['flex flex-row !flex-wrap'], {
export const skeletonStyles = cva([], {
variants: {
isAnimated: {
true: [
Expand Down
10 changes: 3 additions & 7 deletions packages/components/skeleton/src/Skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import { VisuallyHidden } from '@spark-ui/visually-hidden'
import { ComponentPropsWithoutRef, forwardRef, PropsWithChildren } from 'react'

import { type SkeletonStyleProps, skeletonStyles } from './Skeleton.styles'
import { SkeletonGroup, type SkeletonGroupProps } from './SkeletonGroup'

export interface SkeletonProps
extends ComponentPropsWithoutRef<'div'>,
SkeletonStyleProps,
SkeletonGroupProps {
export interface SkeletonProps extends ComponentPropsWithoutRef<'div'>, SkeletonStyleProps {
/**
* Displays an animated light effect.
* @default true
Expand All @@ -21,7 +17,7 @@ export interface SkeletonProps

export const Skeleton = forwardRef<HTMLDivElement, PropsWithChildren<SkeletonProps>>(
({ isAnimated = true, label, className, children, ...rest }, forwardedRef) => (
<SkeletonGroup
<div
ref={forwardedRef}
data-spark-component="skeleton"
className={skeletonStyles({ isAnimated, className })}
Expand All @@ -30,7 +26,7 @@ export const Skeleton = forwardRef<HTMLDivElement, PropsWithChildren<SkeletonPro
{children}

{label && <VisuallyHidden>{label}</VisuallyHidden>}
</SkeletonGroup>
</div>
)
)

Expand Down
29 changes: 0 additions & 29 deletions packages/components/skeleton/src/SkeletonGroup.styles.ts

This file was deleted.

37 changes: 0 additions & 37 deletions packages/components/skeleton/src/SkeletonGroup.tsx

This file was deleted.

Loading

0 comments on commit e6c96f9

Please sign in to comment.