How to get real data into Storybook

 

Ray Knight

Ray Knight practices software engineering at GenUI.

Updated Feb 26, 2020

The following is a copy of an article originally posted here: https://medium.com/@arrayknight/how-to-get-real-data-into-storybook-8915f5371b6

I’ve used Storybook on several projects and introduced it to clients and companies I’ve worked for alike. It’s almost always immediately received as valuable to designers, developers, and stakeholders. It’s an amazing tool to help show design system development long before complete pages are viable, catch edge case and responsive issues early, and as a means of documentation for developers coming onto the project post launch.

But there’s typically a disconnect between presentation and content. Often times the component stories are either extremely light on content or have some amount of mocked content. This can lead to the need for additional manual testing or issues that aren’t caught until production.

Many of us have been on projects where designers don’t have access to content before needing to provide comps, copy writers don’t have enough context about how much content the client will provide/design will be able to support, or developers don’t provide realistic examples of content in Storybook. All of these scenarios can be the potential source of UX/design/component issues.

Some of the biggest reasons to pipe real data into your storybook components are:

  • Validate that component design handles content beyond lorem ipsum
  • Validate that content updates from the copy team meet design expectations
  • Provide a visual preview environment for a headless CMS or API data that would otherwise be difficult to find in context
  • Test feature updates and/or mixtures of content in a fast iteration environment

link Introducing: Storybook Addon Headless

Storybook addon headless provides a simple way to establish queries for your component stories with support for both Restful and GraphQL APIs. It also provides an easy config driven way to define and validate form inputs for variables that get fed into those queries.

link Getting Setup and Running

link Install

npm i -D storybook-addon-headless

link Register

In .storybook/main.js

module.exports = {  
    addons: ['storybook-addon-headless/register'],  
}

link Decorate

Most commonly your Storybook will only need to connect to a single API, whether that’s Restful of GraphQL.

In .storybook/preview.js

Implement either restful or graphql to establish a base config. Both are optional, you’ll have another opportunity to provide/extend these configs in the parameters. Also, if needed, you can use both Restful and GraphQL in the same project.

import { addDecorator } from '@storybook/react'  
import { withHeadless } from 'storybook-addon-headless'addDecorator(withHeadless({  
    restful: {  
        baseURL: 'https://api.server.mock/v3/'  
    },  
    graphql: {  
        uri: 'https://www.server.mock/graphql/'  
    }  
}))

If you need to be able to query against multiple APIs of the same type, you can use the config parameter on an individual query. There is also a planned feature to support multiple global configs.

The Restful config accepts any/all of the Axios request config options.

The GraphQL config accepts any/all of the Apollo Boost config options.


link Update (02/20/2020)

It’s now possible to pass an array of base configs to options. If you need to connect to multiple APIs of the same type, pass an array of base configs, each with an id that is unique and then referenced in story parameters with the base property.


link Query

Most commonly you’ll want to setup queries for stories on a per story file basis. It’s also possible to setup global queries and per story queries, or to abstract the configs to a common file to be reused between story files.

In Component.stories.tsx (however you have your story file defined)

Restful:

export default {  
    ...,  
    parameters: {  
        headless: {  
            Foos: 'foos',  
            Foo: {  
                query: 'foos/{id}',  
                variables: {  
                    id: {  
                        type: 'integer',  
                        minimum: 1,  
                    }  
                }  
            }  
        }  
    }  
}

GraphQL:

import { gql } from 'apollo-boost'  
import { pack } from 'storybook-addon-headless'export default {  
    ...,  
    parameters: {  
        headless: {  
            Foos: pack(gql`  
                {  
                    foos {  
                        id  
                        name  
                    }  
                }  
            `),  
            Foo: {  
                query: pack(gql`  
                    query Foo($id: Int) {  
                        foo(id: $id) {  
                            id  
                            name  
                        }  
                    }  
                `),  
                variables: {  
                    id: {  
                        type: 'integer',  
                        minimum: 1,  
                    }  
                }  
            }  
        }  
    }  
}

Each variable config should be one of the supported variable type schemas. The schemas are validated by Ajv with the optional Ajv-keywords supported.

What’s up with pack wrapped around the gql tag? When a GraphQL Tag DocumentNode is created, it’s a complex, deeply nested, object of metadata that must be passed through Storybook. This process means that the DocumentNode gets stringified via telejson which has a maxDepth that’s not compatible with this level of data complexity. Until this issue has been resolved, packing the DocumentNode is required.

Be sure to check out the other parameters, for things like default values, transforms, and fetching data on story initialization.

link Render

The last piece to implement is passing that data into your component!

In Component.stories.tsx (however you have your story file defined)

import { StoryContext } from '@storybook/addons'export const MyStory = ({ data }: StoryContext) => {  
    if (Array.isArray(data.Foos)) {  
        return   
    }    // Alternative render without real data  
}

You can also feel free to render the component without data, or with Knobs, until the fetched data exists.

 Story Book

link References

The documentation site with working examples:
https://storybook-addon-headless.netlify.com/

The source code can be found on GitHub:

https://github.com/ArrayKnight/storybook-addon-headless

 Story Book

How can we help?

Can we help you apply these ideas on your project? Send us a message! You'll get to talk with our awesome delivery team on your very first call.