Component Story Format
Source file extensions: .stories.(js|jsx|ts|tsx)
Stories are expressed in Storybook’s Component Story Format (CSF).
WebComponents.dev supports Storybook 6.x improvements.
In CSF, stories and component metadata are defined as ES Modules. Every component story file consists of a required default export and one or more named exports.
Stories
Stories are named exports returning a function.
export const BasicButton = () => story;
The rendering of story
depends on his type.
String
export const BasicButton = () => '<button>Basic</button>';
HTMLElement
var element = document.createElement('button');
element.appendChild(document.createTextNode('Click Me!'));
export const BasicButton = () => element;
Or any of the following frameworks
lit-html
import { html } from 'lit-html';
import './my-button.js';
const name = 'World';
export const BasicButton = () => html`<button>Hello ${name}</button>`;
FAST
import { html } from '@microsoft/fast-element';
import './my-button.js';
const name = 'World';
export const BasicButton = () => html`<button>Hello ${name}</button>`;
uhtml
import { html } from 'uhtml';
import './my-button.js';
const name = 'World';
export const BasicButton = () => html`<button>Hello ${name}</button>`;
lighterhtml
import { html } from 'lighterhtml';
import './my-button.js';
const name = 'World';
export const BasicButton = () => html`<button>Hello ${name}</button>`;
Omi (JSX)
/** @jsx h */
import './index.js';
import { h } from 'omi';
export const story1 = () => <my-counter></my-counter>;
Stencil (JSX)
/* @jsx h */
import { h } from '@stencil/core';
import './my-counter.tsx';
export const story1 = () => <my-counter></my-counter>;
Preact (JSX)
/** @jsx h */
import { h } from 'preact';
import MyCounter from './index.js';
export const story1 = () => <MyCounter></MyCounter>;
React (JSX)
import React from 'react';
import MyCounter from './index.js';
export const story1 = () => <MyCounter></MyCounter>;
Riot
import Counter from './index.riot';
export const story1 = () => Counter;
export const story2 = () => ({
Component: Counter,
props: {
count: 5,
},
});
Sinuous
import { html } from 'sinuous';
import MyCounter from './index.js';
export const story1 = () => html`<div><${MyCounter}></${MyCounter}></div>`;
Solid (JSX)
import MyCounter from './index.js';
export const story1 = () => MyCounter;
export const jsx = () => () => <MyCounter />; // Double function
Svelte
import Counter from './index.svelte';
export const story1 = () => Counter;
export const story2 = () => ({
Component: Counter,
props: {
count: 5,
},
});
Vue 3
import MyCounter from './index.vue';
export const story1 = () => ({
components: { MyCounter },
template: '<my-counter></my-counter>',
});
Stories are rendered using our open-source universal-story-renderer.
You can also configure a global preview module that will apply to all stories
Parameters
Supported parameters
are:
parameters: {
backgrounds: {
default: "light" | "dark",
},
layout: "centered" | "padded" | "fullscreen",
}
You can default the parameters of all stories in export default
export default {
parameters: {
backgrounds: {
default: 'dark',
},
layout: 'centered',
},
};
Or setup for a specific story
export const BasicButton = () => html`<button>Hello ${name}</button>`;
BasicButton.parameters = {
backgrounds: {
default: 'dark',
},
layout: 'centered',
};
Args
Args
allows stories to receive dynamic data as input arguments.
const Template = (args) => <Button {...args} />;
export const Text = Template.bind({});
Text.args = { label: 'hello', background: '#ff0' };
export const Emoji = Template.bind({});
Emoji.args = { ...Text.args, label: '😀 😎 👍 💯' };
For more details see Introducing Storybook Args.
Component Story Format 3.0
CSF 3.0 and improves on developer experience.
Story objects
CSF 3.0 now relies on stories being defined as objects, simplifying how to compose them. For example, this is how you previously had to write a new story based on a previous one:
export const PrimaryOnDark = Primary.bind({});
PrimaryOnDark.args = Primary.args;
PrimaryOnDark.parameters = { background: { default: 'dark' } };
While you are now able to use the spread operator to copy that previous story definition:
export const PrimaryOnDark = {
...Primary,
parameters: { background: { default: 'dark' } },
};
Default render functions
In CSF 3.0, story rendering is defined through render functions
. You would typically define a local default render function
like this:
export default {
render: (args) => <Button {...args} />,
};
Meaning that your stories would default to being rendered this way.
However since this is what you are going to do most of the time (spreading args into your component properties), CSF 3.0 introduces automatic default render functions
, which means you do not even need to specify this default render function yourself: the runtime has defaults for the detected framework.
Note that the framework will be detected considering either of those two:
- import of
@storybook/XXX
where XXX is the framework you're targeting (like@storybook/react
) - the presence of the field
framework
on the default export, possible values being the same as the@storybook/XXX
libraries. For example:
export default {
framework: 'react',
};
Play functions
Play functions
allow you to add automated interactions to your stories.
Defining those interactions is done in the play
field of your story. Here is a simple example that will yield a story clicking twice on the "Hello" button:
import { screen } from '@testing-library/dom';
import userEvent from '@divriots/play-user-event';
export const clickPrimary = {
args: { variant: 'primary' },
play: () => {
setTimeout(async () => {
await userEvent.click(screen.getByText('Hello'));
await userEvent.click(screen.getByText('Hello'));
}, 1000);
},
};
We recommend the use of @divriots/play-user-event as it will allow you to have visual feedback on those interactions.
References
Quick example
index.stories.js
import './index.ts';
import { html } from 'lit-html';
export default {
parameters: {
backgrounds: {
default: 'dark',
},
layout: 'centered',
},
};
export const story1 = () => html` <name-tag>John Foo</name-tag> `;
export const story2 = () => html` <name-tag>John Bar</name-tag> `;
story2.parameters = { backgrounds: { default: 'light' } };