Loading states with Storybook and Mock Service Worker

tmikeschu

Mike Schutte

Posted on December 5, 2020

Loading states with Storybook and Mock Service Worker

So there I am wanting to make Storybook stories for four states of a component that fetches its own data by way of an Apollo Client hook.

The four states are:

  • Loading šŸŒ
  • Error šŸšØ
  • No data šŸ“­
  • Data šŸ˜Š

I have Mock Service Worker (MSW) set up to intercept network calls. It's pretty straight forward to set up the mocks for the two data states (empty and present) with MSW's context.data and the error state with context.errors.

But how do I render a component in its loading state indefinitely for the purpose of viewing it in a Storybook demo?

I didn't find anything existing on the usual suspects of Stack Overflow, GitHub issues, and or here on DEV, so I went back to the MSW docs and found the perfect solution:

context.delay

I tried using Infinity as the duration at first, but to no avail. I figure one hour is more than enough to adequately work on or review the loading state UI.

Here is my final implementation:

graphql.query<GetTagsQuery, GetTagsQueryVariables>(
  "GetTags",
  (_req, res, ctx) => {
    // 1 hour delay to view the loading state
    return res(
      ctx.delay(1000 * 60 * 60 * 60), 
      ctx.data(newGetTagsData({}))
    );
  }
)
Enter fullscreen mode Exit fullscreen mode

And here is all of the relevant story code:

import * as React from "react";
import { Flex, Heading } from "@chakra-ui/react";
import { Meta, Story } from "@storybook/react";
import { worker } from "mocks/browser";
import { graphql } from "msw";

import { GetTagsQuery, GetTagsQueryVariables, newGetTagsData } from "api";

import ViewTags from ".";

export default {
  title: "Features/View Tags",
  component: ViewTags,
  decorators: [
    (story) => (
      <Flex py="20" flex="1" direction="column" justify="center" align="center">
        <Heading mb={4} fontSize="6xl">
          Avett Rx
        </Heading>
        {story()}
      </Flex>
    ),
  ],
} as Meta;

const Template: Story = () => <ViewTags />;

export const Loading: Story = Template.bind({});
Loading.decorators = [
  (story) => {
    worker.use(
      graphql.query<GetTagsQuery, GetTagsQueryVariables>(
        "GetTags",
        (_req, res, ctx) => {
          // 1 hour delay to view the loading state
          return res(
            ctx.delay(1000 * 60 * 60 * 60), 
            ctx.data(newGetTagsData({}))
          );
        }
      )
    );
    return story();
  },
];

Enter fullscreen mode Exit fullscreen mode

This use case comes from an article I'm publishing soon that outlines setting up a project with all of my favorite tools for React development in 2020:

šŸ‘€ Keep an eye out for it! šŸ‘€

šŸ’– šŸ’Ŗ šŸ™… šŸš©
tmikeschu
Mike Schutte

Posted on December 5, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related