How to create a custom toolbar addon for Storybook?
Tags: storybook, design, theme, addon
Published: 2022-07-24 09:46:00

Storybook enables faster prototyping of UI elements irrespective of framework. In this article we'll see how to create a toolbar addon for storybook. The example for this topic is toggling theme via a custom toolbar menu addon.

I'm skipping the storybook setup either for new application or an existing application based on any supported framework for time being.

Once you create a new application or added storybook to existing application, run `npm run storybook` to view storybook for your application.

The main files for storybook are `.storybook/main.js` and `.storybook/preview.js`.

Open `.storybook/main.js` and add `"@storybook/addon-toolbars"` to addons array if it is not there. This package enables us to add custom addons to storybook toolbar.

As mentioned earlier our intention is to add a theme selector menu to storybook toolbar as custom addon. For that, open `preview.js` and the below piece of code:

```javascript
// .storybok/preview.js

export const globalTypes = {
  theme: {
    name: 'Toggle theme',
    description: 'Global theme for components',
    defaultValue: 'light',
    toolbar: {
      icon: 'circlehollow',
      items: ['light', 'dark'],
      showName: true,
      dynamicTitle: true
    }
  }
}
```

Once saved, the changes will be reflected on your localhost. And you will see a new toolbar menu with options __light__ and __dark__.

Excellent. We created a new custom menu for storybook toolbar :sunglasses: 

But it is doing nothing when we select any option from the new menu. 

Before going to address this, i want to remind our task at hand. Change the theme for selected UI element. for that let's write a very basic css

```css
// your angular/react application's base.(s)css

:root:not([data-theme='dark']) {
  --bg-color: #fff;
}

[data-theme='dark'] {
  --bg-color: #000;
}

body {
  background-color: var(--bg-color);
}
```

There's nothing fancy in this. We created a css variable `--bg-color` and assigned a value to it based on a data attribute `data-theme`. And atlast we're using that css variable to set body tag background color.

Cool. But where to add that data attribute and how??

The answer for 1st question will showup if we inspect element the rendered UI element. Any UI element in storybook is rendered inside an iframe with id `storybook-preview-iframe`. We need to add our data attribute `data-theme` to the body tag inside that iframe.

Very well. Now it is time to answer the 2nd question **_HOW_**.

For this, let's add a file `useTheme.js` inside `.storybook` folder of the application. And add the below code:

```javascript
// .storybook/useTheme.js

import { useEffect, useGlobals } from '@storybook/addons';

export const useTheme = (StoryFn) => {
  const [{ theme }] = useGlobals();

  useEffect(() => {
    //document.body refers to body tag inside iframe#storybook-preview-iframe
    document.body.setAttribute('data-theme', theme);
  }, [theme]);

  return StoryFn();
};
``` 

That's a mouthful. Let's see what this is doing. Storybook provides an api called `decorators`. We just created a decorator called `useTheme`. Previously in `main.js` we added theme to globalTypes. We can read the selected theme value in our decorator using `useGlobals` of `@storybook/addons`. And in `useEffect` we're setting the selected theme as value to `data-theme` attribute of iframe body tag. 

After creating `useTheme` decorator we need to use this inside `preview.js` as:

```javascript
// .storybook/preview.js
import '<path-to-your-project-theme-or-base-css>';
import {useTheme} from './useTheme';

export const globalTypes = {
  theme: {
    name: 'Toggle theme',
    description: 'Global theme for components',
    defaultValue: 'light',
    toolbar: {
      icon: 'circlehollow',
      items: ['light', 'dark'],
      showName: true,
      dynamicTitle: true
    }
  }
}

export const decorators = [useTheme];
```

Viola. whenever we select a theme from toolbar menu, the iframe body background color will change.

And that's how we created a custom toolbar addon to change the theme. You can create few more css variables to change the appearance of UI elements based on selected theme. 

That's the end of this article. Please post your ideas on toolbar addons in comments section below.

Thanks,
Kiran :wave:

PS: here is the link for [example react repo](https://github.com/KiranMantha/react-storybook-with-custom-toolbar-addon) and for [example angular repo](https://github.com/KiranMantha/angular-storybook-with-custom-toolbar-addon)