Storybook for React — Decorators and Multiple Components
Tags: react, webdev, programming, javascript
Published: 2021-01-24 21:40:08

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**


Storybook lets us prototype components easily with various parameters.

In this article, we’ll look at how to write and browse stories with Storybook.

### Decorators

We can add decorators with the `decorators` property.

It’s used to wrap our component with our own markup.

For example, we can write:

`src/stories/Button.js`

```
import React from 'react';
import PropTypes from 'prop-types';
import './button.css';

export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
  const mode = primary ? 'button-primary' : 'button-secondary';
  return (
    <button
      type="button"
      className={['button', `button-${size}`, mode].join(' ')}
      style={backgroundColor && { backgroundColor }}
      {...props}
    >
      {label}
    </button>
  );
};

Button.propTypes = {
  primary: PropTypes.bool,
  backgroundColor: PropTypes.string,
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  label: PropTypes.string.isRequired,
  onClick: PropTypes.func,
};

Button.defaultProps = {
  backgroundColor: null,
  primary: false,
  size: 'medium',
  onClick: undefined,
};
```

`src/stories/Button.stories.js`

```
import React from 'react';

import { Button } from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
  decorators: [(Story) => <div style={{ margin: '20px' }}><Story /></div>]

};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Button',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Button',
};
```

to add decorators with the `decorators` property.

It’s an array with functions we render the component with.

`Story` is the component that we’re wrapping the component with.

It should be the button since we’re testing the button.

### Stories for two or more components

If we have 2 or more components, then we put them under the same folder.

For example, we can write:

`src/stories/ListItem.js`

```
import React from 'react';

export const ListItem = ({ text }) => {
  return (
    <li>
      {text}
    </li>
  );
};
```

`src/stories/List.js`

```
import React from 'react';
import PropTypes from 'prop-types';

export const List = ({ children, backgroundColor }) => {
  return (
    <ul style={{ backgroundColor }}>
      {children}
    </ul>
  );
};

List.propTypes = {
  backgroundColor: PropTypes.string
}
```

`src/stories/List.stories.js`

```
import React from 'react';
import { List } from './List';
import { ListItem } from './ListItem';

export default {
  component: List,
  title: 'List',
  argTypes: {
    backgroundColor: { control: 'color' },
  },
};

export const Empty = (args) => <List {...args} />;

export const OneItem = (args) => (
  <List {...args}>
    <ListItem text='foo' />
  </List>
);

export const ManyItems = (args) => (
  <List {...args}>
    <ListItem text='foo' />
    <ListItem text='bar' />
    <ListItem text='baz' />
  </List>
);
```

We created the `ListItem` and `List` components that we used together in our story.

The `List` component accepts the `backgroundColor` prop and we set the `argTypes` property to set the control for it to a color picker.

This way, we can set the color for it.

### Conclusion

We can compose different components and test them together within one story with Storybook.