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

Addon-docs: Document dynamic source snippets #12530

Merged
merged 2 commits into from
Sep 24, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions addons/docs/docs/multiframework.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -103,6 +104,59 @@ 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

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.

This dynamic rendering is framework-specific, meaning it needs a custom implementation for each framework.

Let's take a look at the React framework implementation of `dynamic` snippets as a reference for implementing this feature in other frameworks:

```tsx
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;
};
```

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.

<div class="aside">
To learn more and see how it's implemented in context, check out <a href="https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/jsxDecorator.tsx">the code</a> .
</div>

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.

<div class="aside">
To learn more and see how it's implemented in context, check out <a href="https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/react/jsxDecorator.tsx">the code</a> .
</div>

## 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)
Expand Down
5 changes: 5 additions & 0 deletions docs/frameworks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
9 changes: 8 additions & 1 deletion docs/writing-docs/doc-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -150,6 +149,14 @@ To customize the source snippet that’s displayed for a story, set the `docs.so

<!-- prettier-ignore-end -->

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