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:
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({}))
);
}
)
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();
},
];
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:
- Next.js
- TypeScript
- Vercel
- Prettier
- ESlint
- Lint Staged
- Husky
- TypeScript ESLint
- Chakra UI
- Fauna
- GraphQL Code Generator
- Apollo Client
- Storybook
- Jest
- React Testing Library
- Cypress
- Mock Service Worker
- GitHub Actions
👀 Keep an eye out for it! 👀
Top comments (3)
Great overview, Mike!
A nitpick, but it's more common to render the "story" exposed in a decorator, instead of calling it as a function:
Although React components are functions, rendering them as JSX elements is more transparent in terms of what's going on. This is also how the decorators are recommended in the Storybook docs.
Thanks for writing this blogpost Mike!
I've been doing the same with standard
rest
as opposed to graphql and unfortunately when switching to a different story everything is still in the loading state. Have you figured out how to deal with this?Hey! Yeah I remember running into this (having to hard refresh between stories). Take a look at setting up unique query clients (which will cause a full rerender and fresh requests): dev.to/tmikeschu/accessing-unique-...