Skip to content

Commit 8b0d402

Browse files
authored
Add WritingStories docs to Storybook (#14844)
To explain the basics of writing stories, describe the conventions we use, and detail the custom tooling we've built around Storybook.
1 parent cc6194d commit 8b0d402

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import { Meta, Source } from '@storybook/addon-docs/blocks';
2+
3+
<Meta title="Writing Stories" />
4+
5+
# Writing Stories
6+
7+
If you're not familiar with the concept of stories, see the [Storybook docs](https://storybook.js.org/docs/9) and [Chromatic docs](https://www.chromatic.com/docs/) for an introduction.
8+
9+
We recommend using Storybook's [Component Story Format](https://storybook.js.org/docs/9/api/csf/index) version 3 (CSFv3) to write stories. The [documentation](https://storybook.js.org/docs/9/writing-stories) covers this in detail, but in brief it means that your stories file should look something like this:
10+
11+
<Source language="tsx" code={`
12+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
13+
import { Headline } from './Headline';
14+
15+
const meta = {
16+
title: 'Components/Headline',
17+
component: Headline,
18+
} satisfies Meta<typeof Headline>;
19+
20+
export default meta;
21+
22+
type Story = StoryObj<typeof meta>;
23+
24+
export const MyStory = {
25+
args: {
26+
text: 'A headline',
27+
},
28+
} satisfies Story;
29+
`} />
30+
31+
The `title` field is optional, as it can be inferred from the name of the component and its file path. You may also, sometimes, want to choose a different prefix than `Components/`. See [the docs](https://storybook.js.org/docs/9/writing-stories/naming-components-and-hierarchy) for more information. In addition, if you only have a single story you may want to ["hoist"](https://storybook.js.org/docs/9/writing-stories/naming-components-and-hierarchy#single-story-hoisting) it in the sidebar.
32+
33+
## Toolbar and Modes
34+
35+
Viewing a story in a particular state unrelated to its props or context, such as at a specific breakpoint or in dark mode, is achieved through the [toolbar and globals](https://storybook.js.org/docs/9/essentials/toolbars-and-globals).
36+
37+
### Breakpoints
38+
39+
We have configured Storybook to display stories in one of the eight Guardian breakpoints:
40+
41+
- mobile: 320px
42+
- mobileMedium: 375px
43+
- mobileLandscape: 480px
44+
- phablet: 660px
45+
- tablet: 740px
46+
- desktop: 980px
47+
- leftCol: 1140px
48+
- wide: 1300px
49+
50+
These can be applied using the dropdown in the Storybook toolbar, which is powered by the built-in [viewport feature](https://storybook.js.org/docs/9/essentials/viewport).
51+
52+
### Colour Scheme
53+
54+
Most of our components also have both light and dark mode versions, and we want to be able to view either of these in Storybook. To this end we have a custom toolbar item that allows you to display a story in one of four colour schemes:
55+
56+
- Light
57+
- Dark
58+
- Horizontal Split
59+
- Vertical Split
60+
61+
The "split" schemes will display two versions of the story at the same time; either vertically, light above dark, or horizontally, light on the left and dark on the right. The [docs](https://storybook.js.org/docs/9/essentials/toolbars-and-globals) contain details about how this works internally.
62+
63+
These colour schemes will include default background and text colours. If you want to override these defaults this can be done with the `colourSchemeBackground` and `colourSchemeTextColour` parameters:
64+
65+
<Source language="tsx" code={`
66+
export const MyStory = {
67+
parameters: {
68+
colourSchemeBackground: {
69+
light: 'blue',
70+
dark: '#222',
71+
},
72+
colourSchemeTextColour: {
73+
light: palette('--custom-text-colour'),
74+
dark: palette('--custom-text-colour'),
75+
},
76+
},
77+
} satisfies Story;
78+
`} />
79+
80+
As seen in this example, any CSS colour value should work, including variables from the DCAR palette (see `palette.ts`).
81+
82+
### Chromatic and Modes
83+
84+
To apply a particular combination of breakpoint and colour scheme to a story in Chromatic, we use [modes](https://www.chromatic.com/docs/modes/). Some are already defined in `.storybook/modes.ts`, and if the one you want is missing you can add it there. We apply these modes at either the component level via a parameter in the `meta` object, or at the story level via a parameter in the story object. For example:
85+
86+
<Source language="tsx" code={`
87+
import { allModes } from '../../../.storybook/modes';
88+
89+
const meta = {
90+
title: 'Components/Headline',
91+
component: Headline,
92+
parameters: {
93+
chromatic: {
94+
modes: {
95+
'light mobileMedium': allModes['light mobileMedium'],
96+
'vertical leftCol': allModes['vertical leftCol'],
97+
},
98+
}
99+
}
100+
} satisfies Meta<typeof Headline>;
101+
102+
export default meta;
103+
`} />
104+
105+
Note that the previous way of achieving some of this functionality was through a [viewports parameter](https://www.chromatic.com/docs/legacy-viewports/#viewports-legacy-storybook-api). That is now deprecated.
106+
107+
## Format and Colour Palette
108+
109+
Many components will use the DCAR palette (see `palette.ts`) to derive their colours, and the colours in this palette are determined by a "Format" value. If a component takes a `format` prop, and the story therefore provides a `format` arg, then the palette will use this:
110+
111+
<Source language="tsx" code={`
112+
export const MyStory = {
113+
args: {
114+
text: 'A headline',
115+
format: {
116+
design: ArticleDesign.Standard,
117+
display: ArticleDisplay.Standard,
118+
theme: Pillar.News,
119+
},
120+
},
121+
} satisfies Story;
122+
`} />
123+
124+
If the component does not take this prop, you can instead pass a `formats` parameter:
125+
126+
<Source language="tsx" code={`
127+
export const MyStory = {
128+
args: {
129+
text: 'A headline',
130+
},
131+
parameters: {
132+
formats: [
133+
{
134+
design: ArticleDesign.Standard,
135+
display: ArticleDisplay.Standard,
136+
theme: Pillar.News,
137+
},
138+
],
139+
},
140+
} satisfies Story;
141+
`} />
142+
143+
This parameter takes an array because you can pass multiple Formats to render the story multiple times, with a different format, and therefore colour palette, each time. Note that if the component *also* takes a `format` prop then this prop will be overridden by the `formats` parameter. This has consequences beyond the colour palette, as components can derive many aspects of the way they appear from Format.
144+
145+
If a Format is not passed in either the `args` or `parameters` of a story then we use a default to determine the colour palette instead (defined in `globalColourScheme.ts`).
146+
147+
## Story Layout
148+
149+
Components often represent small pieces of UI that are only intended to appear in a certain area of the page, such as in one of the Guardian layout columns (left, centre, right). Due to the way our pages are designed, the styles that determine their layout often live at a higher level than the component, and so rendering the component in a story without that environment may not be very useful. Storybook provides the [decorators](https://storybook.js.org/docs/9/writing-stories/decorators) feature to solve this problem, and we have some custom decorators to place stories within the [Guardian grid](?path=/docs/grid--docs):
150+
151+
<dl>
152+
<dt>centreColumnDecorator</dt>
153+
<dd>
154+
Place the story in the centre column of a Guardian grid across all breakpoints.
155+
</dd>
156+
<dt>leftColumnDecorator</dt>
157+
<dd>
158+
Place the story in the left column of a Guardian grid, when available. On narrower breakpoints, where the left column isn't available, it will instead be placed in the centre column (this is a common layout pattern).
159+
</dd>
160+
<dt>rightColumnDecorator</dt>
161+
<dd>
162+
Place the story in the right column of a Guardian grid, when available. On narrower breakpoints, where the right column isn't available, it will instead be placed in the centre column (this is a common layout pattern).
163+
</dd>
164+
<dt>gridContainerDecorator</dt>
165+
<dd>
166+
Wrap the story in a Guardian grid container. This allows styles either:
167+
<ol style={{ listStyle: 'decimal' }}>
168+
<li>Within the component</li>
169+
<li>In the story</li>
170+
<li>In other decorators</li>
171+
</ol>
172+
to position elements on the grid.
173+
</dd>
174+
</dl>
175+
176+
These decorators can be applied at either the component level via the `meta` object, or at the story level via the story object. For example:
177+
178+
<Source language="tsx" code={`
179+
import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators';
180+
181+
export const MyStory = {
182+
args: {
183+
text: 'A headline',
184+
},
185+
decorators: [centreColumnDecorator],
186+
} satisfies Story;
187+
`} />
188+
189+
## Other Decorators
190+
191+
Aside from the grid decorators mentioned above, we also have the `splitThemeDecorator`, the `themeDecorator`, and the `configContextDecorator`. These are mostly intended for use in Storybook setup, in `preview.ts`, and are _not_ meant to be used directly in stories files.

0 commit comments

Comments
 (0)