Storybook for React — Globals and Addons and Stories

John Au-Yeung - Jan 24 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Storybook lets us prototype components easily with various parameters.

In this article, we’ll look at how to work with globals with Storybook.

Consuming Globals within a Story

We can consume globals inside a story.

To do that, we get the globals property from the story function.

For example, we can write:

.storybook/preview.js

export const globalTypes = {
  locale: {
    name: 'Locale',
    description: 'locale',
    defaultValue: 'en',
    toolbar: {
      icon: 'globe',
      items: [
        { value: 'en', right: '🇺🇸', title: 'English' },
        { value: 'fr', right: '🇫🇷', title: 'Français' },
        { value: 'zh', right: '🇨🇳', title: '中文' },
      ],
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

src/stories/Button.stories.js

import React from 'react';

import { Button } from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
};

const getCaptionForLocale = (locale) => {
  switch (locale) {
    case 'en': return 'Hello';
    case 'fr': return 'Bonjour';
    case 'zh': return '你好';
    default:
      return 'Hello'
  }
}

const Template = (args, { globals: { locale } }) => <Button {...{ ...args, label: getCaptionForLocale(locale) }} />;
Enter fullscreen mode Exit fullscreen mode

We defined the locale global variable with the .storybook/preview.js file.

Then in the src/stories/Button.stories.js file, we get the locale global property with the globals.locale property of the parameter.

The right property is the text that’s displayed on the right side in the toolbar menu when we connect it to a decorator.

Consuming Globals within an Addon

We can consume globals within an addon file.

For example, we can write:

import React from 'react';
import { useGlobals } from '@storybook/api';
import { addons, types } from '@storybook/addons';
import { AddonPanel, Spaced, Title } from '@storybook/components';

const LocalePanel = props => {
  const [{ locale }] = useGlobals();

  return (
    <>
      <style>
        {`
        #panel-tab-content > div > div[hidden] {
          display: flex !important
        }
      `}
      </style>
      <AddonPanel {...props}>
        <Spaced row={3} outer={1}>
          <Title>{locale}</Title>
        </Spaced>
      </AddonPanel>
    </>
  );
};

const ADDON_ID = 'locale-panel';
const PANEL_ID = `${ADDON_ID}/panel`;

addons.register(ADDON_ID, (api) => {
  addons.add(PANEL_ID, {
    type: types.PANEL,
    title: 'Locale',
    render: ({ active, key }) => {
      return <AddonPanel active={active} key={key}>
        <LocalePanel />
      </AddonPanel>
    },
  });
});
Enter fullscreen mode Exit fullscreen mode

to add an addon and get the global property with the useGlobal hook.

We use the AddonPanel , Spaced , and Title properties to show the content inside the addon panel.

The addons.register method register the addon so that it’ll be shown in the Storybook’s addon panel.

Also, we have the AddonPanel to render the panel.

We make it show when we display the panel with the style tag.

Story Rendering

We can change how our stories are rendered.

To add to the head tag, we add HTML code to the .storybook/preview-head.html file:

<link rel="stylesheet"
    href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
    integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
    crossorigin="anonymous">
Enter fullscreen mode Exit fullscreen mode

Then they’ll be shown in the head tag inside the iframe.

Conclusion

We can add our own addons panel with Storybook.

Also, we can add tags inside the head tag in the iframe.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .