UNPKG

8.68 kBMarkdownView Raw
1# Storybook Codemods
2
3Storybook Codemods is a collection of codemod scripts written with JSCodeshift.
4It will help you migrate breaking changes & deprecations.
5
6## CLI Integration
7
8The preferred way to run these codemods is via the CLI's `migrate` command.
9
10To get a list of available codemods:
11
12```
13npx sb migrate --list
14```
15
16To run a codemod `<name-of-codemod>`:
17
18```
19npx sb migrate <name-of-codemod> --glob="**/*.stories.js"
20```
21
22## Installation
23
24If you want to run these codemods by hand:
25
26```sh
27yarn add jscodeshift @storybook/codemod --dev
28```
29
30- `@storybook/codemod` is our collection of codemod scripts.
31- `jscodeshift` is a tool we use to apply our codemods.
32
33After running the migration commands, you can remove them from your `package.json`, if you added them.
34
35## How to run a codemod script
36
37From the directory where you installed both `jscodeshift` and `@storybook/codemod` run:
38
39Example:
40
41```sh
42./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/update-organisation-name.js . --ignore-pattern "node_modules|dist"
43```
44
45Explanation:
46
47 <jscodeShiftCommand> -t <transformFileLocation> <pathToSource> --ignore-pattern "<globPatternToIgnore>"
48
49## Transforms
50
51### update-organisation-name
52
53Updates package names in imports to migrate to the new package names of storybook.
54
55```sh
56./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/update-organisation-name.js . --ignore-pattern "node_modules|dist"
57```
58
59There's a mapping of paths we replace but this example explains the gist of it:
60
61Example:
62
63```js
64import { storiesOf } from '@kadira/storybook';
65import { linkTo } from '@kadira/storybook-addon-links';
66```
67
68Becomes
69
70```js
71import { storiesOf } from '@storybook/react';
72import { linkTo } from '@storybook/addon-links';
73```
74
75### update-addon-info
76
77Replaces the Info addon's deprecated `addWithInfo` API with the standard `withInfo` API.
78
79```sh
80./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/update-addon-info.js . --ignore-pattern "node_modules|dist"
81```
82
83Example:
84
85```js
86storiesOf('Button').addWithInfo('simple usage', 'This is the basic usage of the button.', () => (
87 <Button label="The Button" />
88));
89```
90
91Becomes
92
93```js
94storiesOf('Button').add(
95 'simple usage',
96 withInfo('This is the basic usage of the button.')(() => <Button label="The Button" />)
97);
98```
99
100With options example:
101
102```js
103storiesOf('Button').addWithInfo(
104 'simple usage (disable source)',
105 'This is the basic usage of the button.',
106 () => <Button label="The Button" />,
107 { source: false, inline: true }
108);
109```
110
111Becomes
112
113```js
114storiesOf('Button').add(
115 'simple usage (disable source)',
116 withInfo({
117 text: 'This is the basic usage of the button.',
118 source: false,
119 inline: true,
120 })(() => <Button label="The Button" />)
121);
122```
123
124### add-component-parameters
125
126This tries to smartly adds "component" parameters to all your existing stories
127for use in SB Docs.
128
129```sh
130./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/add-component-parameters.js . --ignore-pattern "node_modules|dist"
131```
132
133For example:
134
135```js
136import { Button } from './Button';
137storiesOf('Button', module).add('story', () => <Button label="The Button" />);
138```
139
140Becomes:
141
142```js
143import { Button } from './Button';
144storiesOf('Button', module)
145 .addParameters({ component: Button })
146 .add('story', () => <Button label="The Button" />);
147```
148
149Heuristics:
150
151- The storiesOf "kind" name must be Button
152- Button must be imported in the file
153
154### storiesof-to-csf
155
156This converts all of your "old-style" `storiesOf` stories into Component Story Format (CSF), which uses standard ES6 modules.
157
158> NOTE: The output of this transformation may require manual editing after running the transformation. `storiesOf` API allows multiple "kinds" (components) to be declared per file, but CSF only allows a single component per file. Therefore, if you use this feature in your input stories, you will need to split up the resulting outputs by hand. You'll see a warning at the console if you need to hand edit.
159
160```sh
161./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/storiesof-to-csf.js . --ignore-pattern "node_modules|dist"
162```
163
164For example:
165
166```js
167storiesOf('Button', module)
168 .add('story', () => <Button label="Story 1" />)
169 .add('second story', () => <Button label="Story 2" onClick={action('click')} />)
170 .add('complex story', () => (
171 <div>
172 <Button label="The Button" onClick={action('onClick')} />
173 <br />
174 </div>
175 ));
176```
177
178Becomes:
179
180```js
181export default {
182 title: 'Button',
183};
184
185export const story = () => <Button label="Story 1" />;
186
187export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
188story2.story = { name: 'second story' };
189
190export const story3 = () => (
191 <div>
192 <Button label="The Button" onClick={action('onClick')} />
193 <br />
194 </div>
195);
196story3.story = { name: 'complex story' };
197```
198
199Heuristics:
200
201- If a file has any default export, it will be skipped
202- If a file has multiple `storiesOf` declarations, it will convert each one separately. This generates invalid ES6, but you can edit the file by hand to split it into multiple files (or whatever is appropriate).
203
204### csf-to-mdx
205
206This converts all of your CSF Component Stories into MDX syntax, which integrates story examples and long-form documentation.
207
208> NOTE: The output of this transformation may require manual editing after running the transformation. MDX is finnicky about the top-level statements it allows. For example, [variables should be defined with exports](https://mdxjs.com/getting-started/#defining-variables-with-exports), meaning `const foo = 5;` should be rewritten as `export const foo = 5;`. We don't do this transformation automatically, since you may prefer to refactor your stories.
209
210```sh
211./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/csf-to-mdx.js . --ignore-pattern "node_modules|dist"
212```
213
214For example:
215
216```js
217export default {
218 title: 'Button',
219};
220
221export const story = () => <Button label="Story 1" />;
222
223export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
224story2.story = { name: 'second story' };
225```
226
227Becomes:
228
229```md
230import { Meta, Story } from '@storybook/addon-docs';
231
232# Button
233
234<Meta title='Button'>
235
236<Story name='story'><Button label="Story 1" /></Story>
237
238<Story name='second story'>
239 <Button label="Story 2" onClick={action('click')} />
240</Story>
241```
242
243### mdx-to-csf
244
245This converts all your MDX stories into Component Story Format.
246
247```sh
248./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/mdx-to-csf.js . --ignore-pattern "node_modules|dist" --extensions=mdx
249```
250
251For example:
252
253```js
254import React from 'react';
255import Button from './Button';
256import { Meta, Story } from '@storybook/addon-docs';
257
258<Meta title='Button' />
259
260<Story name='basic stories'><Button label='The Button' /></Story>
261```
262
263Becomes:
264
265```js
266import React from 'react';
267import Button from './Button';
268
269export default {
270 title: 'Button',
271};
272
273export const basicStory = () => <Button label="The Button" />;
274basicStory.story = {
275 name: 'basic stories',
276};
277```
278
279### upgrade-hierarchy-separators
280
281Starting in 5.3, Storybook is moving to using a single path separator, `/`, to specify the story hierarchy. It previously defaulted to `|` for story "roots" (optional) and either `/` or `.` for denoting paths. This codemod updates the old default to the new default.
282
283```sh
284./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/upgrade-hierarchy-separators.js . --ignore-pattern "node_modules|dist"
285```
286
287For example:
288
289```js
290storiesOf('Foo|Bar/baz');
291storiesOf('Foo.Bar.baz');
292
293export default {
294 title: 'Foo|Bar/baz.whatever',
295};
296```
297
298Becomes:
299
300```js
301storiesOf('Foo/Bar/baz');
302storiesOf('Foo/Bar/baz');
303
304export default {
305 title: 'Foo/Bar/baz/whatever',
306};
307```
308
309### csf-hoist-story-annotations
310
311Starting in 6.0, Storybook has deprecated the `.story` annotation in CSF and is using hoisted annotations.
312
313```sh
314./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/csf-hoist-story-annotations.js . --ignore-pattern "node_modules|dist" --extensions=js
315```
316
317For example:
318
319```js
320export const Basic = () => <Button />
321Basic.story = {
322 name: 'foo',
323 parameters: { ... },
324 decorators: [ ... ],
325};
326```
327
328Becomes:
329
330```js
331export const Basic = () => <Button />
332Basic.storyName = 'foo';
333Basic.parameters = { ... };
334Basic.decorators = [ ... ];
335```
336
337The new syntax is slightly more compact, is more ergonomic, and resembles React's `displayName`/`propTypes`/`defaultProps` annotations.