mcpField
W
writing-react-native-storybook-stories

writing-react-native-storybook-stories

github.com/storybookjs開発1.3k688 回インストール4 日前に更新
リポジトリ

writing-react-native-storybook-stories について

`@storybook/react-native` v10 と CSF を使い、React Native コンポーネントのストーリーを作成するスキルです。構成、コントロール、デコレーター、パラメーターに加え、Jest テストでの再利用方法も扱います。

主な機能

  1. 01CSFストーリーファイルの規約
  2. 02共有 args とカスタムレンダリング
  3. 03コントロールと argTypes の設定
  4. 04パラメーターとデコレーター
  5. 05Jest でのストーリー再利用

活用シーン

  1. 01コンポーネントのバリエーション記録
  2. 02インタラクティブなプロパティ操作の設定
  3. 03ストーリーへのプロバイダー追加
  4. 04Jest テストでのストーリー再利用

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/storybookjs/react-native --skill writing-react-native-storybook-stories

インストール後、Claude Code で/writing-react-native-storybook-storiesを入力して読み込まれたか確認します。

同梱ファイルreferences/controls.md

詳細

React Native Storybook ストーリー

@storybook/react-native v10 と Component Story Format(CSF)を使用して、React Native コンポーネントのストーリーを作成します。

クイックスタート

最小限のストーリーファイル:

import type { Meta, StoryObj } from '@storybook/react-native';
import { MyComponent } from './MyComponent';

const meta = {
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Basic: Story = {
  args: {
    label: 'Hello',
  },
};

ファイルの規約

  • 名前:コンポーネントと同じ場所に配置する ComponentName.stories.tsx
  • @storybook/react-native から Meta と StoryObj をインポートします
  • デフォルトエクスポート:satisfies Meta<typeof Component> を付けた meta オブジェクト
  • 名前付きエクスポート:StoryObj<typeof meta> 型を付けた UpperCamelCase のストーリー名
  • props には args、コントロール設定には argTypes、アドオン設定には parameters を使用します
  • カスタムレンダー関数には render、ラッパーには decorators を使用します

ストーリーパターン

共通の args を使用した複数のストーリー
export const Primary: Story = {
  args: { variant: 'primary', title: 'Click me' },
};

export const Secondary: Story = {
  args: { ...Primary.args, variant: 'secondary' },
};
カスタムレンダー関数
export const WithScrollView: Story = {
  render: (args) => (
    <ScrollView>
      <MyComponent {...args} />
    </ScrollView>
  ),
};
フックを使用したレンダー(名前付き関数である必要があります)
export const Interactive: Story = {
  render: function InteractiveRender() {
    const [count, setCount] = useReducer((s) => s + 1, 0);
    return <Counter count={count} onPress={setCount} />;
  },
};
アクション(モックコールバック)
import { fn } from 'storybook/test';

const meta = {
  component: Button,
  args: { onPress: fn() },
} satisfies Meta<typeof Button>;

または、argTypes を使用します:

argTypes: { onPress: { action: 'pressed' } },
カスタムストーリー名
export const MyStory: Story = {
  storyName: 'Custom Display Name',
  args: { label: 'Hello' },
};
カスタムタイトル/階層化
const meta = {
  title: 'NestingExample/Message/Bubble',
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;

Controls と ArgTypes

コントロールの型の全リファレンスについては、references/controls.md を参照してください。

一般的なパターン:

const meta = {
  component: MyComponent,
  argTypes: {
    // Select dropdown
    size: {
      options: ['small', 'medium', 'large'],
      control: { type: 'select' },
    },
    // Range slider
    opacity: {
      control: { type: 'range', min: 0, max: 1, step: 0.1 },
    },
    // Color picker
    color: { control: { type: 'color' } },
    // Conditional control (shows only when `advanced` arg is true)
    padding: { control: 'number', if: { arg: 'advanced' } },
  },
} satisfies Meta<typeof MyComponent>;

自動検出:TypeScript の prop 型はコントロールに自動的に割り当てられます(string → text、boolean → boolean、ユニオン型 → select、number → number)。

Parameters

アドオンのパラメーター
parameters: {
  // Markdown docs in the Notes addon tab
  notes: `# MyComponent\nUsage: \`<MyComponent label="hi" />\``,
  // Background options for Backgrounds addon
  backgrounds: {
    default: 'dark',
    values: [
      { name: 'light', value: 'white' },
      { name: 'dark', value: '#333' },
    ],
  },
},
RN 固有の UI パラメーター
パラメーター型説明
noSafeAreaboolean上部のセーフエリアのパディングを削除します。この設定を使用する場合、コンポーネント自体でセーフエリアを処理する必要があります。Storybook によるセーフエリアのパディングが適用されなくなるためです。SafeAreaView よりも useSafeAreaInsets() の使用を推奨します。インセットはコンテナーの paddingTop/paddingBottom に適用し、スクロール可能なコンテンツでは SafeAreaView で囲む代わりに contentContainerStyle のパディングを使用してください。
storybookUIVisibility'visible' | 'hidden'初期 UI の表示状態
hideFullScreenButtonboolean全画面表示の切り替えボタンを非表示にします
layout'padded' | 'centered' | 'fullscreen'ストーリーコンテナーのレイアウト

パラメーターはストーリー、メタ(コンポーネント)、またはグローバル(preview.tsx)レベルで設定できます。

デコレーター

ストーリーをプロバイダー、レイアウト、またはコンテキストでラップします。

const meta = {
  component: MyComponent,
  decorators: [
    (Story) => (
      <View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
        <Story />
      </View>
    ),
  ],
} satisfies Meta<typeof MyComponent>;

グローバルデコレーターは .rnstorybook/preview.tsx に記述します。

import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
import type { Preview } from '@storybook/react-native';

const preview: Preview = {
  decorators: [withBackgrounds],
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
    backgrounds: {
      default: 'plain',
      values: [
        { name: 'plain', value: 'white' },
        { name: 'dark', value: '#333' },
      ],
    },
  },
};

export default preview;

設定

.rnstorybook/main.ts
import type { StorybookConfig } from '@storybook/react-native';

const main: StorybookConfig = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  deviceAddons: [
    '@storybook/addon-ondevice-controls',
    '@storybook/addon-ondevice-backgrounds',
    '@storybook/addon-ondevice-actions',
    '@storybook/addon-ondevice-notes',
  ],
  framework: '@storybook/react-native',
};

export default main;

複数のディレクトリを設定する場合、ストーリーの glob ではオブジェクト形式もサポートされています。

stories: [
  '../components/**/*.stories.?(ts|tsx|js|jsx)',
  { directory: '../other_components', files: '**/*.stories.?(ts|tsx|js|jsx)' },
],

ポータブルストーリー(テスト)

ストーリーを Jest テストで再利用します。

import { render, screen } from '@testing-library/react-native';
import { composeStories } from '@storybook/react';
import * as stories from './Button.stories';

const { Primary, Secondary } = composeStories(stories);

test('renders primary button', () => {
  render(<Primary />);
  expect(screen.getByText('Click me')).toBeTruthy();
});

// Override args in tests
test('renders with custom props', () => {
  render(<Primary title="Custom" />);
  expect(screen.getByText('Custom')).toBeTruthy();
});

個別のストーリーには composeStory を使用します。

import { composeStory } from '@storybook/react';
import meta, { Primary } from './Button.stories';

const PrimaryStory = composeStory(Primary, meta);

Jest のセットアップファイルで、テスト用のグローバルアノテーションを設定します。

// setup-portable-stories.ts
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from '../.rnstorybook/preview';
setProjectAnnotations(previewAnnotations);

アドオンの概要

アドオンパッケージ目的
Controls@storybook/addon-ondevice-controlsプロパティをインタラクティブに編集
Actions@storybook/addon-ondevice-actionsコンポーネントの操作を記録
Backgrounds@storybook/addon-ondevice-backgroundsストーリーの背景を変更
Notes@storybook/addon-ondevice-notesMarkdown ドキュメントを追加

コメント