コンテンツへスキップ
Fumadocs

ストーリー

コントロール付きでコンポーネントを表示します。

Callout

string
string

はじめに

Fumadocs Story は、コンポーネントを表示し、ドキュメント化するためのツールです。 Storybook に代わる、ドキュメントに特化したシンプルなツールで、主にコンポーネントライブラリ向けに設計されています。

ストーリーという概念を初めて知る方のために、Storybook のドキュメントから簡単な説明を引用します。

ストーリーは、UI コンポーネントのレンダリングされた状態を捉えたものです。アノテーションを持つオブジェクトであり、 一連の引数が与えられたときのコンポーネントの振る舞いと外観を記述します。

Storybook ではなくこちらを使うべきなのはどんなとき?

Fumadocs Story は Storybook を置き換えるものではありません。本格的な UI テストには、引き続き Storybook をおすすめします。

インストール

npm i @fumadocs/story

Tailwind CSS のプリセットを追加します。

Tailwind CSS
@import '@fumadocs/story/css/preset.css';

Vite プラグインを追加します。

waku.config.ts
import { defineConfig } from 'waku/config';
import story from '@fumadocs/story/vite';

export default defineConfig({
  vite: {
    plugins: [story()],
  },
});

ストーリーを作成する

グローバル設定を保持するストーリーファクトリーを作成します。

lib/story.ts
import { defineStoryFactory } from '@fumadocs/story/vite/client';

export const { defineStory } = defineStoryFactory();

次に、最初のストーリーを作成します。

import { defineStory } from '@/lib/story';
import { MyComponent } from './my-component';

export const story = defineStory({
  // 渡すコンポーネントはクライアントコンポーネントである必要があります
  Component: MyComponent,
  args: {
    // デフォルトの props(推奨)
    initial: {},
  },
});

.story サフィックスは必須です

プラグインが変換するのは *.story.{js,jsx,ts,tsx} に一致するファイルのみです。この条件はVite プラグインの filter オプションで変更できます。

ストーリーをレンダリングする

MDX ファイル内でストーリーをレンダリングします。

index.mdx
import { story } from '@/components/my-component.story';

<story.WithControl />

設定

バリアント

args を使うと、コンポーネントのバリアントを複数指定できます。

import { defineStory } from '@/lib/story';
import { Callout } from '@/components/callout';

export const story = defineStory({
  Component: Callout,
  args: [
    {
      variant: 'Default',
      initial: {
        title: 'This is a Callout',
      },
    },
    {
      variant: 'Warning',
      initial: {
        title: 'This is a Callout',
      },
      // props の固定値
      fixed: {
        type: 'warning',
      },
    },
  ],
});

controls

コントロールはコンポーネントのpropsから自動生成されます。これらを置き換えるには、コントロールノードを指定します。

import { defineStory } from '@/lib/story';
import { GraphView } from '@/components/graph-view';

export const story = defineStory({
  Component: GraphView,
  args: {
    controls: {
      node: {
        type: 'object',
        properties: [],
      },
    },
  },
});

frame

レイアウトのようにビューポート全体を占めるコンポーネントは、iframe 内でレンダリングできます。 ストーリーパネルではフレームがパネルに収まるよう縮小して表示され、フルスクリーンで開くためのボタンも用意されています。

components/my-layout.story.tsx
import { defineStory } from '@/lib/story';
import { MyLayout } from './my-layout.lazy';
import { Window } from './window';

export const story = defineStory({
  Component: MyLayout,
  frame: {
    src: '/story/my-layout',
    // ビューポートサイズ(省略可)
    width: 1280,
    height: 800,
    // フレームをウィンドウなどでラップする(省略可)
    Container: Window,
  },
});

コンポーネントをレンダリングするページを src に作成します。このページはストーリーパネルから引数を受け取ります。

src/pages/story/my-layout.tsx
import { story } from '@/components/my-layout.story';

export default function Page() {
  return <story.Frame />;
}

export const getConfig = async () => ({ render: 'static' }) as const;

ストーリーは、それをレンダリングするすべてのページでコンポーネントを読み込みます。遅延読み込みのコンポーネントを渡せば、フレームでのみ読み込まれるようになります。

components/my-layout.lazy.tsx
'use client';
import { lazy } from 'react';

export const MyLayout = lazy(() =>
  import('./my-layout').then((mod) => ({ default: mod.MyLayout })),
);

補足

  • ページはドキュメントと同じオリジン上に配置する必要があります。
  • 引数は postMessage() で送信されるため、シリアライズ可能な値である必要があります (関数や JSX は使用できません) 。
  • Container はクライアントコンポーネントである必要があります。

国際化

UI レベルで国際化を設定済みの場合:

layout.shared.tsx
import { defineI18n } from 'fumadocs-core/i18n';
import { uiTranslations } from 'fumadocs-ui/i18n';
import { storyTranslations } from '@fumadocs/story/i18n';

const i18n = defineI18n({
  languages: ['en', 'cn'],
  defaultLanguage: 'en',
});

export const translations = i18n
  .translations()
  .extend(uiTranslations())
  .extend(storyTranslations())
  .add({
    cn: {
      displayName: 'Chinese',
      'Add Item(story arguments form)': '新增項目',
    },
  });

詳しくは翻訳を参照してください。

How is this guide?

最終更新日