If you've not yet heard of it, Storybook is an amazing tool for authoring UI components. With storybook you can develop UI components in isolation; eliminating data and environment dependencies that otherwise complicate the rendering (and testing) of components.

Storyshots

Storybook supports several addons, including my personal favorite; storyshots. Like magic, Storyshots takes the hard work you've already put in to create storybook stories, and turns each one into a jest snapshot. A little bit of configuration and you're done. Even if you've already written hundreds of stories.


Getting Started with Storybook and Storyshots

Storybook has amazing documentation to help you with nearly every topic. I recommend completing the Slow Start Guide before you continue.

Storyshots has an excellent README.md that will help you get started. Follow it well.

When you're done with both of those, we will focus on a few more advanced topics, such as:

  • Collocating your stories
  • Mocking Redux
  • Mocking context

Collocating your stories

Once you have the initial storyshots implementation all set up, it's time to make sure you can collocate your stories. That way you can have your stories right next to the code you wish to implement and test.

If you check the Writing Stories guide you notice that they add the following to their config.js file:

  import { configure } from '@storybook/react';

const req = require.context('../src/components', true, /\.stories\.js$/)

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

configure(loadStories, module);

Do this, and you'll be able to keep your storybook stories in the same folder as your component.


Mocking redux

If you're using storybook with any component that depends on data in redux state you're going to have some extra configuration on your hands before you can render it successfully. You have a few ways you can handle this, but I prefer to abstract away the complexities of mocking the store to make day-to-day development simpler. To accomplish this, we need to do the following:

  • Create a Context component
  • Add Context as a global decorator
Create a Context component

Inside your .storybook folder, create a new file named Context.js and add the contents below.

  import React, { Component } from 'react'
import configureStore from 'redux-mock-store'

const middlewares = []
const mockStore = configureStore(middlewares)

const initialState = {
  //... your state goes here
}
const store = mockStore(initialState)

class Context extends Component {
    getChildContext() {
        return {
          store: store,
        }
    }

    render() {
        return (
            {this.props.children}
        )
    }
}

Context.childContextTypes = {
  store: PropTypes.object.isRequired,
}

export default Context

don't forget to npm i -D redux-mock-store

Add Context as a global decorator

For starters, we head on over to the storybook documentation again, this time to review the section on decorators

In this section they describe how you can add a decorator globally by including the following in your config.js file:

  addDecorator((story) => (
  <Context>
    {story()}
  </Context>
))

Make sure you update your imports to include the Context component and the addDecorator function.

  import { configure, addDecorator } from '@storybook/react'
import Context from './Context'

Mocking context

In addition to a dependency on redux state, your components may also depends on React's context from time to time. A common example would be React Router's withRouter implementation, which uses context to expose various details about the route and URL.

As you can see from the section above, adding things to context is pretty much a freebie at this point. You would simply extend getChildContext() and childContextTypes in the Context component to include all of your desired fields for testing.


1,987 0 8