Storybook Tutorial

Tamara Temple

Created: 2018-10-16 Tue 21:55

Introduction

Storybook Tutorial

who am I

  • Tamara Temple
  • github: tamouse
  • Software developer for a (very) long time.
  • Primary focus on back-end development for past 20 years.
  • React development since middle of 2017
  • nearly full-time
  • still do back-end Rails + GraphQL server work

what is storybook?

  • UI development environment for UI components
  • visualize different states of your UI components
  • develop them interactively
  • runs outside of your app
  • develop UI components in isolation
  • without app-specific dependencies and requirements

Quick Setup and Go

Start with your existing react app

cd path/to/project

Install the storybook cli script globally

npm install -g @storybook/cli

# - or -

yarn global add @storybook/cli

Add it to your project

getstorybook

Start compiling stories

npm run storybook

# -or-

yarn storybook

Slow Setup

Add storybook to your project

After creating your react app, add storybook for react:

yarn add @storybook/react

Configure storybook

Create the configuration directory and files:

mkdir .storybook
touch .storybook/config.js

decide how you want to organize stories

This is how storybook finds stories.

  • the quick way: load stories from a single directory
  • the react way (?): stories next to components

project structure

<project_root>/
  .storybook/
    config.js
  src/
    index.js
    App.js
    App.test.js
    App.story.js

load stories via webpack's require.context()

// .storybook/config.js
import { configure } from '@storybook/react';

function importAll(req) {
  req.keys().forEach(filename => req(filename));
}

function loadStories() {
  let req;
  req = require.context('../src', true, /\.story\.js$/);
  importAll(req);
}

configure(loadStories, module);

Write stories

Let's write the story for the main React App component

// src/App.story.js
import React from "react";
import { storiesOf } from "@storybook/react";
import App from "./App";

storiesOf("Main React App", module).add("the app", () => <App />);

Run storybook

Add a script to package.json to run storybook

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject",
  "storybook": "start-storybook -p 9009 -c .storybook"
},

Run the script and open your browser to http://localhost:9009

yarn run storybook

# or just

yarn storybook

Basic usage

A simple component

Let's build a simple horizontal input field component.

This will be wrapped in a form in the application

export class HorizontalInputField extends React.Component {
  render() {
    const { id, label, name, onChange } = this.props;
    return (
      <FormGroup>
        <label for={name}>{label}</label>
        <input
          className="form-control"
          id={id}
          type="text"
          name={name}
          onChange={onChange}
        />
      </FormGroup>
    );
  }
}

A simple set of stories

The corresponding story wraps the component to display it properly

import HorizontalInputField from "./index";
storiesOf("components/HorizontalInputField", module)
  .add("basic example", () => (
    <Wrapper>
      <Form>
        <HorizontalInputField
          id="email-input-field"
          name="email"
          label="Email:"
          onChange={noop}
        />
      </Form>
    </Wrapper>
 ));

Extending stories using addons

Besides the aforementioned actions addon, storybook has many other addons:

https://storybook.js.org/addons/addon-gallery/

Small set I've used:

  • Actions: event "inspector"
  • Apollo: a mock Apollo Client provider
  • Storybook-router: router providers for React and Vue

Actions Addon

Install the addon

yarn add @storybook/addon-actions

Add to configuration

// .storybook/addons.js
import "@storybook/addons/register"

Add them to your story

// ...
import { action } from "@storybook/addon-actions/register"
// ...

storiesOf("components/HorizontalInputField", module)
  .add("basic example", () => (
    <Wrapper>
      <Form>
        <HorizontalInputField
          id="email-input-field"
          name="email"
          label="Email:"
          onChange={action("email field changed")}
        />
      </Form>
    </Wrapper>
 ));

See the events as they trigger

Every time the input field changes, an event is shown in the "Action Logger" area

Tips

Organizing stories

Follow the same organizing principles you use with tests

  • in separate tree
  • alongside component

Share test data

Reuse test data from your specs by organizing it to share

use a Consumer

  • keep stories light
  • common elements to present the story

Example:

class Consumer extends React.Component {
  render() {
    <Component {...this.props} />
  }
}

storiesOf("Component", module)
  .add("example one", () => (<Consumer thing="one" /> ))
  .add("example two", () => (<Consumer thing="two" /> ))

using jsdoc and markup

Extract and display in story

generators

Create component, spec, and story boilerplate

@me

  • Tamara Temple
  • github: tamouse
  • twitter: @tamouse
Created by Tamara Temple <tamara@tamouse.org>