writing-react-native-storybook-stories について
`@storybook/react-native` v10 と CSF を使い、React Native コンポーネントのストーリーを作成するスキルです。構成、コントロール、デコレーター、パラメーターに加え、Jest テストでの再利用方法も扱います。
主な機能
- 01CSFストーリーファイルの規約
- 02共有 args とカスタムレンダリング
- 03コントロールと argTypes の設定
- 04パラメーターとデコレーター
- 05Jest でのストーリー再利用
活用シーン
- 01コンポーネントのバリエーション記録
- 02インタラクティブなプロパティ操作の設定
- 03ストーリーへのプロバイダー追加
- 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 パラメーター
| パラメーター | 型 | 説明 |
|---|---|---|
noSafeArea | boolean | 上部のセーフエリアのパディングを削除します。この設定を使用する場合、コンポーネント自体でセーフエリアを処理する必要があります。Storybook によるセーフエリアのパディングが適用されなくなるためです。SafeAreaView よりも useSafeAreaInsets() の使用を推奨します。インセットはコンテナーの paddingTop/paddingBottom に適用し、スクロール可能なコンテンツでは SafeAreaView で囲む代わりに contentContainerStyle のパディングを使用してください。 |
storybookUIVisibility | 'visible' | 'hidden' | 初期 UI の表示状態 |
hideFullScreenButton | boolean | 全画面表示の切り替えボタンを非表示にします |
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-notes | Markdown ドキュメントを追加 |