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 mdx to module format #7419

Merged
merged 11 commits into from Jul 18, 2019
20 changes: 8 additions & 12 deletions addons/docs/mdx-compiler-plugin.test.js
Expand Up @@ -4,7 +4,14 @@ const mdx = require('@mdx-js/mdx');
const prettier = require('prettier');
const plugin = require('./mdx-compiler-plugin');

function format(code) {
async function generate(filePath) {
const content = await fs.readFile(filePath, 'utf8');

const code = mdx.sync(content, {
filepath: filePath,
compilers: [plugin({})],
});

return prettier.format(code, {
parser: 'babel',
printWidth: 100,
Expand All @@ -15,17 +22,6 @@ function format(code) {
});
}

async function generate(filePath) {
const content = await fs.readFile(filePath, 'utf8');

const result = mdx.sync(content, {
filepath: filePath,
compilers: [plugin({})],
});

return format(result);
}

describe('docs-mdx-compiler-plugin', () => {
it('supports vanilla mdx', async () => {
const code = await generate(path.resolve(__dirname, './fixtures/vanilla.mdx'));
Expand Down
38 changes: 38 additions & 0 deletions lib/codemod/README.md
Expand Up @@ -211,6 +211,8 @@ This converts all of your component module stories into MDX format, which integr
./node_modules/.bin/jscodeshift -t ./node_modules/@storybook/codemod/dist/transforms/convert-to-module-format.js . --ignore-pattern "node_modules|dist"
```

For example:

```js
export default {
title: 'Button',
Expand All @@ -237,3 +239,39 @@ import { Meta, Story } from '@storybook/addon-docs/blocks';
<Button label="Story 2" onClick={action('click')} />
</Story>
```

### convert-mdx-to-module

This converts all your MDX stories into module format.

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

For example:

```js
import React from 'react';
import Button from './Button';
import { Meta, Story } from '@storybook/addon-docs/blocks';

<Meta title='Button' />

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any markdown here just gets completely dropped?

Copy link
Member Author

@shilman shilman Jul 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, yes. PR's welcome 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. do you think this is useful if we do that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely. We're going from a format that supports mixtures of longform documentation and code to one that does not. This completely solves the tedious task of translation in an automated fashion, and for now users will have to figure out themselves how they want to encode that documentation.

If we ever have a recommended way to encode longform docs in module format, we can try to encode that in the codemod.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you could put markdown in the module format somehow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a prescription for that. Some users will put it in docgen comments, others in strings, others in .md files that get loaded in. When somebody has a good solution they want to push, they can upgrade this codemod accordingly.

I personally think people should be using MDX for longform docs and not module format, and the purpose of this PR is to help people who try it and want to go back for whatever reason.

<Story name='basic stories'><Button label='The Button' /></Story>
```

Becomes:

```
import React from 'react';
import Button from './Button';

export default {
title: 'Button',
};

export const basicStory = () => <Button label="The Button" />;
basicStory.story = {
name: 'basic stories',
};
```
1 change: 1 addition & 0 deletions lib/codemod/package.json
Expand Up @@ -21,6 +21,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@mdx-js/mdx": "^1.0.0",
"@storybook/node-logger": "5.2.0-beta.1",
"core-js": "^3.0.1",
"cross-spawn": "^6.0.5",
Expand Down
@@ -0,0 +1,14 @@
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,25 @@
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 secondStory = () => <Button label="Story 2" onClick={action('click')} />;

secondStory.story = {
name: 'second story',
};

export const complexStory = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);

complexStory.story = {
name: 'complex story',
};
@@ -0,0 +1,8 @@
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,13 @@
import React from 'react';
import Button from './Button';

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

export const withDecorator = () => <Button label="The Button" />;

withDecorator.story = {
name: 'with decorator',
};
@@ -0,0 +1,19 @@
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,30 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';
export const rowData = {
col1: 'a',
col2: 2,
};

export default {
title: 'Button',
includeStories: ['story1', 'secondStory', 'complexStory'],
};

export const story1 = () => <Button label="Story 1" />;
export const secondStory = () => <Button label="Story 2" onClick={action('click')} />;

secondStory.story = {
name: 'second story',
};

export const complexStory = () => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
</div>
);

complexStory.story = {
name: 'complex story',
};
@@ -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,19 @@
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 withKindParameters = () => <Button label="The Button" />;

withKindParameters.story = {
name: 'with kind parameters',
};
@@ -0,0 +1,3 @@
import { Story, Meta } from '@storybook/addon-docs/blocks';

<Story name="plaintext">Plain text</Story>;
@@ -0,0 +1,3 @@
import React from 'react';
export default {};
export const plaintext = () => 'Plain text';
@@ -0,0 +1,18 @@
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,26 @@
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';

export default {
title: 'Button',
};

export const withStoryParameters = () => <Button label="The Button" />;

withStoryParameters.story = {
name: 'with story parameters',

parameters: {
header: false,
inline: true,
},
};

export const foo = () => <Button label="Foo" />;

foo.story = {
parameters: {
bar: 1,
},
};
@@ -0,0 +1,21 @@
import { Story, Meta } from '@storybook/addon-docs/blocks';
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';

<Meta
title="Addons|Docs/mdx"
decorators={[storyFn => <div style={{ backgroundColor: 'yellow' }}>{storyFn()}</div>]}
parameters={{ component: Button, notes: 'component notes' }}
/>

## Getting into details

<Story name="solo story">
<Button onClick={action('clicked')}>solo</Button>
</Story>

<Source name="hello story" />

## Configurable height

<Story id="basics-button--all-buttons" height="400px" />
@@ -0,0 +1,30 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';

export default {
title: 'Addons|Docs/mdx',

decorators: [
storyFn => (
<div
style={{
backgroundColor: 'yellow',
}}
>
{storyFn()}
</div>
),
],

parameters: {
component: Button,
notes: 'component notes',
},
};

export const soloStory = () => <Button onClick={action('clicked')}>solo</Button>;

soloStory.story = {
name: 'solo story',
};
@@ -1,4 +1,3 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';
import { Meta, Story } from '@storybook/addon-docs/blocks';
Expand Down
@@ -1,4 +1,3 @@
import React from 'react';
import Button from './Button';
import { Meta, Story } from '@storybook/addon-docs/blocks';

Expand Down
@@ -1,4 +1,3 @@
import React from 'react';
import Button from './Button';
import { action } from '@storybook/addon-actions';
import { Meta, Story } from '@storybook/addon-docs/blocks';
Expand Down
@@ -1,4 +1,3 @@
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
import { Meta, Story } from '@storybook/addon-docs/blocks';
Expand Down
@@ -1,4 +1,3 @@
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
import { Meta, Story } from '@storybook/addon-docs/blocks';
Expand Down
15 changes: 15 additions & 0 deletions lib/codemod/src/transforms/__tests__/convert-mdx-to-module.test.js
@@ -0,0 +1,15 @@
import { defineTest } from 'jscodeshift/dist/testUtils';

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

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