Boost your design system with Storybook Cartesian

Posted on 2018-10-02

Storybook Cartesian

As part of my investment in DesignOps, Iā€™ve bumped into a common problem with a rather nice solution.

Storybook Cartesian is a library that automatically generates stories for all of your component variants and puts them back in your storybook in a clean and tidy way.

Having a great React component means having it take some properties and do something useful with it. Take a look at this button:

const Button = props =>(
    <button
        style={{color: props.color}}
        enabled={!props.disabled}>
        {props.text}
    </button>)

To build stories for this component in your design system, you can use some common sense and want to test enabled / disabled, and a couple of colors. But what about empty text? what about a combination of disabled and empty text? does that create anything odd?

Instead of guessing all these combinations, you can generate them with storybook-cartesian.

Storybook cartesian in actionStorybook Cartesian in action!

On the one hand, itā€™s great to be able to click through all of the variants (instead of using knobs to force them out manually), but more importantly, if you have these stories layed out, you can get a massive test coverage with storyshots.

If youā€™re impatient, thereā€™s a fully working example in the storybook-cartesian repository.

Quick Start

$ yarn add --dev storybook-cartesian

To integrate in your own project, add the following to your stories in your storybook files. Hereā€™s a real life button being exercised to its fullest:

Understanding Cartesian Stories

The general structure for a cartesian story is this:

cartesian(<stories>)
    .add(
        <seed function>,
        <title renderer>,
        <component renderer>,
        <valid combination filter (optional)>,
        <story apply function (optional)>
    )

Which gets you this kind of story layout generated automatically (for now the last ā€œAll/all variantsā€ is a story discussed:

Stories layoutAutomatically generated with storybook-cartesian

Your seed function is responsible to generate content in the form of:

Your titleRender function gets an instance of your props and returns a string. This is where you get to build those fancy story titles that will make every generated story super visible.

const titleRender = props => `${props.one} / ${props.check}`

Your storyRender function gets an instance of your props and returns a component. This is where you can just spread the props out and have an easy win, or do something fancy and build out a storyfied component proper.

const componentRender = props => <Button {...props} />

And to make use of all of these with cartesian we can now do:

cartesian(storiesOf('Button/Cartesian'))
    .add(
        seedfn,
        titleRender,
        componentRender
    )

Last thing to rememberā€Šā€”ā€Šyour seedfn is a function. Currently it just returns a static bag of props, but itā€™s a function for good reason. You can use tools like react-fake-props to generate this seed bag of props automatically as wellā€Šā€”ā€Šwithout writing any verbatim seed code. In addition you can use this function to fetch content dynamically from a backend.### Validation and Advanced Rendering

Some times not all prop combinations make sense. For example if you have an isLoading and a results props it doesn't make sense to have both true and results populated:

// doesn't make sense
<SearchResults isLoading={true} results={['hello', 'world']}>

For this, we have a valid function that we can add on top of what weā€™ve seen until now. With it, the following will filter out this invalid combination:

cartesian(storiesOf('Button/Cartesian'))
    .add(
        seedfn,
        titleRender,
        componentRender,
        props => !(props.isLoading && props.results)
    )

The default valid function, if not given, is simply returning true always.

Some other times you might want to customize how stories get created. By default, react-cartesian will add one story per variation. For some components types (such as a button) you might not want to ā€œspendā€ an entire story.

For example, letā€™s say you want just one story to contain all cartesian product items.

All variants

For this, we have another optional function that we can use, that gives you stories and candidates back with which you can do what ever you want:

Try it out!

Storybook Cartesian is a great way to get your design system going. It will allow you to spread out all these UI states that you havenā€™t considered with no effort at all. You can take a look on Github and youā€™re welcome to try it out (or submit pull requests!).

Have fun!