diff --git a/addons/docs/docs/recipes.md b/addons/docs/docs/recipes.md index e1cfc9cd82a0..3158623ce7a1 100644 --- a/addons/docs/docs/recipes.md +++ b/addons/docs/docs/recipes.md @@ -32,46 +32,50 @@ The only limitation is that your exported titles (CSF: `default.title`, MDX `Met Perhaps you want to write your stories in CSF, but document them in MDX? Here's how to do that: -**Button.mdx** - -```md -import { Story } from '@storybook/addon-docs/blocks'; -import { SomeComponent } from 'somewhere'; - -# Button - -I can embed a story (but not define one, since this file should not contain a `Meta`): - - - -And of course I can also embed arbitrary markdown & JSX in this file. - - -``` - **Button.stories.js** ```js +import React from 'react'; import { Button } from './Button'; -import mdx from './Button.mdx'; export default { title: 'Demo/Button', - parameters: { - docs: { - page: mdx, - }, - }, + component: Button, + includeStories: [], // or simply don't load this file at all }; export const basic = () => ; +basic.story = { + parameters: { foo: 'bar' }, +}; +``` + +**Button.stories.mdx** + +```md +import { Meta, Story } from '@storybook/addon-docs/blocks'; +import * as stories from './Button.stories.js'; +import { SomeComponent } from 'path/to/SomeComponent'; + + + +# Button + +I can define a story with the function imported from CSF: + +{stories.basic} + +And of course I can also embed arbitrary markdown & JSX in this file. + + ``` -Note that in contrast to other examples, the MDX file suffix is `.mdx` rather than `.stories.mdx`. This key difference means that the file will be loaded with the default MDX loader rather than Storybook's CSF loader, which has several implications: +What's happening here: -1. You don't need to provide a `Meta` declaration. -2. You can refer to existing stories (i.e. ``) but cannot define new stories (i.e. ``). -3. The documentation gets exported as the default export (MDX default) rather than as a parameter hanging off the default export (CSF). +- Your stories are defined in CSF, but because of `includeStories: []`, they are not actually added to Storybook. +- The MDX file is adding the stories to Storybook, and using the story function defined in CSF. +- The MDX loader is using story metadata from CSF, such as name, decorators, parameters, but will give giving preference to anything defined in the MDX file. +- The MDX file is using the Meta `default` defined in the CSF. ## Mixing storiesOf with CSF/MDX diff --git a/addons/docs/src/blocks/index.ts b/addons/docs/src/blocks/index.ts index 96a505258171..4e68cf50276c 100644 --- a/addons/docs/src/blocks/index.ts +++ b/addons/docs/src/blocks/index.ts @@ -11,3 +11,6 @@ export * from './Props'; export * from './Source'; export * from './Story'; export * from './Wrapper'; + +// helper function for MDX +export const makeStoryFn = (val: any) => (typeof val === 'function' ? val : () => val); diff --git a/addons/docs/src/mdx/__snapshots__/mdx-compiler-plugin.test.js.snap b/addons/docs/src/mdx/__snapshots__/mdx-compiler-plugin.test.js.snap index 519c6c08745b..2456a29c4ee3 100644 --- a/addons/docs/src/mdx/__snapshots__/mdx-compiler-plugin.test.js.snap +++ b/addons/docs/src/mdx/__snapshots__/mdx-compiler-plugin.test.js.snap @@ -2,7 +2,7 @@ exports[`docs-mdx-compiler-plugin decorators.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Button } from '@storybook/react/demo'; import { Story, Meta } from '@storybook/addon-docs/blocks'; @@ -89,7 +89,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin docs-only.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Meta } from '@storybook/addon-docs/blocks'; @@ -145,7 +145,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin non-story-exports.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Button } from '@storybook/react/demo'; import { Story, Meta } from '@storybook/addon-docs/blocks'; @@ -210,7 +210,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin parameters.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Button } from '@storybook/react/demo'; import { Story, Meta } from '@storybook/addon-docs/blocks'; @@ -298,7 +298,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin previews.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Button } from '@storybook/react/demo'; import { Preview, Story, Meta } from '@storybook/addon-docs/blocks'; @@ -378,7 +378,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin story-current.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Story } from '@storybook/addon-docs/blocks'; @@ -423,7 +423,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin story-def-text-only.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Story, Meta } from '@storybook/addon-docs/blocks'; @@ -453,7 +453,7 @@ function MDXContent({ components, ...props }) { MDXContent.isMDXComponent = true; -export const text = () => 'Plain text'; +export const text = makeStoryFn('Plain text'); text.story = {}; text.story.name = 'text'; text.story.parameters = { mdxSource: \\"'Plain text'\\" }; @@ -476,7 +476,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin story-definitions.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; import { Button } from '@storybook/react/demo'; import { Story, Meta } from '@storybook/addon-docs/blocks'; @@ -550,7 +550,7 @@ export default componentMeta; exports[`docs-mdx-compiler-plugin story-function.mdx 1`] = ` "/* @jsx mdx */ -import { DocsContainer } from '@storybook/addon-docs/blocks'; +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; const makeShortcode = name => function MDXDefaultShortcode(props) { @@ -581,12 +581,12 @@ function MDXContent({ components, ...props }) { MDXContent.isMDXComponent = true; -export const functionStory = () => { +export const functionStory = makeStoryFn(() => { const btn = document.createElement('button'); btn.innerHTML = 'Hello Button'; btn.addEventListener('click', action('Click')); return btn; -}; +}); functionStory.story = {}; functionStory.story.name = 'function'; functionStory.story.parameters = { @@ -610,9 +610,66 @@ export default componentMeta; " `; +exports[`docs-mdx-compiler-plugin story-function-var.mdx 1`] = ` +"/* @jsx mdx */ +import { DocsContainer, makeStoryFn } from '@storybook/addon-docs/blocks'; + +import { Meta, Story } from '@storybook/addon-docs/blocks'; +export const basicFn = () => ; diff --git a/examples/official-storybook/stories/addon-docs/csf-with-mdx-docs.stories.mdx b/examples/official-storybook/stories/addon-docs/csf-with-mdx-docs.stories.mdx new file mode 100644 index 000000000000..485a6dbd6b49 --- /dev/null +++ b/examples/official-storybook/stories/addon-docs/csf-with-mdx-docs.stories.mdx @@ -0,0 +1,10 @@ +import { Meta, Story } from '@storybook/addon-docs/blocks'; +import * as stories from './csf-with-mdx-docs.stories'; + + + +# Button + +I can define a story with the function imported from CSF: + +{stories.basic}