Categories
Storybook for React

Storybook for React — Decorators and Multiple Components

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *