contribute/style-guides/storybook.md
Storybook is a tool which Grafana uses to manage our design system and its components. Storybook consists of stories. Each story represents a component and the case in which it is used.
To show a wide variety of use cases is good both documentation wise and for troubleshooting—it might be possible to reproduce a bug for an edge case in a story.
Storybook is:
Stories for a component should be placed next to the component file. The Storybook file requires the same name as the component file. For example, a story for SomeComponent.tsx has the file name SomeComponent.story.tsx.
If a story should be internal—not visible in production—name the file SomeComponent.story.internal.tsx.
When writing stories, we use the CSF format.
Note: For more in-depth information on writing stories, see Storybook’s documentation.
With the CSF format, the default export defines some general information about the stories in the file:
title: Where the component is going to live in the hierarchydecorators: A list which can contain wrappers or provide context, such as themingExample:
// In MyComponent.story.tsx
import MyComponent from './MyComponent';
export default {
title: 'General/MyComponent',
component: MyComponent,
decorators: [ ... ],
}
When it comes to writing the actual stories, you should continue in the same file with named exports. The exports are turned into the story name like so:
// Will produce a story name “some story”
export const someStory = () => <MyComponent />;
If you want to write cover cases with different values for props, then using knobs is usually enough. You don’t need to create a new story. This topic will be covered further down.
We have these categories of components:
An MDX file is a markdown file with the possibility to add JSX. These files are used by Storybook to create a “docs” tab.
To link a component’s stories with an MDX file you have to do this:
// In TabsBar.story.tsx
import { TabsBar } from './TabsBar';
// Import the MDX file
import mdx from './TabsBar.mdx';
export default {
title: 'General/Tabs/TabsBar',
component: TabsBar,
parameters: {
docs: {
// This is the reference required for the MDX file
page: mdx,
},
},
};
The MDX file should contain the following items:
Preview element to show live examples in MDX// In MyComponent.mdx
import { Props } from '@storybook/addon-docs/blocks';
import { MyComponent } from './MyComponent';
<Props of={MyComponent} />;
An MDX file can exist by itself without any connection to a story. This can be good for writing things such as a general guidelines page.
Two conditions must be met for this to work:
*.story.mdxMeta tag must exist that says where in the hierarchy the component lives. It can look like this:<Meta title="Docs Overview/Color Palettes"/>
# Guidelines for using colors
...
You can add parameters to the Meta tag. This example shows how to hide the tools:
<Meta title="Docs Overview/Color Palettes" parameters={{ options: { isToolshown: false }}}/>
# Guidelines for using colors
...
A quick way to get an overview of what a component does is by looking at its properties. That's why it is important that you document these in a good way.
When writing the props interface for a component, it's possible to add a comment to that specific property. When you do so, the comment will appear in the Props table in the MDX file. The comments are generated by react-docgen and are formatted by writing /** */.
interface MyProps {
/** Sets the initial values, which are overridden when the query returns a value*/
defaultValues: Array<T>;
}
The controls addon provides a way to interact with a component's properties dynamically. It also requires much less code than knobs.
Knobs are deprecated in favor of using controls.
As a test, we migrated the button story.
Here's the guide on how to migrate a story to controls.
Remove the @storybook/addon-knobs dependency.
Import the Story type from @storybook/react
import { Story } from @storybook/react
Import the props interface from the component you're working on (these must be exported in the component):
import { Props } from './Component'
Add the Story type to all stories in the file, then replace the props sent to the component and remove any knobs:
Before:
export const Simple = () => {
const prop1 = text('Prop1', 'Example text');
const prop2 = select('Prop2', ['option1', 'option2'], 'option1');
return <Component prop1={prop1} prop2={prop2} />;
};
After:
export const Simple: Story<Props> = ({ prop1, prop2 }) => {
return <Component prop1={prop1} prop2={prop2} />;
};
Add default props (or args in Storybook language):
Simple.args = {
prop1: 'Example text',
prop2: 'option 1',
};
If the component has advanced props type (that is, other than string, number, or Boolean), you need to specify these in an argTypes. Do this in the default export of the story:
export default {
title: 'Component/Component',
component: Component,
argTypes: {
prop2: { control: { type: 'select', options: ['option1', 'option2'] } },
},
};
*.story.tsx file.