writing-react-native-storybook-stories

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Native Storybook Stories

React Native Storybook 故事编写

Write stories for React Native components using
@storybook/react-native
v10 and Component Story Format (CSF).
使用
@storybook/react-native
v10和Component Story Format (CSF)为React Native组件编写故事。

Quick Start

快速入门

Minimal story file:
tsx
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',
  },
};
最简故事文件:
tsx
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',
  },
};

File Conventions

文件约定

  • Name:
    ComponentName.stories.tsx
    colocated with the component
  • Import
    Meta
    and
    StoryObj
    from
    @storybook/react-native
  • Default export:
    meta
    object with
    satisfies Meta<typeof Component>
  • Named exports: UpperCamelCase story names, typed as
    StoryObj<typeof meta>
  • Use
    args
    for props,
    argTypes
    for control config,
    parameters
    for addon config
  • Use
    render
    for custom render functions,
    decorators
    for wrappers
  • 命名:
    ComponentName.stories.tsx
    ,与组件放在同一目录下
  • @storybook/react-native
    导入
    Meta
    StoryObj
  • 默认导出:
    meta
    对象,需满足
    Meta<typeof Component>
    类型
  • 命名导出:故事名称采用大驼峰式,类型为
    StoryObj<typeof meta>
  • 使用
    args
    传递属性,
    argTypes
    配置控件,
    parameters
    配置插件
  • 使用
    render
    定义自定义渲染函数,
    decorators
    定义包装器

Story Patterns

故事编写模式

Multiple stories with shared args

共享参数的多个故事

tsx
export const Primary: Story = {
  args: { variant: 'primary', title: 'Click me' },
};

export const Secondary: Story = {
  args: { ...Primary.args, variant: 'secondary' },
};
tsx
export const Primary: Story = {
  args: { variant: 'primary', title: 'Click me' },
};

export const Secondary: Story = {
  args: { ...Primary.args, variant: 'secondary' },
};

Custom render function

自定义渲染函数

tsx
export const WithScrollView: Story = {
  render: (args) => (
    <ScrollView>
      <MyComponent {...args} />
    </ScrollView>
  ),
};
tsx
export const WithScrollView: Story = {
  render: (args) => (
    <ScrollView>
      <MyComponent {...args} />
    </ScrollView>
  ),
};

Render with hooks (must be a named function)

结合Hooks渲染(必须是命名函数)

tsx
export const Interactive: Story = {
  render: function InteractiveRender() {
    const [count, setCount] = useReducer((s) => s + 1, 0);
    return <Counter count={count} onPress={setCount} />;
  },
};
tsx
export const Interactive: Story = {
  render: function InteractiveRender() {
    const [count, setCount] = useReducer((s) => s + 1, 0);
    return <Counter count={count} onPress={setCount} />;
  },
};

Actions (mock callbacks)

动作(模拟回调)

tsx
import { fn } from 'storybook/test';

const meta = {
  component: Button,
  args: { onPress: fn() },
} satisfies Meta<typeof Button>;
Or via argTypes:
tsx
argTypes: { onPress: { action: 'pressed' } },
tsx
import { fn } from 'storybook/test';

const meta = {
  component: Button,
  args: { onPress: fn() },
} satisfies Meta<typeof Button>;
或者通过argTypes:
tsx
argTypes: { onPress: { action: 'pressed' } },

Custom story name

自定义故事名称

tsx
export const MyStory: Story = {
  storyName: 'Custom Display Name',
  args: { label: 'Hello' },
};
tsx
export const MyStory: Story = {
  storyName: 'Custom Display Name',
  args: { label: 'Hello' },
};

Custom title / nesting

自定义标题/嵌套结构

tsx
const meta = {
  title: 'NestingExample/Message/Bubble',
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;
tsx
const meta = {
  title: 'NestingExample/Message/Bubble',
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;

Controls & ArgTypes

控件与ArgTypes

For the full control type reference, see references/controls.md.
Common patterns:
tsx
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>;
Auto-detection: TypeScript prop types are automatically mapped to controls (
string
-> text,
boolean
-> boolean, union types -> select,
number
-> number).
完整的控件类型参考请见references/controls.md
常见配置模式:
tsx
const meta = {
  component: MyComponent,
  argTypes: {
    // 下拉选择器
    size: {
      options: ['small', 'medium', 'large'],
      control: { type: 'select' },
    },
    // 范围滑块
    opacity: {
      control: { type: 'range', min: 0, max: 1, step: 0.1 },
    },
    // 颜色选择器
    color: { control: { type: 'color' } },
    // 条件控件(仅当`advanced`参数为true时显示)
    padding: { control: 'number', if: { arg: 'advanced' } },
  },
} satisfies Meta<typeof MyComponent>;
自动检测:TypeScript属性类型会自动映射为控件(
string
→文本框、
boolean
→布尔开关、联合类型→下拉选择器、
number
→数字输入框)。

Parameters

参数

Addon parameters

插件参数

tsx
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' },
    ],
  },
},
tsx
parameters: {
  // 在Notes插件标签页中显示Markdown文档
  notes: `# MyComponent\nUsage: \`<MyComponent label="hi" />\``,
  // Backgrounds插件的背景选项
  backgrounds: {
    default: 'dark',
    values: [
      { name: 'light', value: 'white' },
      { name: 'dark', value: '#333' },
    ],
  },
},

RN-specific UI parameters

React Native专属UI参数

ParameterTypeDescription
noSafeArea
boolean
Remove top safe area padding. When using this, the component itself must handle safe areas since Storybook will no longer provide safe area padding. Prefer
useSafeAreaInsets()
over
SafeAreaView
— apply insets as
paddingTop
/
paddingBottom
on the container, and for scrollable content use
contentContainerStyle
padding instead of wrapping in
SafeAreaView
.
storybookUIVisibility
'visible'
|
'hidden'
Initial UI visibility
hideFullScreenButton
boolean
Hide fullscreen toggle
layout
'padded'
|
'centered'
|
'fullscreen'
Story container layout
Parameters can be set at story, meta (component), or global (preview.tsx) level.
参数名称类型说明
noSafeArea
boolean
移除顶部安全区域内边距。使用此参数时,组件本身必须处理安全区域,因为Storybook将不再提供安全区域内边距。优先使用
useSafeAreaInsets()
而非
SafeAreaView
——将内边距作为容器的
paddingTop
/
paddingBottom
应用,对于可滚动内容,请使用
contentContainerStyle
内边距,而非用
SafeAreaView
包裹。
storybookUIVisibility
'visible'
|
'hidden'
初始UI可见性
hideFullScreenButton
boolean
隐藏全屏切换按钮
layout
'padded'
|
'centered'
|
'fullscreen'
故事容器布局
参数可在故事、元数据(组件)或全局(preview.tsx)级别设置。

Decorators

装饰器

Wrap stories in providers, layouts, or context:
tsx
const meta = {
  component: MyComponent,
  decorators: [
    (Story) => (
      <View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
        <Story />
      </View>
    ),
  ],
} satisfies Meta<typeof MyComponent>;
Global decorators go in
.rnstorybook/preview.tsx
:
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;
用提供者、布局或上下文包裹故事:
tsx
const meta = {
  component: MyComponent,
  decorators: [
    (Story) => (
      <View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
        <Story />
      </View>
    ),
  ],
} satisfies Meta<typeof MyComponent>;
全局装饰器需放在
.rnstorybook/preview.tsx
中:
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;

Configuration

配置

.rnstorybook/main.ts

.rnstorybook/main.ts

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

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

export default main;
Story globs also support the object form for multi-directory setups:
ts
stories: [
  '../components/**/*.stories.?(ts|tsx|js|jsx)',
  { directory: '../other_components', files: '**/*.stories.?(ts|tsx|js|jsx)' },
],
ts
import type { StorybookConfig } from '@storybook/react-native';

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

export default main;
故事路径通配符也支持对象形式,适用于多目录场景:
ts
stories: [
  '../components/**/*.stories.?(ts|tsx|js|jsx)',
  { directory: '../other_components', files: '**/*.stories.?(ts|tsx|js|jsx)' },
],

Portable Stories (Testing)

可移植故事(测试用)

Reuse stories in Jest tests:
tsx
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();
});
For single stories use
composeStory
:
tsx
import { composeStory } from '@storybook/react';
import meta, { Primary } from './Button.stories';

const PrimaryStory = composeStory(Primary, meta);
Setup global annotations for tests in a Jest setup file:
ts
// setup-portable-stories.ts
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from '../.rnstorybook/preview';
setProjectAnnotations(previewAnnotations);
在Jest测试中复用故事:
tsx
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();
});

// 在测试中覆盖参数
test('renders with custom props', () => {
  render(<Primary title="Custom" />);
  expect(screen.getByText('Custom')).toBeTruthy();
});
单个故事可使用
composeStory
tsx
import { composeStory } from '@storybook/react';
import meta, { Primary } from './Button.stories';

const PrimaryStory = composeStory(Primary, meta);
在Jest配置文件中设置全局注解:
ts
// setup-portable-stories.ts
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from '../.rnstorybook/preview';
setProjectAnnotations(previewAnnotations);

Addons Summary

插件汇总

AddonPackagePurpose
Controls
@storybook/addon-ondevice-controls
Edit props interactively
Actions
@storybook/addon-ondevice-actions
Log component interactions
Backgrounds
@storybook/addon-ondevice-backgrounds
Change story backgrounds
Notes
@storybook/addon-ondevice-notes
Add markdown documentation
插件名称包名用途
Controls
@storybook/addon-ondevice-controls
交互式编辑组件属性
Actions
@storybook/addon-ondevice-actions
记录组件交互行为
Backgrounds
@storybook/addon-ondevice-backgrounds
切换故事背景
Notes
@storybook/addon-ondevice-notes
添加Markdown文档说明