🔨

Storybook の addon 作ってタブパネルを表示する

2022/09/04に公開

はじめに

以下の続き。以降の説明はこの状態からスタートする

https://zenn.dev/sterashima78/articles/0bb466a6036199

タブを表示する

パネルと同様に addon API を使って登録する。

src/addon.tsx
import React from 'react';
import { addons, types } from '@storybook/addons';

- import { AddonPanel } from '@storybook/components';
+ import { AddonPanel, TabWrapper } from '@storybook/components';
import { useParameter } from '@storybook/api';

const ADDON_ID = 'myaddon';
const PANEL_ID = `${ADDON_ID}/panel`;
const TAB_ID = `${ADDON_ID}/tab`;
const PARAM_KEY = 'myAddon';

const MyPanel = () => {
  const value = useParameter<{ data: string } | null>(PARAM_KEY, null);
  const item = value ? value.data : 'No story parameter defined';
  return <div>{item}</div>;
};

addons.register(ADDON_ID, () => {
  addons.add(PANEL_ID, {
    type: types.PANEL,
    title: 'My Addon',
    render: ({ active, key }) => (
      <AddonPanel active={!!active} key={key}>
        <MyPanel />
      </AddonPanel>
    ),
  });
+  addons.add(TAB_ID, {
+    type: types.TAB,
+    title: 'My Addon',
+    route: ({ storyId }) => `/myaddon/${storyId}`,
+    match: ({ viewMode }) => viewMode === "myaddon",
+    render: ({ active, key }) => (
+        <TabWrapper active={!!active} key={key}>
+            <MyPanel />
+        </TabWrapper>
+    ),
+  });
});

パネルの時と異なるのは、routematch のプロパティが増えている。
route はタブクリックされたときのルーティング先で、match は表示条件となる。
渡されるパラメータの型は、いずれも以下となっている。

export interface StoryData {
    viewMode?: string;
    storyId?: string;
    refId?: string;
}

どうやら viewMode はルーティングのパスの第一階層らしい。
つまり、上記の設定は、My Addon という名前のタブがクリックされたら、/myaddon から始まるパスへルーティングし、/myaddon/ のパスにルーティングされたら、render の内容を表示するというものになるようだ。

動かしてみる。

$ npm run build
$ npm run dev

My Addon というタブが表示された。

タブをクリックすると、パネルのときと同様の内容が表示される。

Story に関する情報を表示する

Storybook から提供されている hook を使うことで Storybook の状態を参照したり操作することができる。

https://storybook.js.org/docs/react/addons/addons-api#storybook-hooks

src/addon.tsx
import React from 'react';
import { addons, types } from '@storybook/addons';

- import { AddonPanel, TabWrapper } from '@storybook/components';
+ import { AddonPanel, SyntaxHighlighter, TabWrapper } from '@storybook/components';
- import { useParameter } from '@storybook/api';
+ import { useParameter, useStorybookApi } from '@storybook/api';

const ADDON_ID = 'myaddon';
const PANEL_ID = `${ADDON_ID}/panel`;
const TAB_ID = `${ADDON_ID}/tab`;
const PARAM_KEY = 'myAddon';

const MyPanel = () => {
  const value = useParameter<{ data: string } | null>(PARAM_KEY, null);
  const item = value ? value.data : 'No story parameter defined';
  return <div>{item}</div>;
};

addons.register(ADDON_ID, () => {
  addons.add(PANEL_ID, {
    type: types.PANEL,
    title: 'My Addon',
    render: ({ active, key }) => (
      <AddonPanel active={!!active} key={key}>
        <MyPanel />
      </AddonPanel>
    ),
  });
  addons.add(TAB_ID, {
    type: types.TAB,
    title: 'My Addon',
    route: ({ storyId }) => `/myaddon/${storyId}`,
    match: ({ viewMode }) => {
      return viewMode === "myaddon"
    },
-    render: ({ active, key }) => (
+    render: ({ active, key }) => {
+      const api = useStorybookApi()
+      return (
        <TabWrapper active={!!active} key={key}>
-	  <MyPanel />
+         <SyntaxHighlighter language='json'>{JSON.stringify(api.getCurrentStoryData(), undefined, 2)}</SyntaxHighlighter>
        </TabWrapper>
      )
+    }
  });
});

動かしてみる。

$ npm run build
$ npm run dev

Story のメタデータが表示された。

おわりに

タブに任意のコンテンツが表示することができ、Story のデータを参照する方法がわかった。
ここで参照できるデータは必ずしも CSF に記載した全てでは無いようだった。
例えば、play フィールドを記載してもここには表示されなかった。
また、addon intraction をインストールしなくても、play 関数が実行されていたため、機能のいくつかは、addon ではなく Storybook のコアに深く依存しているのだということがわかった。

引き続きツールバーとデコレータ、ビルド設定などの addon の書き方を整理して、それぞれが連動した何かを書きたい。

また、addon 周りはドキュメントに記載がないものがちょこちょこあるので、公式アドオンの実装を見ながら調べる必要があった。
ただ、古くからあるものはなんで書いているんだろう?というものがあったり、前述のようなコア機能が前提で書いているものがあったりとなかなか理解が難しいと感じることがあった。

ここまでの実装は以下。

https://github.com/sterashima78/test-storybook-addon/tree/tabs

続き

https://zenn.dev/sterashima78/articles/8b612dc325fee7

Discussion