UNPKG

9.85 kBMarkdownView Raw
1# Storybook Addon Contexts
2
3**Storybook Addon Contexts** is an addon for driving your components under dynamic contexts in
4[Storybook](https://storybook.js.org/).
5
6## 💡 Why you need this?
7
8Real world users expects your application being customizable, that is why often your components are **polymorphic**:
9they need to adapt themselves under different contextual environments. Imagine your components can speak
10Chinese, English, or even French, and they change their skin tone under dark or light theme. Yeah, you want to make
11sure a component looks great in all scenarios.
12
13A good practice to write maintainable components is separate the presentation and its business logic. Storybook is
14a great place for exercising the visualization and interaction of your components, which may depend on some contexts.
15Often enough, you will find it become very tedious to wrap each component deeply with its contextual environments
16before you can really write the main story. You even start to write extra components or factory functions just to
17make your life easier. How about changing the context of your story dynamically?! There was simply no good way so
18you ended up writing stories like an accountant.
19
20That is why you need this. An elegant way to wrap your component stories and change their contextual environment
21directly and dynamically in Storybook UI! Kind of like a dependency injection, eh! The best bit is **you define it
22once then apply it everywhere**.
23
24## ✅ Features
25
261. Define a single global file for managing contextual environments (a.k.a. containers) for all of your stories
27 declaratively. No more repetitive setups or noisy wrapping, making your stories more focused and readable.
282. Support dynamic contextual props switching from Storybook toolbar at runtime. You can slice into
29 different environments (e.g. languages or themes ) to understand how your component is going to respond.
303. Library agnostic: no presumption on what kind of components you want to wrap around your stories. You can even
31 use it to bridge with your favorite routing, state-management solutions, or even your own
32 [React Context](https://reactjs.org/docs/context.html) provider.
334. Offer chainable and granular configurations. It is even possible to fine-tune at per story level.
345. Visual regression friendly. You can use this addon to drive the same story under different contexts to smoke
35 test important visual states.
36
37## 🧰 Requirements
38
39Make sure the version of your Storybook is above v5. For the full list of the current supported frameworks, see
40[Addon / Framework Support Table](../../ADDONS_SUPPORT.md).
41
42## 🎬 Getting started
43
44To get it started, add this package into your project:
45
46```bash
47yarn add -D @storybook/addon-contexts
48```
49
50within `.storybook/main.js`:
51
52```js
53module.exports = {
54 addons: ['@storybook/addon-contexts/register']
55}
56```
57
58To load your contextual setups for your stories globally, add the following lines into `preview.js` file (you should
59see it near your `addon.js` file):
60
61```js
62import { addDecorator } from '@storybook/[framework]';
63import { withContexts } from '@storybook/addon-contexts/[framework]';
64import { contexts } from './configs/contexts'; // we will define the contextual setups later in API section
65
66addDecorator(withContexts(contexts));
67```
68
69Alternatively, like other addons, you can use this addon only for a given set of stories:
70
71```js
72import { withContexts } from '@storybook/addon-contexts/[framework]';
73import { contexts } from './configs/contexts';
74
75export default {
76 title: 'Component With Contexts',
77 decorators: [withContexts(contexts)],
78};
79```
80
81Finally, you may want to create new contextual environments or disable default setups at the story level. To create a new contextual environment at the story level:
82
83```js
84export const defaultView = () => <div />; // sample story in CSF format
85defaultView.story = {
86 parameters: {
87 contexts: [{ /* contextual environment defined using the API below */ }]
88 }
89};
90```
91
92To disable a default setup at the story level:
93
94```js
95export const defaultView = () => <div />;
96defaultView.story = {
97 parameters: {
98 contexts: [
99 {
100 title: '[title of contextual environment defined in contexts.js]'
101 options: { disable: true }
102 }
103 ]
104 }
105};
106```
107
108To override the default option for a default setup at the story level, see [this suggestion](https://discordapp.com/channels/486522875931656193/501692020226654208/687359410577604732).
109
110
111## ⚙️ Setups
112
113### Overview
114
115It is recommended to have a separate file for managing your contextual environment setups. Let's add a file named
116`contexts.js` first. Before diving into API details, here is an overview on the landscape. For example (in React),
117to inject component theming contexts to both `styled-components` and `material-ui` theme providers in stories:
118
119```js
120export const contexts = [
121 {
122 icon: 'box', // a icon displayed in the Storybook toolbar to control contextual props
123 title: 'Themes', // an unique name of a contextual environment
124 components: [
125 // an array of components that is going to be injected to wrap stories
126 /* Styled-components ThemeProvider, */
127 /* Material-ui ThemeProvider, */
128 ],
129 params: [
130 // an array of params contains a set of predefined `props` for `components`
131 { name: 'Light Theme', props: { theme /* : your light theme */ } },
132 { name: 'Dark Theme', props: { theme /* : your dark theme */ }, default: true },
133 ],
134 options: {
135 deep: true, // pass the `props` deeply into all wrapping components
136 disable: false, // disable this contextual environment completely
137 cancelable: false, // allow this contextual environment to be opt-out optionally in toolbar
138 },
139 },
140 /* ... */ // multiple contexts setups are supported
141];
142```
143
144---
145
146### APIs
147
148#### `withContexts(contexts) : function`
149
150A decorating function for wrapping your stories under your predefined `contexts`. This means multiple contextual
151environments are supported. They are going to be loaded layer by layer and wrapped in a descending oder (top -> down
152-> story). The `contexts` is an array of objects that should have the following properties:
153
154---
155
156#### `icon : string?`
157
158(default `undefined`)
159
160An icon displayed in the Storybook toolbar to control contextual props. This addon allows you to define an icon for
161each contextual environment individually. Take a look at the currently supported
162[icon lists](https://storybooks-official.netlify.com/?path=/story/basics-icon--labels) from the official Storybook
163story. You must define an icon first if you want to take advantage of switching props dynamically in your Storybook
164toolbar.
165
166---
167
168#### `title : string`
169
170(required)
171
172A unique name of a contextual environment; if duplicate names are provided, the latter is going to be ignored.
173
174---
175
176#### `components : (Component|string)[]`
177
178(required)
179
180An array of components that is going to be injected to wrap stories. This means this addon allows multiple wrapping
181components to coexist. The wrapping sequence is from the left to right (parent -> children -> story). This nested
182wrapping behaviour can be useful in some cases; for instance, in the above example, we are wrapping stories under
183`styled-components` and `material-ui` theme providers. Also, you can use this addon to wrap any valid HTML tags.
184
185---
186
187#### `params : object[] | undefined`
188
189(default: `undefined`)
190
191An array of params contains a set of predefined `props` for `components`. This object has the following properties:
192
193#### `params.name : string`
194
195(required)
196
197A unique name for representing the props.
198
199#### `params.props : object | null:`
200
201(required)
202
203The `props` that are accepted by the wrapping component(s).
204
205#### `params.default : true?`
206
207(default: `undefined`)
208
209Set to `true` if you want to use this param initially. Only the first one marked as default is identified.
210
211---
212
213#### `options`
214
215A set of options offers more granular control over the defined contextual environment. These properties can be
216overridden at the story level:
217
218#### `options.deep : boolean?`
219
220(default: `false`)
221
222Pass the `props` deeply into all wrapping components. Useful when you want them all to be passed with the same props.
223
224#### `options.disable : boolean?`
225
226(default: `false`)
227
228Disable this contextual environment completely. Useful when you want to opt-out this context from a given story.
229
230#### `options.cancelable : boolean?`
231
232(default: `false`)
233
234Allow this contextual environment to be opt-out optionally in toolbar. When set to `true`, an **Off** option will
235be shown at first in the toolbar menu in your Storybook.
236
237## 📔 Notes
238
2391. You can use this addon to inject any valid components, that is why `icon` and `params` can be optional.
2402. As mentioned, extra contextual environment setups can be added at the story level. Please make sure they are
241 passed via the second argument as `{ contexts: [{ /* extra contexts */ }}`.
2423. Additional `params` can be "appended" into an existing setup at the story level too (make sure it goes with the
243 correct `title`); however, they are never be able to overridden the default setups. So it is important to have
244 non-colliding names.
2454. The addon will persist the selected params (the addon state) between stories at run-time (similar to other
246 addons). If the active params were gone after story switching, it falls back to the default then the first. As a
247 rule of thumb, whenever collisions are possible, the first always wins.
2485. Query parameters are supported for pre-selecting contexts param, which comes in handy for visual regression testing.
249 You can do this by appending `&contexts=[name of contexts]=[name of param]` in the URL under iframe mode. Use `,`
250 to separate multiple contexts (e.g. `&contexts=Theme=Forests,Language=Fr`).
251
252## 📖 License
253
254MIT