Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codemod: Convert module format to MDX #7418

Merged
merged 8 commits into from Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Expand Up @@ -7,6 +7,7 @@ docs/public
storybook-static
built-storybooks
lib/cli/test
lib/codemod/src/transforms/__testfixtures__
scripts/storage
*.bundle.js
*.js.map
Expand Down
47 changes: 43 additions & 4 deletions lib/codemod/README.md
Expand Up @@ -151,12 +151,14 @@ Heuristics:
- The storiesOf "kind" name must be Button
- Button must be imported in the file

### convert-to-module-format
### convert-storiesof-to-module

This converts all of your "old-style" `storiesOf` stories into component module format, which uses standard ES6 modules.

> NOTE: The output of this transformation may require manual editing after running the transformation. `storiesOf` format allows multiple "kinds" (components) to be declared per file, but module format 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.

```sh
./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/convert-to-module-format.js . --ignore-pattern "node_modules|dist"
./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/convert-storiesof-to-module.js . --ignore-pattern "node_modules|dist"
```

For example:
Expand All @@ -183,18 +185,55 @@ export default {
export const story = () => <Button label="Story 1" />;

export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
story2.title = 'second story';
story2.story = { name: 'second story' };

export const story3 = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);
story3.title = 'complex story';
story3.story = { name: 'complex story' };
```

Heuristics:

- If a file has any default export, it will be skipped
- If a file has multiple `storiesOf` declarations, it will convert each one seperately. This generates invalid ES6, but you can edit the file by hand to split it into multiple files (or whatever is appropriate).

### convert-module-to-mdx

This converts all of your component module stories into MDX format, which integrates story examples and long-form documentation.

> 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.

```sh
./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/convert-to-module-format.js . --ignore-pattern "node_modules|dist"
```

```js
export default {
title: 'Button',
};

export const story = () => <Button label="Story 1" />;

export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
story2.story = { name: 'second story' };
```

Becomes:

```md
import { Meta, Story } from '@storybook/addon-docs/blocks';

# Button

<Meta title='Button'>

<Story name='story'><Button label="Story 1" /></Story>

<Story name='second story'>
<Button label="Story 2" onClick={action('click')} />
</Story>
```
1 change: 1 addition & 0 deletions lib/codemod/package.json
Expand Up @@ -28,6 +28,7 @@
"jscodeshift": "^0.6.3",
"lodash": "^4.17.11",
"prettier": "^1.16.4",
"recast": "^0.16.1",
"regenerator-runtime": "^0.12.1"
},
"publishConfig": {
Expand Down
@@ -0,0 +1,20 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';

export default {
title: 'Button',
};

export const story1 = () => <Button label="Story 1" />;

export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
story2.story = { name: 'second story' };

export const story3 = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);
story3.story = { name: 'complex story' };
@@ -0,0 +1,15 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';
import { Meta, Story } from '@storybook/addon-docs/blocks';

<Meta title='Button' />

<Story name='story1'><Button label='Story 1' /></Story>

<Story name='second story'><Button label='Story 2' onClick={action('click')} /></Story>

<Story name='complex story'><div>
<Button label='The Button' onClick={action('onClick')} />
<br />
</div></Story>
@@ -0,0 +1,10 @@
import React from 'react';
import Button from './Button';

export default {
title: 'Some.Button',
decorators: [withKnobs, storyFn => <div className="foo">{storyFn}</div>],
};

export const story1 = () => <Button label="The Button" />;
story1.story = { name: 'with decorator' };
@@ -0,0 +1,9 @@
import React from 'react';
import Button from './Button';
import { Meta, Story } from '@storybook/addon-docs/blocks';

<Meta
title='Some.Button'
decorators={[withKnobs, storyFn => <div className='foo'>{storyFn}</div>]} />

<Story name='with decorator'><Button label='The Button' /></Story>
@@ -0,0 +1,23 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';

export default {
title: 'Button',
excludeStories: /.*Data$/,
};

export const rowData = { col1: 'a', col2: 2 };

export const story1 = () => <Button label="Story 1" />;

export const story2 = () => <Button label="Story 2" onClick={action('click')} />;
story2.story = { name: 'second story' };

export const story3 = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);
story3.story = { name: 'complex story' };
@@ -0,0 +1,19 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';
import { Meta, Story } from '@storybook/addon-docs/blocks';

<Meta title='Button' />

export const rowData = {
col1: 'a',
col2: 2,
};
<Story name='story1'><Button label='Story 1' /></Story>

<Story name='second story'><Button label='Story 2' onClick={action('click')} /></Story>

<Story name='complex story'><div>
<Button label='The Button' onClick={action('onClick')} />
<br />
</div></Story>
@@ -0,0 +1,17 @@
import React from 'react';
import Button from './Button';

import { storiesOf } from '@storybook/react';

export default {
title: 'Button',

parameters: {
component: Button,
foo: 1,
bar: 2,
},
};

export const story1 = () => <Button label="The Button" />;
story1.story = { name: 'with kind parameters' };
@@ -0,0 +1,14 @@
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
import { Meta, Story } from '@storybook/addon-docs/blocks';

<Meta
title='Button'
parameters={{
component: Button,
foo: 1,
bar: 2,
}} />

<Story name='with kind parameters'><Button label='The Button' /></Story>
@@ -0,0 +1,24 @@
import React from 'react';
import Button from './Button';

import { storiesOf } from '@storybook/react';

export default {
title: 'Button',
};

export const story1 = () => <Button label="The Button" />;
story1.story = {
name: 'with story parameters',
parameters: {
header: false,
inline: true,
},
};

export const foo = () => <Button label="Foo" />;
foo.story = {
parameters: {
bar: 1,
},
};
@@ -0,0 +1,19 @@
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
import { Meta, Story } from '@storybook/addon-docs/blocks';

<Meta title='Button' />

<Story
name='with story parameters'
parameters={{
header: false,
inline: true,
}}><Button label='The Button' /></Story>

<Story
name='foo'
parameters={{
bar: 1,
}}><Button label='Foo' /></Story>
@@ -0,0 +1,7 @@
import { defineTest } from 'jscodeshift/dist/testUtils';

const testNames = ['basic', 'decorators', 'parameters', 'story-parameters', 'exclude-stories'];

testNames.forEach(testName => {
defineTest(__dirname, `convert-module-to-mdx`, null, `convert-module-to-mdx/${testName}`);
});
Expand Up @@ -13,5 +13,10 @@ const testNames = [
];

testNames.forEach(testName => {
defineTest(__dirname, `convert-to-module-format`, null, `convert-to-module-format/${testName}`);
defineTest(
__dirname,
`convert-storiesof-to-module`,
null,
`convert-storiesof-to-module/${testName}`
);
});