storybook/docs/api/csf.md

241 lines
9.8 KiB
Markdown
Raw Normal View History

2020-08-03 20:44:36 +01:00
---
title: 'Component Story Format (CSF)'
---
<YouTubeCallout id="uH9_dfc-6Kc" title="Test components the EASY way | Component Story Format 3" />
2020-08-10 12:58:21 -04:00
Component Story Format (CSF) is the recommended way to [write stories](../writing-stories/introduction.md). It's an [open standard](https://github.com/ComponentDriven/csf) based on ES6 modules that is portable beyond Storybook.
2020-08-03 20:44:36 +01:00
2020-08-06 09:54:18 -06:00
<div class="aside">
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
💡 If you are writing stories in the older `storiesOf()` syntax, you can find documentation in an [advanced README](../../lib/core/docs/storiesOf.md).
2020-08-03 20:44:36 +01:00
</div>
2020-08-10 12:58:21 -04:00
In CSF, stories and component metadata are defined as ES Modules. Every component story file consists of a required [default export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Using_the_default_export) and one or more [named exports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export).
2020-08-03 20:44:36 +01:00
2020-08-10 13:17:47 -04:00
CSF is supported in all frameworks except React Native, where you should use the [storiesOf API](../../lib/core/docs/storiesOf.md) instead.
2020-08-03 20:44:36 +01:00
## Default export
2020-08-03 20:44:36 +01:00
The default export defines metadata about your component, including the `component` itself, its `title` (where it will show up in the [navigation UI story hierarchy](../writing-stories/naming-components-and-hierarchy.md#sorting-stories)), [decorators](../writing-stories/decorators.md), and [parameters](../writing-stories/parameters.md).
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
The `component` field is required and used by addons for automatic prop table generation and display of other component metadata. The `title` field is optional and should be unique (i.e., not re-used across files).
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/my-component-story-mandatory-export.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
For more examples, see [writing stories](../writing-stories/introduction.md).
## Named story exports
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
With CSF, every named export in the file represents a story object by default.
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<CodeSnippets
paths={[
'react/my-component-story-basic-and-props.js.mdx',
'react/my-component-story-basic-and-props.ts.mdx',
'vue/my-component-story-basic-and-props.js.mdx',
2021-05-06 17:36:21 +01:00
'svelte/my-component-story-basic-and-props.js.mdx',
2021-09-17 15:54:43 +01:00
'angular/my-component-story-basic-and-props.ts.mdx',
2020-08-11 01:11:41 +01:00
]}
/>
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
The exported identifiers will be converted to "start case" using Lodash's [startCase](https://lodash.com/docs/#startCase) function. For example:
2020-08-10 12:58:21 -04:00
| Identifier | Transformation |
| ---------------- | :---------------: |
| name | Name |
| someName | Some Name |
| someNAME | Some NAME |
| some_custom_NAME | Some Custom NAME |
| someName1234 | Some Name 1 2 3 4 |
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
We recommend that all export names to start with a capital letter.
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
Story objects can be annotated with a few different fields to define story-level [decorators](../writing-stories/decorators.md) and [parameters](../writing-stories/parameters.md), and also to define the `name` of the story.
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
Storybook's `name` configuration element is helpful in specific circumstances. Common use cases are names with special characters or Javascript restricted words. If not specified, Storybook defaults to the named export.
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<CodeSnippets
paths={[
2020-08-14 21:46:15 +01:00
'common/my-component-story-with-storyname.js.mdx',
2020-08-11 01:11:41 +01:00
]}
/>
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
## Args story inputs
2020-08-03 20:44:36 +01:00
Starting in SB 6.0, stories accept named inputs called Args. Args are dynamic data that are provided (and possibly updated by) Storybook and its addons.
Consider Storybooks ["Button" example](../writing-stories/introduction.md#defining-stories) of a text button that logs its click events:
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<CodeSnippets
paths={[
'react/button-story-click-handler.js.mdx',
'react/button-story-click-handler.ts.mdx',
2021-02-24 17:56:50 +00:00
'vue/button-story-click-handler.2.js.mdx',
'vue/button-story-click-handler.3.js.mdx',
2021-05-06 17:36:21 +01:00
'svelte/button-story-click-handler.js.mdx',
2021-09-17 15:54:43 +01:00
'angular/button-story-click-handler.ts.mdx',
2020-08-11 01:11:41 +01:00
]}
/>
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
Now consider the same example, re-written with args:
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'react/button-story-click-handler-args.js.mdx',
2021-02-24 17:56:50 +00:00
'vue/button-story-click-handler-args.2.js.mdx',
'vue/button-story-click-handler-args.3.js.mdx',
'angular/button-story-click-handler-args.ts.mdx',
2022-01-06 20:45:26 +00:00
'svelte/button-story-click-handler-args.js.mdx',
2020-08-11 01:11:41 +01:00
]}
/>
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
Or even more simply:
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'react/button-story-click-handler-simplificated.js.mdx',
2021-09-17 15:54:43 +01:00
'angular/button-story-click-handler-simplificated.ts.mdx',
2022-01-06 20:45:26 +00:00
'vue/button-story-click-handler-simplificated.js.mdx',
2020-08-11 01:11:41 +01:00
]}
/>
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
Not only are these versions shorter and more accessible to write than their no-args counterparts, but they are also more portable since the code doesn't depend on the actions addon specifically.
2020-08-03 20:44:36 +01:00
For more information on setting up [Docs](../writing-docs/introduction.md) and [Actions](../essentials/actions.md), see their respective documentation.
2021-09-17 15:54:43 +01:00
## Play function
2020-08-03 20:44:36 +01:00
Storybook's `play` functions are small snippets of code executed when the story renders in the UI. They are convenient helper methods to help you test use cases that otherwise weren't possible or required user intervention.
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
A good use case for the `play` function is a form component. With previous Storybook versions, you'd write your set of stories and had to interact with the component to validate it. With Storybook's play functions, you could write the following story:
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'react/login-form-with-play-function.js.mdx',
'react/login-form-with-play-function.ts.mdx',
'angular/login-form-with-play-function.ts.mdx',
'vue/login-form-with-play-function.2.js.mdx',
'vue/login-form-with-play-function.3.js.mdx',
'svelte/login-form-with-play-function.js.mdx',
2021-09-17 15:54:43 +01:00
]}
/>
<!-- prettier-ignore-end -->
When the story renders in the UI, Storybook executes each step defined in the `play` function and runs the assertions without the need for user interaction.
2022-07-07 21:15:32 +01:00
## Custom render functions
Starting in Storybook 6.4, you can write your stories as JavaScript objects, reducing the boilerplate code you need to generate to test your components, thus improving functionality and usability. `Render` functions are helpful methods to give you additional control over how the story renders. For example, if you were writing a story as an object and you wanted to specify how your component should render, you could write the following:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'react/component-story-with-custom-render-function.js.mdx',
'react/component-story-with-custom-render-function.ts.mdx',
'angular/component-story-with-custom-render-function.ts.mdx',
'vue/component-story-with-custom-render-function.js.mdx',
'vue/component-story-with-custom-render-function.ts.mdx',
'preact/component-story-with-custom-render-function.js.mdx',
'web-components/component-story-with-custom-render-function.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
When Storybook loads this story, it will detect the existence of a `render` function and adjust the component rendering accordingly based on what's defined.
2021-09-17 15:54:43 +01:00
## Storybook export vs. name handling
Storybook handles named exports and the `name` option slightly differently. When should you use one vs. the other?
Storybook will always use the named export to determine the story ID and URL.
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
If you specify the `name` option, it will be used as the story display name in the UI. Otherwise, it defaults to the named export, processed through Storybook's `storyNameFromExport` and `lodash.startCase` functions.
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-test-with-storyname.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
When you want to change the name of your story, rename the CSF export. It will change the name of the story and also change the story's ID and URL.
2020-08-03 20:44:36 +01:00
It would be best if you used the `name` configuration element in the following cases:
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
1. You want the name to show up in the Storybook UI in a way that's not possible with a named export, e.g., reserved keywords like "default", special characters like emoji, spacing/capitalization other than what's provided by `storyNameFromExport`.
2. You want to preserve the Story ID independently from changing how it's displayed. Having stable Story IDs is helpful for integration with third-party tools.
2020-08-03 20:44:36 +01:00
## Non-story exports
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
In some cases, you may want to export a mixture of stories and non-stories (e.g., mocked data).
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
You can use the optional configuration fields `includeStories` and `excludeStories` in the default export to make this possible. You can define them as an array of strings or regular expressions.
2020-08-03 20:44:36 +01:00
Consider the following story file:
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-start -->
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<CodeSnippets
paths={[
'react/my-component-story-with-nonstory.js.mdx',
'react/my-component-story-with-nonstory.ts.mdx',
2021-09-17 15:54:43 +01:00
'vue/my-component-story-with-nonstory.2.js.mdx',
'vue/my-component-story-with-nonstory.3.js.mdx',
2021-05-06 17:36:21 +01:00
'svelte/my-component-story-with-nonstory.js.mdx',
2021-09-17 15:54:43 +01:00
'angular/my-component-story-with-nonstory.ts.mdx'
2020-08-11 01:11:41 +01:00
]}
/>
2020-08-03 20:44:36 +01:00
2020-08-11 01:11:41 +01:00
<!-- prettier-ignore-end -->
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
When this file renders in Storybook, it treats `ComplexStory` and `SimpleStory` as stories and ignores the `data` named exports.
2020-08-03 20:44:36 +01:00
2021-09-17 15:54:43 +01:00
For this particular example, you could achieve the same result in different ways, depending on what's convenient:
2020-08-03 20:44:36 +01:00
- `includeStories: /^[A-Z]/`
2020-08-03 20:44:36 +01:00
- `includeStories: /.*Story$/`
- `includeStories: ['SimpleStory', 'ComplexStory']`
- `excludeStories: /^[a-z]/`
2020-08-06 09:54:18 -06:00
- `excludeStories: /.*Data$/`
- `excludeStories: ['simpleData', 'complexData']`
2022-07-25 18:51:24 +02:00
The first option is the recommended solution if you follow the best practice of starting story exports with an uppercase letter (i.e., use UpperCamelCase).