Setting up Storybook in TypeScript/Sass environment with Webpack 4

Storybook is a wonderful tool for developing UI components. It allows you to see your components in all their supported states by providing proper data to them. It supports quite a few libraries (Angular, Vue, React, and others). We have been using it for quite some time in our shared UI elements packages without issues. However, when we tried to add it to our Moje VZP Single Page App it was not exactly easy to make it work. In this article, we will describe the process of setting up the Storybook integration and the issues we encountered.

Project stack

The previous post on this blog goes into great detail about the Moje VZP project technology stack, so let’s just point out the relevant information here.

The app is written in React using TypeScript. The code is bundled using Webpack 4 with TypeScript compiler and Babel. The styles are written in Sass and are bundled by Webpack as well.

Initial setup

The recommended way to set Storybook up is by using their CLI utility — @storybook/cli. While this utility works well for “standard” projects, it has two issues that prevented us from using it:

  • it uses npm instead of yarn that we use
  • it does not take neither TypeScript nor Sass into account

Because of those two reasons we have to set Storybook up manually by loosely following the slow start guide.

The first step is to install Storybook itself by running yarn add -D @storybook/react @types/storybook__react. This adds the React version of Storybook and TypeScript type definitions for it (this allows us to write the stories in TypeScript, more on that later on).

Next, we add a script for starting the Storybook to our package.json:

package.json with the Storybook script

Finally, we create the config file .storybook/config.js in our app's root:

First version of Storybook's config.js

The file tells Storybook that the story files are located inside the app folder and their names end in .stories.tsx. The reason for not using the default file is that we think it is more clear when the stories are right next to "their" components rather than in some arbitrary folder. So for example for the Currency component specified in Currency.tsx there is now Currency.stories.tsx file with its stories right next to it.

Notice that the stories will be written in TypeScript. In a project with components written in TypeScript as well this makes writing the stories much easier — the prop types are checked therefore you are unlikely to pass nonsensical data to the component. This is especially useful with components that display complex data.

Making TypeScript work

Unfortunately, Storybook does not support TypeScript out of the box. There is a guide for setting it up, however we ran into some issues using it.

The guide states that we need to extend the Storybook Webpack config in order to use TypeScript files. However, there are two issues with this:

  • the current stable version of Storybook (3) uses Webpack 3, whereas we use Webpack 4
  • the guide suggests using awesome-typescript-loader, whereas we use ts-loader

These two facts together mean that if we wanted to use ts-loader we already use in bundling our app (which is only compatible with Webpack 4), we would need to have two versions of ts-loader – one for Webpack 4 for bundling our app and one for Webpack 3 to use with Storybook. This is of course not desirable. Fortunately, there is an alpha version of Storybook 4 available that uses Webpack 4.

Therefore the first step to make the TypeScript work is to install the alpha version — yarn add -D @storybook/react@alpha. Next, we can create the .stories/webpack.config.js:

First version of Storybook’s webpack.config.js

As for the tsconfig.test.json, it extends our "normal" tsconfig.json and overrides some settings needed by Storybook (and Jest as well, hence the name test):

Storybook-compatible tsconfig.test.json

As we can see, it uses the extend functionality to override our standard configuration. It changes is the module system and tells TypeScript to compile JSX (our base config uses the esnext module system and does not compile JSX). This makes TypeScript output files Storybook can use.

This is all it takes to make TypeScript work (tested with @storybook/react@4.0.0-alpha.16).

Making styles work

As stated earlier, the styles for our app are written in Sass. This again requires additional configuration in Storybook.

First, we need to make sure Storybook knows how to process .scss files by updating .stories/webpack.config.js:

Second version of Storybook’s webpack.config.js

As we can see, the setup is similar to the TypeScript one — we add appropriate loaders and add the extension. Note that we did not add loader configuration for CSS as Storybook supports it by default and adding it here would cause errors. The getSassLoaderConfig function is used by our standard Webpack config as well and looks like this:

The file with loader utilities — webpack.commons.js

The last thing to do is to include the styles in the stories. This can be done for all the stories at once by updating the .storybook/config.js with an import of the styles' main file:

Second version of Storybook’s config.js

Writing components properly

Having made Storybook work with our stack, the last thing is to write the stories themselves. There is a guide on how to do it so we will not go into detail here.

The important thing is that you must write your components in a way that makes using them in Storybook possible. For us this meant splitting components using Redux into two as mocking Redux would be impractical.

Let’s use HotNews as an example:

HotNews component before the split

This component downloads some news data and displays it. This works well, but we cannot write stories for it because it is connected to Redux which is not available in Storybook. The solution is to split HotNewsList into two components:

HotNews component after the split

The HotNews component no longer handles the data obtaining, just the data display. Thanks to this change it can now be used in Storybook, we just provide it the relevant data. There is no need for the complication of mocking Redux.

The logic behind getting the data from server and connecting to Redux was moved to HotNewsContainer which uses the HotNews internally and passes it the data from the server. Also, wherever we previously used HotNews in our app, we now must use HotNewsContainer instead.

Summary

In this article we’ve shown a way to make Storybook work in an environment with TypeScript, Webpack 4 and Sass. We also discussed how to write components in a way suitable for Storybook. Hopefully, this will inspire you, to try Storybook yourself, it really is worth it!

Like what you read? Give Dan Homola a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.