Storybook で複数 Theme の切り替えを可能にする

2021.09.07

Storybook のアドオン、React Themingを使うと Storybook 上で複数の Theme 切り替えができるようになります。

できたもの

theme-switcher

前提条件

React + Typescript で実装された既存のプロジェクトに Storybook を導入し、Theme 切り替えができるようにします。

  • React(CRA) 17.0.2
  • Typescript 4.1.2
  • emotion 11
  • Storybook 6.3.8

React のプロジェクトをクローン

こちらの既存の React プロジェクトをクローンします。

左のメニューから Zip で落とすことができるので、VSCode で開いてyarnコマンドで依存をダウンロードします。

Storybook

先ほど落としてきたプロジェクトに Storybook を導入します

npx sb init

yarn storybookを実行して以下の画面が立ち上がれば OK です。

Story を追加

クローンしたサンプルに Story を追加します。

不要なファイルを削除

Introduction.stories.mdx以外のファイルは利用しないので削除します。Introductionもよしなに更新してください。

MembersCard.stories.tsx を追加

以下の Story をstoriesへ追加します。

import React from 'react'
import { Story, Meta } from '@storybook/react/types-6-0'

import { MembersCard } from './../components/MembersCard'

export default {
  title: 'pages/MembersCard',
  component: MembersCard,
  parameters: {
    docs: {
      description: {
        component: 'MembersCard',
      },
    },
  },
} as Meta

const Template: Story = (args: any) => (
  <MembersCard {...args}></MembersCard>
)

emotion の Theming を利用しているため、このままだと Theme が正常に読み込まれません。次のステップで.stories/previes.jsReact Theming アドオン を設定します。

React Theming をインストール

## npm
npm i --save-dev @react-theming/storybook-addon
## or yarn
yarn add --dev @react-theming/storybook-addon

emotion-themingも利用するのでインストールしておきます。

yarn add --dev emotion-theming

main.js を更新

main.jsを以下のように更新してアドオンを追加します。

module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/preset-create-react-app",
    "@react-theming/storybook-addon",
  ],
};

preview.js を更新

StorybookのDecorator機能を利用してThemeProviderを設定します。

####preview.js

import React from "react";
import styled from "@emotion/styled";
import {ThemeProvider} from "emotion-theming";
import {addDecorator} from "@storybook/react";
import {withThemes} from "@react-theming/storybook-addon";
import {pink, yellow, sky} from "../src/themes/theme";

addDecorator(withThemes(ThemeProvider, [pink, yellow, sky]));

const PageDefaultStyle = styled.div`
  font-family: Hiragino Sans;
`;

export const decorators = [
  (Story) => (
    <PageDefaultStyle>
      <Story />
    </PageDefaultStyle>
  ),
];

Emotionを利用する場合はemotion-themingを使ってください。@emotion/react等だと正常にThemeが読み込まれません。

themes/theme.ts

切り替えたいThemeの定義です。

export const pink = {
  fonts: {
    mainFont: 'Avenir Next, sans-serif',
    subFont: 'Hira Kaku Pro N, sans-serif',
    textFont: 'Hiragino Sans',
    numberFont: 'Avenir Next, sans-serif',
  },
  colors: {
    mainColor: '#64363C',
    subTextColor: '#8E354A',
    lineColor: '#E6E5E4',
    backgroundColor: '#F8C3CD',
    titleBackgroudColor: '#F8F4F4',
    accentColor: '#EB7A77',
    stageColor: '#CB1B45',
  },
}

export const yellow = {
  fonts: {
    mainFont: 'Times New Roman',
    subFont: 'Garamond',
    textFont: 'Tahoma',
    numberFont: 'Times New Roman',
  },
  colors: {
    mainColor: '#5B622E',
    subTextColor: '#8D742A',
    lineColor: '#E6E5E4',
    backgroundColor: '#DCB879',
    titleBackgroudColor: '#EFBB24',
    accentColor: '#212121',
    stageColor: '#90B44B',
  },
}

export const sky = {
  fonts: {
    mainFont: 'Impact',
    subFont: 'Tahoma, sans-serif',
    textFont: 'Impact',
    numberFont: 'Impact',
  },
  colors: {
    mainColor: '#6c756b',
    subTextColor: '#8D8D8D',
    lineColor: '#E6E5E4',
    backgroundColor: '#F3F4F5',
    titleBackgroudColor: '#96C5F7',
    accentColor: '#212121',
    stageColor: '#4D565D',
  },
}

最後に

本記事ではStorybookとReact Themingを使ってThemeを切り替えた表示をStorybook上で実現する方法をまとめました。サンプルはPageのみにStoryを追加しましたが、Atoms, Molecures別にStoryを追加した場合でも同様にThemeを切り替えてStorybook上でUIを確認できるので複数Themeの要件があるプロジェクトではとても便利です。何かの参考になれれば幸いです。

References