659文字
3分
編集

Storybookでサイドバーのラベルにタグを付ける

Storybook で次のような表示が出来るようにする。

#tags からラベルにタグを付ける

各 Story で tags に特定のタグが含まれていればラベルにタグを表示するようにする。

次の例では、deprecatedというタグが含まれていれば、非推奨を表すタグを表示するようにしている。

jsx
// .storybook/manager.jsx
import { addons } from "@storybook/manager-api";
import startCase from "lodash/startCase.js";
import Tag from "../your-components/tag";

addons.setConfig({
  sidebar: {
    renderLabel: (item, api) => {
      // item.typeがrootの場合、apiはundefined
      const data = api?.getCurrentStoryData()
      if (item.depth === 1) {
        return(
            <span>
                {startCase(item.name)}
                {data?.tags.includes("deprecated") && (
                    <Tag variant="warning">Deprecated</Tag>
                )}
            </span>
        );
      }
      return  startCase(item.name)
    }
});

この例では、item.depthを見ることで一階層目にのみタグが付くようにしている。これを行わない場合、tags は継承されていくため、そのラベル以下の階層全てのラベルに同じタグが付いてしまう。

#tags の付与方法

tags は、次のように指定することが出来る。

tsx
// grid.stories.tsx
// ...
export default {
  title: "Layout/Grid",
  component: Grid,
  tags: ["deprecated"],
} satisfies Meta<typeof Grid>;
// ...

#注意点

この方法は、とてもタグと tags が紐づいており、とても素直な実装に思えるかもしれないが、次のことに注意が必要。

  • タグが被ると意図しない副作用が発生する。
    • tags には、暗黙的にdev/test/autodocs/play-fnなど Storybook から様々なタグが自動的に付与される。
    • これらを Storybook やその Addon が見ているため、副作用が起こりえる。
  • タグの取得に利用している api を使うことが想定されていないように思える。
    • api の型が any になっている。
    • ドキュメントにもほぼ言及がない。

#Stories ファイルのタイトル名からタグを付ける

tagsapiの利用が不安であれば、タイトルの文字列をパースするくらいしか方法が残されていないように思う。

次のようにタイトルへタグを付ける。

tsx
// grid.stories.tsx
// ...
export default {
  title: "Layout/Grid#Deprecated",
  component: Grid,
} satisfies Meta<typeof Grid>;
// ...

次のようにしてタイトルをパースして利用する。

jsx
// .storybook/manager.jsx
import { addons } from "@storybook/manager-api";
import startCase from "lodash/startCase.js";
import Tag from "../your-components/tag";

addons.setConfig({
  sidebar: {
    renderLabel: (item) => {
        if (item.depth === 1) {
            if (item.name.endsWith("#Deprecated")){
                const name = item.name.replace("#Deprecated", "");
                return(
                    <span>
                        {startCase(name)}
                        <Tag variant="warning">Deprecated</Tag>
                    </span>
                );
            }
        }
        return  startCase(item.name)
    }
});

#注意点

この方法では、Docs タブに#Deprecatedの文字列が表示されてしまう。 これが許容できなければ、api から生えている set メソッドで更新できると思うが、であれば tags を利用した方がいいだろう。

編集