Skip to content

Commit c376f7d

Browse files
committed
feat: use chromatic modes instead of having mobile/desktop stories
1 parent df3d2f7 commit c376f7d

File tree

8 files changed

+140
-65
lines changed

8 files changed

+140
-65
lines changed

.storybook/preview.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { Box, Skeleton } from '@chakra-ui/react'
66
import { ThemeProvider } from '@opengovsg/design-system-react'
77
import { withThemeFromJSXProvider } from '@storybook/addon-themes'
88
import {
9-
Loader,
9+
type Loader,
1010
type Args,
1111
type Decorator,
1212
type ReactRenderer,
13+
type Parameters,
1314
} from '@storybook/react'
1415
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
1516
import { httpBatchLink } from '@trpc/client'
@@ -33,6 +34,8 @@ import { env } from '~/env.mjs'
3334
import { LoginStateContext } from '~/features/auth'
3435
import { type AppRouter } from '~/server/modules/_app'
3536
import { theme } from '~/theme'
37+
import { viewport } from '~/stories/utils/viewports'
38+
import { withChromaticModes } from '~/stories/utils/chromatic'
3639

3740
// Initialize MSW
3841
initialize({
@@ -200,3 +203,24 @@ export const decorators: Decorator[] = [
200203
]
201204

202205
export const loaders: Loader[] = [mswLoader]
206+
207+
export const parameters: Parameters = {
208+
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
209+
layout: 'fullscreen',
210+
viewport,
211+
/**
212+
* If tablet view is needed, add it on a per-story basis.
213+
* @example
214+
* ```
215+
* export const SomeStory: Story = {
216+
* parameters: {
217+
* chromatic: withChromaticModes(["gsib", "desktop", "tablet"]),
218+
* }
219+
* }
220+
* ```
221+
*/
222+
chromatic: {
223+
...withChromaticModes(['desktop']),
224+
prefersReducedMotion: 'reduce',
225+
},
226+
}

src/stories/Page/HomePage.stories.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@ import type { Meta, StoryObj } from '@storybook/react'
22
import { meHandlers } from 'tests/msw/handlers/me'
33

44
import HomePage from '~/pages/home'
5+
import { withChromaticModes } from '../utils/chromatic'
56

67
const meta: Meta<typeof HomePage> = {
78
title: 'Pages/Home Page',
89
component: HomePage,
910
parameters: {
1011
mockdate: new Date('2023-06-28T07:23:18.349Z'),
11-
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
12-
layout: 'fullscreen',
1312
msw: {
1413
handlers: [meHandlers.me()],
1514
},
15+
chromatic: withChromaticModes(['mobile', 'desktop']),
1616
},
1717
}
1818

1919
export default meta
2020
type Story = StoryObj<typeof HomePage>
2121

22-
export const Default: Story = {}
22+
export const Default: Story = {
23+
name: 'Home Page',
24+
}
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
import type { Meta, StoryObj } from '@storybook/react'
22

33
import LandingPage from '~/pages/index'
4-
import { getMobileViewParameters } from '../utils/viewports'
4+
import { withChromaticModes } from '../utils/chromatic'
55

66
const meta: Meta<typeof LandingPage> = {
77
title: 'Pages/Landing Page',
88
component: LandingPage,
99
parameters: {
10-
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
11-
layout: 'fullscreen',
10+
chromatic: withChromaticModes(['mobile', 'tablet', 'desktop']),
1211
},
1312
}
1413

1514
export default meta
1615
type Story = StoryObj<typeof LandingPage>
1716

18-
export const Desktop: Story = {}
19-
20-
export const Mobile: Story = {
21-
parameters: getMobileViewParameters(),
17+
export const Default: Story = {
18+
name: 'Landing Page',
2219
}

src/stories/Page/SgidSelectProfilePage.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import type { Meta, StoryObj } from '@storybook/react'
22
import { authSgidHandlers } from 'tests/msw/handlers/auth/sgid'
33
import SelectProfilePage from '~/pages/sign-in/select-profile'
4+
import { withChromaticModes } from '../utils/chromatic'
45

56
const meta: Meta<typeof SelectProfilePage> = {
67
title: 'Pages/sgID Select Profile Page',
78
component: SelectProfilePage,
89
decorators: [],
910
parameters: {
10-
layout: 'fullscreen',
11+
chromatic: withChromaticModes(['mobile', 'desktop']),
1112
msw: {
1213
handlers: [authSgidHandlers.listStoredProfiles.returnSingleProfile()],
1314
},

src/stories/Page/SignInPage.stories.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@ import { authEmailHandlers } from 'tests/msw/handlers/auth/email'
44
import { meHandlers } from 'tests/msw/handlers/me'
55

66
import SignInPage from '~/pages/sign-in'
7-
import { getMobileViewParameters } from '../utils/viewports'
7+
import { withChromaticModes } from '../utils/chromatic'
88

99
const VALID_AUTH_EMAIL = '[email protected]'
1010

1111
const meta: Meta<typeof SignInPage> = {
1212
title: 'Pages/Sign In Page',
1313
component: SignInPage,
1414
parameters: {
15-
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
16-
layout: 'fullscreen',
1715
msw: {
1816
handlers: [
1917
meHandlers.unauthorized(),
@@ -29,27 +27,21 @@ const meta: Meta<typeof SignInPage> = {
2927
export default meta
3028
type Story = StoryObj<typeof SignInPage>
3129

32-
export const Default: Story = {}
30+
export const Default: Story = {
31+
parameters: {
32+
chromatic: withChromaticModes(['mobile', 'desktop']),
33+
},
34+
}
3335

3436
export const WithSgidLogin: Story = {
3537
parameters: {
38+
chromatic: withChromaticModes(['mobile', 'desktop']),
3639
features: {
3740
sgid: true,
3841
},
3942
},
4043
}
4144

42-
export const Mobile: Story = {
43-
parameters: getMobileViewParameters(),
44-
}
45-
46-
export const MobileWithSgid: Story = {
47-
parameters: {
48-
...WithSgidLogin.parameters,
49-
...Mobile.parameters,
50-
},
51-
}
52-
5345
export const InputValidation: Story = {
5446
play: async ({ canvasElement, step }) => {
5547
const canvas = within(canvasElement)

src/stories/utils/chromatic.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { modes } from './modes'
2+
3+
export const withChromaticModes = (args: (keyof typeof modes)[]) => {
4+
const modesArr = Array.from(new Set(args))
5+
return {
6+
modes: modesArr.reduce(
7+
(acc, mode) => {
8+
return {
9+
...acc,
10+
// Only want to preserve width, and not height for Chromatic snapshots.
11+
[mode]: modes[mode],
12+
}
13+
},
14+
{} as Partial<typeof modes>,
15+
),
16+
}
17+
}

src/stories/utils/modes.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* This file is for Chromatic viewport modes.
3+
* @see https://www.chromatic.com/docs/modes/
4+
* The names should correspond to the viewports exported in `viewports.ts`.
5+
*/
6+
7+
export type ChromaticModeKey = keyof typeof modes
8+
9+
export const modes = {
10+
mobile: {
11+
viewport: 'sm',
12+
},
13+
tablet: {
14+
viewport: 'md',
15+
},
16+
laptop: {
17+
viewport: 'lg',
18+
},
19+
desktop: {
20+
viewport: 'xl',
21+
},
22+
// You can also combine modes by passing in the appropriate parameters
23+
// "dark desktop": {
24+
// backgrounds: { value: "#1E293B" },
25+
// theme: "dark",
26+
// viewport: "lg",
27+
// },
28+
}
29+
30+
// Mainly for typing available viewports for use in storybook
31+
export const getViewportByMode = (viewport: ChromaticModeKey) => {
32+
return modes[viewport].viewport
33+
}

src/stories/utils/viewports.ts

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,53 @@
1-
import { theme } from '~/theme'
2-
31
/**
4-
* Helper function to convert theme breakpoint into viewport width in px for
5-
* Chromatic viewpoint snapshots.
6-
* @param breakpoint the theme breakpoint to convert
7-
* @returns the number pixel width of the given breakpoint.
2+
* This file holds the viewports for Storybook rendering, and also for
3+
* Choromatic to take snapshots based on the given mode.
4+
* @see https://www.chromatic.com/docs/modes/ and the `modes.ts` file.
5+
*
6+
* Width breakpoints are set as OGPDS defaults (as of writing this ) of
7+
*
8+
* sm: '30em',
9+
* md: '48em',
10+
* lg: '64em',
11+
* xl: '90em',
12+
*
13+
* @see https://tailwindcss.com/docs/screens
14+
*
15+
* If you override default tailwindcss breakpoints, you should update this file
16+
* so snapshot widths are consistent with the breakpoints in your app.
817
*/
9-
const breakpointToViewportWidth = (
10-
breakpoint: keyof typeof theme.breakpoints,
11-
) => {
12-
const rem = 16
13-
return parseInt(theme.breakpoints[breakpoint]) * rem
18+
19+
/** Type that conform to what Storybook expects for the `viewport` parameter */
20+
interface StorybookViewportParameter {
21+
viewports: Record<
22+
string,
23+
{
24+
name: string
25+
} & (
26+
| {
27+
styles: {
28+
width: string
29+
height?: string
30+
}
31+
}
32+
| number
33+
)
34+
>
1435
}
1536

1637
/**
17-
* Viewports mapping viewport key to their width in (pixel) number.
18-
* Used for Chromatic viewpoint snapshots which requires the numbers in pixels.
38+
* The names of the viewports should correspond to the `viewport` parameter you
39+
* set in `modes.ts`.
40+
*
41+
* An additional `xs` viewport is added specifically for Chromatic to have a
42+
* snapshot for the smallest screen size.
1943
*/
20-
export const viewports = {
21-
xs: 320, // '20rem'
22-
sm: breakpointToViewportWidth('sm'),
23-
md: breakpointToViewportWidth('md'),
24-
lg: breakpointToViewportWidth('lg'),
25-
xl: breakpointToViewportWidth('xl'),
26-
}
27-
28-
export const getMobileViewParameters = () => {
29-
return {
30-
viewport: {
31-
defaultViewport: 'mobile1',
32-
},
33-
chromatic: { viewports: [viewports.xs] },
34-
}
35-
}
36-
37-
export const getTabletViewParameters = () => {
38-
return {
39-
viewport: {
40-
defaultViewport: 'tablet',
41-
},
42-
chromatic: { viewports: [viewports.md] },
43-
}
44-
}
44+
export const viewport = {
45+
viewports: {
46+
xs: { name: 'xs', styles: { width: '320px', height: '100%' } },
47+
sm: { name: 'sm', styles: { width: '480px', height: '100%' } },
48+
md: { name: 'md', styles: { width: '768px', height: '100%' } },
49+
lg: { name: 'lg', styles: { width: '1280px', height: '100%' } },
50+
xl: { name: 'xl', styles: { width: '1440px', height: '100%' } },
51+
'2xl': { name: '2xl', styles: { width: '1536px', height: '100%' } },
52+
},
53+
} as const satisfies StorybookViewportParameter

0 commit comments

Comments
 (0)