DEV Community

Kiran Mantha
Kiran Mantha

Posted on • Edited on

How to create a custom toolbar addon for Storybook?

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:

// .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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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 😎

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

// 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);
}
Enter fullscreen mode Exit fullscreen mode

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:

// .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();
};
Enter fullscreen mode Exit fullscreen mode

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:

// .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];
Enter fullscreen mode Exit fullscreen mode

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 👋

PS: here is the link for example react repo and for example angular repo

Top comments (3)

Collapse
 
joanllenas profile image
Joan Llenas Masó

I had to disable the backgrounds addon to make it work:

.storybook/main.js

module.exports = {
  addons: [
    // (...)
    {
      name: '@storybook/addon-essentials',
      options: {
        backgrounds: false,
      }
    }
    // (...)
  ]
};
Enter fullscreen mode Exit fullscreen mode

Thanks for sharing!

Collapse
 
adammescher profile image
Adam Mescher

Exactly what I needed.

Thanks for sharing with the world and letting me use your hard work to maintain momentum in a project.

Collapse
 
kiranmantha profile image
Kiran Mantha

My humble thanks for your kind words and very happy that this article helped you for what you need 👍