2020-03-02 13:11:04 -06:00
# Storybook
2024-08-22 14:15:12 -05:00
[Storybook ](https://storybook.js.org/ ) 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.
2020-03-02 13:11:04 -06:00
Storybook is:
- A good way to publish our design system with its implementations
- Used as a tool for documentation
- Used for debugging and displaying edge cases
## How to create stories
2024-08-22 14:15:12 -05:00
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` .
2020-03-02 13:11:04 -06:00
### Writing stories
2024-08-22 14:15:12 -05:00
When writing stories, we use the [CSF format ](https://storybook.js.org/docs/formats/component-story-format/ ).
> **Note:** For more in-depth information on writing stories, see [Storybook’ s documentation](https://storybook.js.org/docs/basics/writing-stories/).
2020-03-02 13:11:04 -06:00
With the CSF format, the default export defines some general information about the stories in the file:
2024-08-22 14:15:12 -05:00
- **`title`**: Where the component is going to live in the hierarchy
- **`decorators`**: A list which can contain wrappers or provide context, such as theming
Example:
2020-03-02 13:11:04 -06:00
```jsx
// In MyComponent.story.tsx
import MyComponent from './MyComponent';
export default {
title: 'General/MyComponent',
component: MyComponent,
decorators: [ ... ],
}
```
2024-08-22 14:15:12 -05:00
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:
2020-03-02 13:11:04 -06:00
```jsx
// Will produce a story name “some story”
export const someStory = () => < MyComponent / > ;
```
2024-08-22 14:15:12 -05:00
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.
2020-03-02 13:11:04 -06:00
### Categorization
2024-08-22 14:15:12 -05:00
We have these categories of components:
2020-03-02 13:11:04 -06:00
- **Docs Overview** - Guidelines and information regarding the design system
- **Forms** - Components commonly used in forms such as different kind of inputs
- **General** - Components which can be used in a lot of different places
- **Visualizations** - Data visualizations
- **Panel** - Components belonging to panels and panel editors
## Writing MDX documentation
2024-08-22 14:15:12 -05:00
An MDX file is a markdown file with the possibility to add JSX. These files are used by Storybook to create a “docs” tab.
2020-03-02 13:11:04 -06:00
### Link the MDX file to a component’ s stories
To link a component’ s stories with an MDX file you have to do this:
```jsx
// In TabsBar.story.tsx
2020-11-24 03:38:41 -06:00
import { TabsBar } from './TabsBar';
2020-03-02 13:11:04 -06:00
// Import the MDX file
2020-11-24 03:38:41 -06:00
import mdx from './TabsBar.mdx';
2020-03-02 13:11:04 -06:00
export default {
2020-11-24 03:38:41 -06:00
title: 'General/Tabs/TabsBar',
2020-03-02 13:11:04 -06:00
component: TabsBar,
parameters: {
docs: {
// This is the reference required for the MDX file
page: mdx,
},
},
};
```
### MDX file structure
2024-08-22 14:15:12 -05:00
The MDX file should contain the following items:
2020-03-02 13:11:04 -06:00
- When and why the component should be used
- Best practices - dos and don’ ts for the component
- Usage examples with code. It is possible to use the `Preview` element to show live examples in MDX
- Props table. This can be generated by doing the following:
```jsx
// In MyComponent.mdx
2020-11-24 03:38:41 -06:00
import { Props } from '@storybook/addon-docs/blocks';
import { MyComponent } from './MyComponent';
2020-03-02 13:11:04 -06:00
< Props of = {MyComponent} / > ;
```
### MDX file without a relationship to a component
2024-08-22 14:15:12 -05:00
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:
2020-04-06 23:59:50 -05:00
- The file needs to be named `*.story.mdx`
- A `Meta` tag must exist that says where in the hierarchy the component lives. It can look like this:
2020-03-02 13:11:04 -06:00
```jsx
< Meta title = "Docs Overview/Color Palettes" / >
# Guidelines for using colors
...
```
2024-08-22 14:15:12 -05:00
You can add parameters to the `Meta` tag. This example shows how to hide the tools:
2020-03-02 13:11:04 -06:00
```jsx
< Meta title = "Docs Overview/Color Palettes" parameters = {{ options: { isToolshown: false } } } / >
# Guidelines for using colors
...
```
## Documenting component properties
2024-08-22 14:15:12 -05:00
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.
2020-03-02 13:11:04 -06:00
### Comments
2024-08-22 14:15:12 -05:00
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 ](https://github.com/reactjs/react-docgen ) and are formatted by writing `/** */` .
2020-03-02 13:11:04 -06:00
```jsx
interface MyProps {
/** Sets the initial values, which are overridden when the query returns a value*/
defaultValues: Array< T > ;
}
```
2020-11-24 03:38:41 -06:00
### Controls
2020-03-02 13:11:04 -06:00
2024-08-22 14:15:12 -05:00
The [controls addon ](https://storybook.js.org/docs/react/essentials/controls ) 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.
2020-03-02 13:11:04 -06:00
2020-11-24 03:38:41 -06:00
#### Migrating a story from Knobs to Controls
2020-03-02 13:11:04 -06:00
2024-08-22 14:15:12 -05:00
As a test, we migrated the [button story ](https://github.com/grafana/grafana/blob/main/packages/grafana-ui/src/components/Button/Button.story.tsx ).
Here's the guide on how to migrate a story to controls.
2020-03-02 13:11:04 -06:00
2020-11-24 03:38:41 -06:00
1. Remove the `@storybook/addon-knobs` dependency.
2024-08-22 14:15:12 -05:00
2. Import the `Story` type from `@storybook/react`
2020-11-24 03:38:41 -06:00
`import { Story } from @storybook/react`
2024-08-22 14:15:12 -05:00
3. Import the props interface from the component you're working on (these must be exported in the component):
2020-11-24 03:38:41 -06:00
`import { Props } from './Component'`
2024-08-22 14:15:12 -05:00
4. Add the `Story` type to all stories in the file, then replace the props sent to the component and remove any knobs:
2020-11-24 03:38:41 -06:00
2024-08-22 14:15:12 -05:00
Before:
2020-11-24 03:38:41 -06:00
```tsx
export const Simple = () => {
const prop1 = text('Prop1', 'Example text');
const prop2 = select('Prop2', ['option1', 'option2'], 'option1');
return < Component prop1 = {prop1} prop2 = {prop2} / > ;
};
```
2024-08-22 14:15:12 -05:00
After:
2020-11-24 03:38:41 -06:00
```tsx
export const Simple: Story< Props > = ({ prop1, prop2 }) => {
return < Component prop1 = {prop1} prop2 = {prop2} / > ;
};
```
2024-08-22 14:15:12 -05:00
5. Add default props (or `args` in Storybook language):
2020-11-24 03:38:41 -06:00
```tsx
Simple.args = {
prop1: 'Example text',
prop2: 'option 1',
};
```
2024-08-22 14:15:12 -05:00
6. 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:
2020-03-02 13:11:04 -06:00
2020-11-24 03:38:41 -06:00
```tsx
export default {
title: 'Component/Component',
component: Component,
argTypes: {
prop2: { control: { type: 'select', options: ['option1', 'option2'] } },
},
};
```
2020-03-02 13:11:04 -06:00
## Best practices
2024-08-22 14:15:12 -05:00
- When creating a new component or writing documentation for an existing one, add a code example. The example should always cover the basic use case it was intended for.
2020-03-02 13:11:04 -06:00
- Use stories and knobs to create edge cases. If you are trying to solve a bug, try to reproduce it with a story.
2024-08-22 14:15:12 -05:00
- Do not create stories in the MDX. Instead, create them in the `*.story.tsx` file.