From 90f5686c2f3996331df8f6c3855518ee644cae2e Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 21 Sep 2020 17:11:30 +0800 Subject: [PATCH 1/2] Addon-docs: Document dynamic source snippets --- addons/docs/docs/multiframework.md | 42 ++++++++++++++++++++++++++++++ docs/frameworks.js | 5 ++++ docs/writing-docs/doc-blocks.md | 9 ++++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/addons/docs/docs/multiframework.md b/addons/docs/docs/multiframework.md index bb628db839cc..4824616e2f0a 100644 --- a/addons/docs/docs/multiframework.md +++ b/addons/docs/docs/multiframework.md @@ -6,6 +6,7 @@ Storybook Docs [provides basic support for all non-RN Storybook view layers](../ - [Arg tables](#arg-tables) - [Component descriptions](#component-descriptions) - [Inline story rendering](#inline-story-rendering) +- [Dynamic source rendering](#dynamic-source-rendering) - [More resources](#more-resources) ## Framework-specific configuration @@ -103,6 +104,47 @@ addParameters({ The input is the story function and the story context (id, parameters, args, etc.), and the output is a React element, because we render docs pages in react. In the case of Vue, all of the work is done by the `@egoist/vue-to-react` library. If there's no analogous library for your framework, you may need to figure it out yourself! +## Dynamic source rendering + +Starting in 6.0 there are several modes of rendering stories in the [Source doc block](https://storybook.js.org/docs/react/writing-docs/doc-blocks#source). The `dynamic` source type renders a snippet based on the output of a story function. For example React +s `dynamic` snippets are JSX reconstructed based on the React node. + +Since this dynamic rendering is framework-specific, it must be added to each framework. Here we dissect the React framework implementation as a reference for implementing this feature in other frameworks. + +Here are the key lines of the [React preview config](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/config.ts): + +```ts +import { jsxDecorator } from './jsxDecorator'; +export const decorators = [jsxDecorator]; +``` + +This function gets run on every story. Diving into [its implementation](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/jsxDecorator.tsx): + +```ts +import { addons, StoryContext } from '@storybook/addons'; +import { SNIPPET_RENDERED } from '../../shared'; + +export const jsxDecorator = (storyFn: any, context: StoryContext) => { + const story = storyFn(); + + // We only need to render JSX if the source block is actually going to + // consume it. Otherwise it's just slowing us down. + if (skipJsxRender(context)) { + return story; + } + + const channel = addons.getChannel(); + + const options = {}; // retrieve from story parameters + const jsx = renderJsx(story, options); + channel.emit(SNIPPET_RENDERED, (context || {}).id, jsx); + + return story; +}; +``` + +The `renderJsx` function is a react-specific function that takes the output of a story function and converts it into a framework-appropriate string. The resulting value is emitted on Storybook's channel and subsequently consumed up by the Source block for that story, if one exists. + ## More resources - References: [README](../README.md) / [DocsPage](docspage.md) / [MDX](mdx.md) / [FAQ](faq.md) / [Recipes](recipes.md) / [Theming](theming.md) / [Props](props-tables.md) diff --git a/docs/frameworks.js b/docs/frameworks.js index b9e92ba0f1a1..6bf0e9f75be2 100644 --- a/docs/frameworks.js +++ b/docs/frameworks.js @@ -109,6 +109,11 @@ module.exports = { unsupported: [], path: 'writing-docs/doc-blocks#source', }, + { + name: 'Dynamic source', + supported: ['react'], + path: 'writing-docs/doc-blocks#source', + }, { name: 'Args Table', supported: ['react', 'vue', 'angular', 'html', 'ember', 'web-components'], diff --git a/docs/writing-docs/doc-blocks.md b/docs/writing-docs/doc-blocks.md index 29e86d761568..00faddd48439 100644 --- a/docs/writing-docs/doc-blocks.md +++ b/docs/writing-docs/doc-blocks.md @@ -4,7 +4,6 @@ title: 'Doc Blocks' Doc Blocks are the building blocks of Storybook documentation pages. By default, [DocsPage](./docs-page.md) uses a combination of the blocks below to build a page for each of your components automatically. - Custom [addons](../api/addons.md) can also provide their own doc blocks. ## ArgsTable @@ -150,6 +149,14 @@ To customize the source snippet that’s displayed for a story, set the `docs.so +There is also a `docs.source.type` parameter that controls how source is auto-generated. Valid values include: + +| Value | Description | Support | +| :----------------- | :------------------------------------------------------------------------------------------------------------------ | :------------------------------------------: | +| **auto** (default) | Use `dynamic` snippets if the story is written using [Args](../writing-stories/args) and the framework supports it. | All | +| **dynamic** | Dynamically generated snippet based on the output of the story function, e.g. JSX code for react. | [Limited](../api/frameworks-feature-support) | +| **code** | Use the raw story source as written in the story file. | All | + ### MDX You can also use the `Source` block in MDX. It accepts either a story ID or `code` snippet. Use the `language` for syntax highlighting. From 36da8085d20e0ac875f820eff3535712ed9fbd5b Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Thu, 24 Sep 2020 15:46:53 +0800 Subject: [PATCH 2/2] Update multiframework.md --- addons/docs/docs/multiframework.md | 38 ++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/addons/docs/docs/multiframework.md b/addons/docs/docs/multiframework.md index 4824616e2f0a..f2148511e5f0 100644 --- a/addons/docs/docs/multiframework.md +++ b/addons/docs/docs/multiframework.md @@ -106,21 +106,13 @@ The input is the story function and the story context (id, parameters, args, etc ## Dynamic source rendering -Starting in 6.0 there are several modes of rendering stories in the [Source doc block](https://storybook.js.org/docs/react/writing-docs/doc-blocks#source). The `dynamic` source type renders a snippet based on the output of a story function. For example React -s `dynamic` snippets are JSX reconstructed based on the React node. +With the release of Storybook 6.0, we've improved how stories are rendered in the [Source doc block](https://storybook.js.org/docs/react/writing-docs/doc-blocks#source). One of such improvements is the `dynamic` source type, which renders a snippet based on the output the story function. -Since this dynamic rendering is framework-specific, it must be added to each framework. Here we dissect the React framework implementation as a reference for implementing this feature in other frameworks. +This dynamic rendering is framework-specific, meaning it needs a custom implementation for each framework. -Here are the key lines of the [React preview config](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/config.ts): +Let's take a look at the React framework implementation of `dynamic` snippets as a reference for implementing this feature in other frameworks: -```ts -import { jsxDecorator } from './jsxDecorator'; -export const decorators = [jsxDecorator]; -``` - -This function gets run on every story. Diving into [its implementation](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/jsxDecorator.tsx): - -```ts +```tsx import { addons, StoryContext } from '@storybook/addons'; import { SNIPPET_RENDERED } from '../../shared'; @@ -143,7 +135,27 @@ export const jsxDecorator = (storyFn: any, context: StoryContext) => { }; ``` -The `renderJsx` function is a react-specific function that takes the output of a story function and converts it into a framework-appropriate string. The resulting value is emitted on Storybook's channel and subsequently consumed up by the Source block for that story, if one exists. +A few key points from the above snippet: + +- The **renderJsx** function call is responsible for transforming the output of a story function into a string specific to the framework (in this case React). +- The returned snippet string is emitted on Storybook's channel through **channel.emit()** and subsequently consumed up by the Source block for any given story, if it exists. + +
+ To learn more and see how it's implemented in context, check out the code . +
+ +Now we need a way to configure how it's displayed in the UI: + +```tsx +import { jsxDecorator } from './jsxDecorator'; +export const decorators = [jsxDecorator]; +``` + +This configures the `jsxDecorator` to be run on every story. + +
+ To learn more and see how it's implemented in context, check out the code . +
## More resources