Skip to content

Commit 658085c

Browse files
Merge pull request #220 from commitd/stuarthendren/issue218
Adds Skeleton component
2 parents 1c1d0d6 + 9619619 commit 658085c

File tree

9 files changed

+2028
-1744
lines changed

9 files changed

+2028
-1744
lines changed

package-lock.json

Lines changed: 1793 additions & 1714 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,17 @@
155155
"@fontsource/dosis": "^4.4.5",
156156
"@fontsource/inter": "^4.4.5",
157157
"@size-limit/preset-small-lib": "^5.0.1",
158-
"@storybook/addon-docs": "^6.3.2",
159-
"@storybook/addon-essentials": "^6.3.2",
160-
"@storybook/react": "^6.3.2",
158+
"@storybook/addon-docs": "^6.3.8",
159+
"@storybook/addon-essentials": "^6.3.8",
160+
"@storybook/react": "^6.3.8",
161161
"@storybook/storybook-deployer": "^2.8.10",
162162
"@storybook/testing-react": "^0.0.22",
163-
"@storybook/theming": "^6.3.2",
163+
"@storybook/theming": "^6.3.8",
164164
"@testing-library/jest-dom": "^5.14.1",
165165
"@testing-library/react": "^12.0.0",
166-
"@testing-library/user-event": "^13.1.9",
167-
"@types/faker": "^5.5.6",
168-
"@types/parse-color": "^1.0.0",
166+
"@testing-library/user-event": "^13.2.1",
167+
"@types/faker": "^5.5.8",
168+
"@types/parse-color": "^1.0.1",
169169
"@types/react": "^17.0.11",
170170
"@types/react-dom": "^17.0.8",
171171
"@types/react-router-dom": "^5.1.7",

src/components/Avatar/Avatar.stories.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,13 @@ export const Default = () => (
1414
)
1515

1616
/**
17-
* Size can be controlled through the `css` prop, the default is `$7`.
17+
* Size can be controlled through the `size` prop, with values `small`, `medium` (default) and `large`.
1818
*/
1919
export const Size = () => (
20-
<Flex>
21-
<Avatar
22-
css={{ m: '$3', size: '$6' }}
23-
alt="John Smith"
24-
src="https://i.pravatar.cc"
25-
/>
26-
<Avatar css={{ m: '$3' }} alt="John Smith" src="https://i.pravatar.cc" />
27-
<Avatar
28-
css={{ m: '$3', size: '$8' }}
29-
alt="John Smith"
30-
src="https://i.pravatar.cc"
31-
/>
20+
<Flex css={{ gap: '$3' }}>
21+
<Avatar size="small" alt="John Smith" src="https://i.pravatar.cc" />
22+
<Avatar alt="John Smith" src="https://i.pravatar.cc" />
23+
<Avatar size="large" alt="John Smith" src="https://i.pravatar.cc" />
3224
</Flex>
3325
)
3426

src/components/Avatar/Avatar.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
import { Fallback, Image, Root } from '@radix-ui/react-avatar'
2+
import { VariantProps } from '@stitches/react'
23
import React from 'react'
34
import type { CSS } from '../../stitches.config'
45
import { styled } from '../../stitches.config'
56

6-
interface AvatarProps {
7-
src?: string
8-
alt?: string
9-
css?: CSS
10-
color?: CSS['color']
11-
backgroundColor?: CSS['backgroundColor']
12-
}
13-
147
const StyledRoot = styled(Root, {
158
display: 'inline-flex',
169
alignItems: 'center',
@@ -20,8 +13,16 @@ const StyledRoot = styled(Root, {
2013
userSelect: 'none',
2114
borderRadius: '$round',
2215

23-
width: '$7',
24-
height: '$7',
16+
variants: {
17+
size: {
18+
small: { size: '$5' },
19+
medium: { size: '$7' },
20+
large: { size: '$9' },
21+
},
22+
},
23+
defaultVariants: {
24+
size: 'medium',
25+
},
2526
})
2627

2728
const StyledImage = styled(Image, {
@@ -38,6 +39,14 @@ const StyledFallback = styled(Fallback, {
3839
justifyContent: 'center',
3940
})
4041

42+
interface AvatarProps extends VariantProps<typeof StyledRoot> {
43+
src?: string
44+
alt?: string
45+
css?: CSS
46+
color?: CSS['color']
47+
backgroundColor?: CSS['backgroundColor']
48+
}
49+
4150
/**
4251
* The Avatar should be used for profile images. If an image is not available initials can be used.
4352
*/
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react'
2+
import { Flex } from '../Flex'
3+
import { Story, Meta } from '@storybook/react'
4+
import { Skeleton } from '.'
5+
6+
export default {
7+
title: 'Components/Skeleton',
8+
component: Skeleton,
9+
} as Meta
10+
11+
const Template: Story = (args) => <Skeleton {...args} />
12+
13+
export const Primary = Template.bind({})
14+
15+
export const Text = Template.bind({})
16+
Text.args = {
17+
variant: 'text',
18+
}
19+
20+
export const Title = Template.bind({})
21+
Title.args = {
22+
variant: 'title',
23+
}
24+
25+
/** An alternative animation, `pulse`, is available */
26+
export const Animation = Template.bind({})
27+
Animation.args = {
28+
variant: 'title',
29+
animation: 'pulse',
30+
}
31+
32+
/** The `avatar` variant also supports an additional size prop to reflect the sizing on the `Avatar` component. */
33+
export const Avatar = () => (
34+
<Flex css={{ gap: '$3' }}>
35+
<Skeleton variant="avatar" size="small" />
36+
<Skeleton variant="avatar" />
37+
<Skeleton variant="avatar" size="large" />
38+
</Flex>
39+
)
40+
41+
/** The `button` variant also supports an additional size prop to reflect the sizing on the `Button` component. */
42+
export const Buttons = () => (
43+
<Flex css={{ gap: '$3' }}>
44+
<Skeleton variant="button" size="small" />
45+
<Skeleton variant="button" />
46+
<Skeleton variant="button" size="large" />
47+
</Flex>
48+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react'
2+
import { renderLight, renderDark } from 'test-utils'
3+
import * as stories from './Skeleton.stories'
4+
import { composeStories } from '@storybook/testing-react'
5+
const { Primary, Avatar, Buttons } = composeStories(stories)
6+
7+
it('renders light without error', () => {
8+
const { asFragment } = renderLight(<Primary />)
9+
expect(asFragment()).toBeDefined()
10+
})
11+
12+
it('renders as Avatar', () => {
13+
const { asFragment } = renderDark(<Avatar />)
14+
expect(asFragment()).toBeDefined()
15+
})
16+
17+
it('renders as button', () => {
18+
const { asFragment } = renderDark(<Buttons />)
19+
expect(asFragment()).toBeDefined()
20+
})

src/components/Skeleton/Skeleton.tsx

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { keyframes, styled } from '../../stitches.config'
2+
3+
const DEFAULT_TAG = 'div'
4+
5+
const BASE_COLOR = '$grey5'
6+
const HIGHLIGHT_COLOR = '$grey7'
7+
8+
export const ripple = keyframes({
9+
'0%': {
10+
backgroundPosition: '-1000px 0',
11+
},
12+
'100%': {
13+
backgroundPosition: '1000px 0',
14+
},
15+
})
16+
17+
export const pulse = keyframes({
18+
'0%': { opacity: 0 },
19+
'50%': { opacity: '100%' },
20+
'100%': { opacity: '100%' },
21+
})
22+
23+
/**
24+
* StyledSkeleton base component
25+
*/
26+
export const Skeleton = styled(DEFAULT_TAG, {
27+
backgroundColor: BASE_COLOR,
28+
position: 'relative',
29+
overflow: 'hidden',
30+
31+
'&::after': {
32+
bottom: 0,
33+
content: '""',
34+
left: 0,
35+
position: 'absolute',
36+
right: 0,
37+
top: 0,
38+
},
39+
variants: {
40+
variant: {
41+
avatar: {
42+
borderRadius: '$round',
43+
size: '$7',
44+
},
45+
text: {
46+
height: '$4',
47+
},
48+
title: {
49+
height: '$5',
50+
},
51+
heading: {
52+
height: '$7',
53+
},
54+
button: {
55+
borderRadius: '$default',
56+
height: '$6',
57+
width: '$8',
58+
},
59+
},
60+
size: {
61+
small: {},
62+
default: {},
63+
large: {},
64+
},
65+
animation: {
66+
ripple: {
67+
'&::after': {
68+
animationName: `${ripple}`,
69+
animationDuration: '2s',
70+
animationDirection: 'normal',
71+
animationIterationCount: 'infinite',
72+
animationTimingFunction: 'ease-in-out',
73+
backgroundImage: `linear-gradient(
74+
to right,
75+
${BASE_COLOR} 4%,
76+
${HIGHLIGHT_COLOR} 25%,
77+
${BASE_COLOR} 35%
78+
)`,
79+
backgroundSize: '1000px 100%',
80+
backgroundRepeat: 'no-repeat',
81+
borderRadius: 'inherit',
82+
lineHeight: '$default',
83+
width: '100%',
84+
},
85+
},
86+
pulse: {
87+
'&::after': {
88+
animationName: `${pulse}`,
89+
animationDuration: '1s',
90+
animationDirection: 'alternate',
91+
animationIterationCount: 'infinite',
92+
animationTimingFunction: 'ease-in-out',
93+
backgroundColor: HIGHLIGHT_COLOR,
94+
},
95+
},
96+
},
97+
},
98+
compoundVariants: [
99+
{
100+
variant: 'button',
101+
size: 'small',
102+
css: {
103+
height: '$5',
104+
width: '$7',
105+
},
106+
},
107+
{
108+
variant: 'button',
109+
size: 'large',
110+
css: {
111+
height: '$7',
112+
width: '$9',
113+
},
114+
},
115+
{
116+
variant: 'avatar',
117+
size: 'small',
118+
css: {
119+
size: '$5',
120+
},
121+
},
122+
{
123+
variant: 'avatar',
124+
size: 'large',
125+
css: {
126+
size: '$9',
127+
},
128+
},
129+
],
130+
defaultVariants: {
131+
variant: 'text',
132+
animation: 'ripple',
133+
},
134+
})

src/components/Skeleton/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Skeleton'

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ export * from './ThemeProvider'
4545
export * from './Tooltip'
4646
export * from './ThemeSwitch'
4747
export * from './Popover'
48+
export * from './Skeleton'

0 commit comments

Comments
 (0)