適用於 React Native 的 Storybook
[!IMPORTANT]
此 README 適用於 v8,對於 v7 文件,請參閱 v7.6 文件。
使用適用於 React Native 的 Storybook,您可以在不執行應用程式的情況下設計和開發個別的 React Native 組件。
如果您要從 7.6 遷移到 8.3,您可以在此處找到遷移指南
有關 storybook 的更多資訊,請造訪:storybook.js.org
[!NOTE]
@storybook/react-native
至少需要 8.3.1,如果您安裝其他 storybook 核心套件,它們應該是^8.3.1
或更新版本。
目錄
- 🚀 開始使用
- 📒 撰寫 stories
- 🔌 擴充功能
- 📱 隱藏/顯示 Storybook
- ⚙️ withStorybook 包裝函式
- 🔧 getStorybookUI
- 🧪 在單元測試中使用 stories
- 🤝 貢獻
- ✨ 範例
開始使用
新專案
有一些專案範本,其中 @storybook/react-native
和 @storybook/addon-react-native-web
都已配置,並附帶一個簡單範例。
對於 expo,您可以使用這個 範本,並使用以下命令
# With NPM
npx create-expo-app --template expo-template-storybook AwesomeStorybook
對於 react native cli,您可以使用這個 範本
npx react-native init MyApp --template react-native-template-storybook
現有專案
執行 init 以使用所有依賴項和組態檔設定您的專案
npx storybook@latest init
剩下要做的就是像這樣在您的應用程式進入點(例如 App.tsx
)中返回 Storybook 的 UI
export { default } from './.storybook';
然後,使用 withStorybook 函式包裝您的 metro 配置,如下文所示
如果您想要在 storybook 和您的應用程式之間輕鬆切換,請查看這篇部落格文章
如果您想自己新增所有內容,請查看 此處的手動指南。
其他步驟:更新您的 metro 配置
我們需要 unstable_allowRequireContext 轉換器選項,才能根據 main.ts
中的 stories glob 啟用動態 story 匯入。我們也可以從 metro 配置呼叫 storybook 產生函式,以便在 metro 執行時自動產生 storybook.requires.ts
檔案。
Expo
首先,如果您還沒有,請建立 metro 配置檔案。
npx expo customize metro.config.js
然後,使用 withStorybook 函式包裝您的配置,如下所示。
// metro.config.js
const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');
const withStorybook = require('@storybook/react-native/metro/withStorybook');
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
module.exports = withStorybook(config, {
// Set to false to remove storybook specific options
// you can also use a env variable to set this
enabled: true,
// Path to your storybook config
configPath: path.resolve(__dirname, './.storybook'),
// Optional websockets configuration
// Starts a websocket server on the specified port and host on metro start
// websockets: {
// port: 7007,
// host: 'localhost',
// },
});
React native
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
const withStorybook = require('@storybook/react-native/metro/withStorybook');
const defaultConfig = getDefaultConfig(__dirname);
/**
* Metro configuration
* https://reactnative.dev.org.tw/docs/metro
*
* @type {import('metro-config').MetroConfig}
*/
const config = {};
// set your own config here 👆
const finalConfig = mergeConfig(defaultConfig, config);
module.exports = withStorybook(finalConfig, {
// Set to false to remove storybook specific options
// you can also use a env variable to set this
enabled: true,
// Path to your storybook config
configPath: path.resolve(__dirname, './.storybook'),
// Optional websockets configuration
// Starts a websocket server on the specified port and host on metro start
// websockets: {
// port: 7007,
// host: 'localhost',
// },
});
Reanimated 設定
請確保您的專案中有 react-native-reanimated
,並且您的 babel 配置中已設定外掛程式。
// babel.config.js
plugins: ['react-native-reanimated/plugin'];
撰寫 stories
在 storybook 中,我們使用稱為 CSF 的語法,如下所示
import type { Meta, StoryObj } from '@storybook/react';
import { MyButton } from './Button';
const meta = {
component: MyButton,
} satisfies Meta<typeof MyButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic: Story = {
args: {
text: 'Hello World',
color: 'purple',
},
};
您應該從 .storybook
資料夾中的 main.ts
配置檔案中配置您的 story 檔案的路徑。
// .storybook/main.ts
import { StorybookConfig } from '@storybook/react-native';
const main: StorybookConfig = {
stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
addons: [],
};
export default main;
裝飾器和參數
對於 stories,您可以在預設匯出或特定的 story 上新增裝飾器和參數。
import type { Meta } from '@storybook/react';
import { Button } from './Button';
const meta = {
title: 'Button',
component: Button,
decorators: [
(Story) => (
<View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
<Story />
</View>
),
],
parameters: {
backgrounds: {
values: [
{ name: 'red', value: '#f00' },
{ name: 'green', value: '#0f0' },
{ name: 'blue', value: '#00f' },
],
},
},
} satisfies Meta<typeof Button>;
export default meta;
對於全域裝飾器和參數,您可以將它們新增到 .storybook
資料夾內的 preview.tsx
。
// .storybook/preview.tsx
import type { Preview } from '@storybook/react';
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
const preview: Preview = {
decorators: [
withBackgrounds,
(Story) => (
<View style={{ flex: 1, color: 'blue' }}>
<Story />
</View>
),
],
parameters: {
backgrounds: {
default: 'plain',
values: [
{ name: 'plain', value: 'white' },
{ name: 'warm', value: 'hotpink' },
{ name: 'cool', value: 'deepskyblue' },
],
},
},
};
export default preview;
擴充功能
cli 將為您安裝一些基本擴充功能,例如 controls 和 actions。Ondevice 擴充功能是可以透過您在手機上看到的裝置 UI 呈現的擴充功能。
目前可用的擴充功能為
@storybook/addon-ondevice-controls
:即時調整您的元件 props@storybook/addon-ondevice-actions
:使用會在 actions 標籤中記錄資訊的 actions 模擬 onPress 呼叫@storybook/addon-ondevice-notes
:在您的 stories 中新增一些 markdown,以協助記錄其用法@storybook/addon-ondevice-backgrounds
:變更 storybook 的背景,以比較您的元件在不同背景下的外觀
安裝您想要使用的每個擴充功能,並將它們新增到 main.ts
擴充功能清單,如下所示
// .storybook/main.ts
import { StorybookConfig } from '@storybook/react-native';
const main: StorybookConfig = {
// ... rest of config
addons: [
'@storybook/addon-ondevice-notes',
'@storybook/addon-ondevice-controls',
'@storybook/addon-ondevice-backgrounds',
'@storybook/addon-ondevice-actions',
],
};
export default main;
在您的 story 中使用擴充功能
有關每個 ondevice 擴充功能的詳細資訊,請參閱 README
隱藏/顯示 storybook
React native 上的 Storybook 是一個正常的 React Native 組件,可以根據您自己的邏輯在您的 RN 應用程式中的任何位置使用或隱藏。
您也可以建立一個單獨的應用程式,專門用於 storybook,它也可以作為您的視覺元件的套件。有些人選擇使用 react native 開發人員選單中的自訂選項來切換 storybook 元件。
withStorybook 包裝函式
withStorybook
是一個包裝函式,用於擴充您的 Metro 配置 以用於 Storybook。它接受您現有的 Metro 配置和一個選項物件,用於設定 Storybook 的啟動和配置方式。
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const withStorybook = require('@storybook/react-native/metro/withStorybook');
const defaultConfig = getDefaultConfig(__dirname);
module.exports = withStorybook(defaultConfig, {
enabled: true,
// See API section below for available options
});
選項
enabled
類型:boolean
,預設值:true
決定是否將指定的選項套用至 Metro 配置。這對於同時使用和不使用 Storybook 的專案設定,並且需要有條件地套用選項時非常有用。在此範例中,它是使用環境變數有條件地完成的
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const withStorybook = require('@storybook/react-native/metro/withStorybook');
const defaultConfig = getDefaultConfig(__dirname);
module.exports = withStorybook(defaultConfig, {
enabled: process.env.WITH_STORYBOOK,
// ... other options
});
onDisabledRemoveStorybook
類型:boolean
,預設值:false
如果 onDisabledRemoveStorybook 為 true
且 enabled
為 false
,則 storybook 套件將從建置中移除。如果您想要從生產建置中移除 storybook,這會很有用。
useJs
類型:boolean
,預設值:false
在 JavaScript 中而不是 TypeScript 中產生 .storybook/storybook.requires
檔案。
configPath
類型:string
,預設值:path.resolve(process.cwd(), './.storybook')
您的 Storybook 配置目錄的位置,其中包含 main.ts
和其他專案相關檔案。
websockets
類型:{ host: string?, port: number? }
,預設值:undefined
如果指定,則在啟動時建立 WebSocket 伺服器。這可讓您同步多個裝置以顯示相同的 story 和與 UI 中 story 連接的 arg 值。
websockets.host
類型:string
,預設值:'localhost'
如果指定,則要執行 WebSocket 的主機。
websockets.port
類型:number
,預設值:7007
如果指定,則要執行 WebSocket 的連接埠。
getStorybookUI 選項
您可以將這些參數傳遞到您的 storybook 進入點中的 getStorybookUI 呼叫
{
initialSelection?: string | Object (undefined)
-- initialize storybook with a specific story. eg: `mybutton--largebutton` or `{ kind: 'MyButton', name: 'LargeButton' }`
storage?: Object (undefined)
-- {getItem: (key: string) => Promise<string | null>;setItem: (key: string, value: string) => Promise<void>;}
-- Custom storage to be used instead of AsyncStorage
shouldPersistSelection: Boolean (true)
-- Stores last selected story in your devices storage.
onDeviceUI?: boolean;
-- show the ondevice ui
enableWebsockets?: boolean;
-- enable websockets for the storybook ui
query?: string;
-- query params for the websocket connection
host?: string;
-- host for the websocket connection
port?: number;
-- port for the websocket connection
secured?: boolean;
-- use secured websockets
shouldPersistSelection?: boolean;
-- store the last selected story in the device's storage
theme: Partial<Theme>;
-- theme for the storybook ui
}
在單元測試中使用 stories
Storybook 提供測試公用程式,可讓您在外部測試環境(例如 Jest)中重複使用您的 stories。這樣一來,您可以更輕鬆地編寫單元測試,並重複使用 Storybook 中已完成的設定,但在您的單元測試中。您可以在可攜式 stories 章節中找到更多相關資訊。
貢獻
我們歡迎您為 Storybook 做出貢獻!
正在尋找要解決的第一個問題嗎?
- 當我們認為這些問題非常適合剛接觸程式碼庫或一般 OSS 的人時,我們會使用 Good First Issue 標籤來標記問題。
- 與我們交談,我們會找到適合您的技能和學習興趣的內容。
範例
以下是一些範例專案,可協助您開始使用
- @axeldelafosse 的單一儲存庫設定 https://github.com/axeldelafosse/storybook-rnw-monorepo
- Expo 設定 https://github.com/dannyhw/expo-storybook-starter
- React native cli 設定 https://github.com/dannyhw/react-native-storybook-starter
- 在 RN CLI 專案的本機檔案中新增單獨的進入點和開發人員選單項目:https://github.com/zubko/react-native-storybook-with-dev-menu
- 想要展示您自己的專案嗎?開啟一個 PR 並將它新增到清單中!