# Storybook

## Installation

To install [Storybook](https://storybook.js.org/) you will only need to run:

```
npm i --save-dev @storybook/react @types/storybook__react awesome-typescript-loader react-docgen-typescript-webpack-plugin
```

See their [official web page](https://storybook.js.org/configurations/typescript-config/) for more.

## Plugins

In Storybook we have something called addons.&#x20;

### Recommended plugins

* actions: can be used to display data received by event handlers.
* info: will show additional information for stories.
* knobs: allow you to edit React props dynamically using the Storybook UI.
* jsx: shows the JSX of the component.

All of them can be installed by running:

```
npm i --save-dev @storybook/addon-actions @types/storybook__addon-actions
npm i --save-dev @storybook/addon-info @types/storybook__addon-info
npm i --save-dev @storybook/addon-knobs @types/storybook__addon-knobs
npm i --save-dev storybook-addon-jsx
```

## Configuration

We will proceed to create a config folder only for Storybook in the root of our project. It will look like this:

```
.storybook
├── addons.js
├── config.js
├── style.js
└── webpack.config.js
```

**addons.js** file will import all the addons we need for Storybook:

```
import "@storybook/addon-options/register";
import "@storybook/addon-knobs/register";
import "@storybook/addon-actions/register";
import 'storybook-addon-jsx/register';
```

The **config.js** file will contain all the configuration. Is the one responsible to load all the stories:

```
import { configure, addDecorator, setAddon } from "@storybook/react";
import "../src/styles.css";
import { setOptions } from "@storybook/addon-options";
import { withKnobs } from "@storybook/addon-knobs";
import JSXAddon from 'storybook-addon-jsx';

setAddon(JSXAddon);

// Load all the stories
const req = require.context("../src/components", true, /\.story\.tsx$/);

function loadStories() {
    req.keys().forEach(filename => req(filename));
}

// Default decorators
addDecorator(withKnobs);

// Addon-options configuration
setOptions({
    name: "shark-events-client",
    addonPanelInRight: false,
    sortStoriesByKind: true,
    sidebarAnimations: false
});

configure(loadStories, module);
```

Finally, we will need to set our **webpack.config.js** file. Because we are using TypeScript, our info addon will need to import a plugin called react-docgen-typescript-webpack-plugin:

```
const path = require("path");
const TSDocgenPlugin = require("react-docgen-typescript-webpack-plugin");
module.exports = (baseConfig, env, config) => {
    config.module.rules.push({
        test: /\.(ts|tsx)$/,
        loader: require.resolve("awesome-typescript-loader")
    });
    config.plugins.push(new TSDocgenPlugin()); // optional
    config.resolve.extensions.push(".ts", ".tsx");
    return config;
};
```

{% hint style="danger" %}
JSX addon does not have types. TypeScript will complain once you add it to a component.
{% endhint %}

To solve this, create a new dile inside your project in the following path **src/storybook.d.ts**:

```
import { RenderFunction } from "@storybook/react";

declare module "@storybook/react" {
    export interface Story {
        addWithJSX(storyName: string, callback: RenderFunction): this;
    }
}
```

## Component example

In our application we have a button component:

```
import { boolean, select, text } from "@storybook/addon-knobs";
import { storiesOf } from "@storybook/react";
import React from "react";
import { EditIcon } from "../icon";
import { APPEARANCE_DEFAULT } from "../lozenge/lozenge.constant";
import { Button } from "./button";
import { BUTTON_APPEARANCE_DEFAULT, BUTTON_APPEARANCE_PRIMARY, BUTTON_APPEARANCE_TRANSPARENT } from "./button.constant";

const appearanceOptions = {
    default: BUTTON_APPEARANCE_DEFAULT,
    transparent: BUTTON_APPEARANCE_TRANSPARENT,
    primary: BUTTON_APPEARANCE_PRIMARY
};

storiesOf("Button", module).addWithJSX("as dynamic variables", () => {
    const placeholder = text("Text", "Lorem");
    const appearance = select("Appearance", appearanceOptions, APPEARANCE_DEFAULT);
    const isDisabled = boolean("Is disabled", false);
    const isLoading = boolean("Is loading", false);
    const hasIcon = boolean("Has icon", true);
    const isIconBefore = boolean("Is icon before", true);
    const icon = hasIcon ? <EditIcon width={18} height={18} /> : undefined;

    const content = (
        <Button
            text={placeholder}
            appearance={appearance}
            icon={icon}
            isIconBefore={isIconBefore}
            isDisabled={isDisabled}
            isLoading={isLoading}
        />
    );
    return <div>{content}</div>;
});
```

We don't need to add a decorator here for knobs because we already set this as default in our config.js file:

```
addDecorator(withKnobs);
```

However, if we want to use the JSX addon, we will need to add a decorator in our story called addWithJSX:

```
storiesOf("Button", module).addWithJSX("story", () => <p>Lorem</p>))
```

## Run Storybook

We can add a command in our package.json:

```
"storybook": "start-storybook -p 9001 -c .storybook"
```

�This will open a new browser tab in the port 9001 with the .storybook configuration.

![](https://2030486006-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LH-6CacFC7RSoDwI6uU%2F-LH4FbHfawfeewuCPaU6%2F-LH4Fk9ifTh1hpVy5LtO%2Fimage.png?alt=media\&token=1b45f2c6-0846-4734-b3f5-7ce66613e27f)

See more about [Storybook CLI](https://storybook.js.org/configurations/cli-options/) commands
