Storybook の addon 作ってタブパネルを表示する
はじめに
以下の続き。以降の説明はこの状態からスタートする
タブを表示する
パネルと同様に addon
API を使って登録する。
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>
+ ),
+ });
});
パネルの時と異なるのは、route
と match
のプロパティが増えている。
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 の状態を参照したり操作することができる。
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 周りはドキュメントに記載がないものがちょこちょこあるので、公式アドオンの実装を見ながら調べる必要があった。
ただ、古くからあるものはなんで書いているんだろう?というものがあったり、前述のようなコア機能が前提で書いているものがあったりとなかなか理解が難しいと感じることがあった。
ここまでの実装は以下。
続き
Discussion