# 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.

![](/files/-LH4Fk9ifTh1hpVy5LtO)

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kaaerreeene.gitbook.io/front-end-guide/storybook.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
