Addon API
Storybook 的 API 允許開發人員以程式設計方式與 Storybook 互動。透過 API,開發人員可以建置和部署自訂附加元件及其他工具,以增強 Storybook 的功能。
核心附加元件 API
我們的 API 透過兩個不同的套件公開,每個套件都有不同的用途
@storybook/manager-api
用於與 Storybook 管理器 UI 互動或存取 Storybook API。@storybook/preview-api
用於控制和設定附加元件的行為。
import { addons } from '@storybook/preview-api';
import { useStorybookApi } from '@storybook/manager-api';
addons.add()
add
方法可讓您註冊與附加元件關聯的 UI 元件類型(例如,面板、工具列、標籤頁)。對於最低可行的 Storybook 附加元件,您應提供以下引數
type
:要註冊的 UI 元件類型。title
:要在附加元件面板中顯示的標題。render
:呈現附加元件 UI 元件的函數。
import React from 'react';
import { addons, types } from '@storybook/manager-api';
import { AddonPanel } from '@storybook/components';
const ADDON_ID = 'myaddon';
const PANEL_ID = `${ADDON_ID}/panel`;
addons.register(ADDON_ID, (api) => {
addons.add(PANEL_ID, {
type: types.PANEL,
title: 'My Addon',
render: ({ active }) => (
<AddonPanel active={active}>
<div> Storybook addon panel </div>
</AddonPanel>
),
});
});
呼叫 render 函數時會帶有 active
。active
值在面板於 UI 上聚焦時將為 true。
addons.register()
作為所有附加元件的進入點。它可讓您註冊附加元件並存取 Storybook API。例如
import { addons } from '@storybook/preview-api';
// Register the addon with a unique name.
addons.register('my-organisation/my-addon', (api) => {});
現在您將取得我們 StorybookAPI 的執行個體。請參閱 api 文件,以瞭解有關使用該執行個體的資訊。
addons.getChannel()
取得通道的執行個體,以與管理器和預覽畫面進行通訊。您可以在附加元件註冊程式碼和附加元件的包裝函式元件(在 story 中使用時)中找到此執行個體。
它具有 NodeJS EventEmitter 相容 API。因此,您可以使用它來發出事件和監聽事件。
import React, { useCallback } from 'react';
import { FORCE_RE_RENDER } from '@storybook/core-events';
import { addons } from '@storybook/preview-api';
import { useGlobals } from '@storybook/manager-api';
import { IconButton } from '@storybook/components';
import { OutlineIcon } from '@storybook/icons';
const ExampleToolbar = () => {
const [globals, updateGlobals] = useGlobals();
const isActive = globals['my-param-key'] || false;
// Function that will update the global value and trigger a UI refresh.
const refreshAndUpdateGlobal = () => {
updateGlobals({
['my-param-key']: !isActive,
}),
// Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh
addons.getChannel().emit(FORCE_RE_RENDER);
};
const toggleToolbarAddon = useCallback(() => refreshAndUpdateGlobal(), [isActive]);
return (
<IconButton
key="Example"
active={isActive}
title="Show the toolbar addon"
onClick={toggleToolbarAddon}
>
<OutlineIcon />
</IconButton>
);
};
makeDecorator
使用 makeDecorator
API 以官方附加元件的樣式建立裝飾器。如下所示
import { makeDecorator } from '@storybook/preview-api';
export const withAddonDecorator = makeDecorator({
name: 'withSomething',
parameterName: 'CustomParameter',
skipIfNoParametersOrOptions: true
wrapper: (getStory, context, { parameters }) => {
/*
* Write your custom logic here based on the parameters passed in Storybook's stories.
* Although not advised, you can also alter the story output based on the parameters.
*/
return getStory(context);
},
});
如果 story 的參數包含 { exampleParameter: { disable: true } }
(其中 exampleParameter
是附加元件的 parameterName
),則不會呼叫您的裝飾器。
makeDecorator
API 需要以下引數
name
:用於識別自訂附加元件裝飾器的唯一名稱。parameterName
:設定要由附加元件取用的唯一參數。skipIfNoParametersOrOptions
:(選用)如果使用者沒有透過裝飾器或參數設定選項,則不會執行裝飾器。wrapper
:您的裝飾器函數。採用getStory
、context
,以及options
和parameters
(如上方skipIfNoParametersOrOptions
中所定義)。
Storybook API
Storybook 的 API 可讓您存取 Storybook UI 的不同功能。
api.selectStory()
selectStory
API 方法可讓您選取單一 story。它接受以下兩個參數:story 種類名稱和選用的 story 名稱。例如
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
/* 👇 The title prop is optional.
* See https://storybook.dev.org.tw/docs/configure/#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Button',
component: Button,
//👇 Creates specific parameters for the story
parameters: {
myAddon: {
data: 'This data is passed to the addon',
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.dev.org.tw/docs/api/csf
* to learn how to use render functions.
*/
export const Basic: Story = {
render: () => <Button>Hello</Button>,
};
以下是如何選取上述 story
addons.register('my-organisation/my-addon', (api) => {
api.selectStory('Button', 'Default');
});
api.selectInCurrentKind()
與 selectStory
API 方法類似,但它僅接受 story 作為唯一參數。
addons.register('my-organisation/my-addon', (api) => {
api.selectInCurrentKind('Default');
});
api.setQueryParams()
此方法可讓您設定查詢字串參數。您可以將其用作附加元件的暫時儲存空間。以下說明如何定義查詢參數
addons.register('my-organisation/my-addon', (api) => {
api.setQueryParams({
exampleParameter: 'Sets the example parameter value',
anotherParameter: 'Sets the another parameter value',
});
});
此外,如果您需要移除查詢參數,請將其設定為 null
,而不是從附加元件中移除它們。例如
addons.register('my-organisation/my-addon', (api) => {
api.setQueryParams({
exampleParameter: null,
});
});
api.getQueryParam()
允許擷取透過 setQueryParams
API 方法啟用的查詢參數。例如
addons.register('my-organisation/my-addon', (api) => {
api.getQueryParam('exampleParameter');
});
api.getUrlState(overrideParams)
此方法可讓您取得應用程式 URL 狀態,包括任何覆寫或自訂參數值。例如
addons.register('my-organisation/my-addon', (api) => {
const href = api.getUrlState({
selectedKind: 'kind',
selectedStory: 'story',
}).url;
});
api.on(eventName, fn)
此方法可讓您註冊處理常式函數,每當使用者在 story 之間導覽時呼叫該函數。
addons.register('my-organisation/my-addon', (api) => {
// Logs the event data to the browser console whenever the event is emitted.
api.on('custom-addon-event', (eventData) => console.log(eventData));
});
addons.setConfig(config)
此方法可讓您覆寫預設 Storybook UI 設定(例如,設定主題或隱藏 UI 元素)
import { addons } from '@storybook/manager-api';
addons.setConfig({
navSize: 300,
bottomPanelHeight: 300,
rightPanelWidth: 300,
panelPosition: 'bottom',
enableShortcuts: true,
showToolbar: true,
theme: undefined,
selectedPanel: undefined,
initialActive: 'sidebar',
sidebar: {
showRoots: false,
collapsedRoots: ['other'],
},
toolbar: {
title: { hidden: false },
zoom: { hidden: false },
eject: { hidden: false },
copy: { hidden: false },
fullscreen: { hidden: false },
},
});
下表詳細說明如何使用 API 值
名稱 | 類型 | 描述 | 範例值 |
---|---|---|---|
navSize | 數字 (像素) | 顯示 story 清單的側邊欄大小 | 300 |
bottomPanelHeight | 數字 (像素) | 在底部位置時,附加元件面板的大小 | 200 |
rightPanelWidth | 數字 (像素) | 在右側位置時,附加元件面板的大小 | 200 |
panelPosition | 字串 | 在何處顯示附加元件面板 | 'bottom' 或 'right' |
enableShortcuts | 布林值 | 啟用/停用快速鍵 | true |
showToolbar | 布林值 | 顯示/隱藏工具列 | true |
theme | 物件 | Storybook 主題,請參閱下一節 | undefined |
selectedPanel | 字串 | 選取附加元件面板的 ID | storybook/actions/panel |
initialActive | 字串 | 選取行動裝置上的預設活動標籤頁 | sidebar 或 canvas 或 addons |
sidebar | 物件 | 側邊欄選項,請參閱下方 | { showRoots: false } |
toolbar | 物件 | 使用附加元件 ID 修改工具列中的工具 | { fullscreen: { hidden: false } } |
sidebar
命名空間下可設定下列選項
名稱 | 類型 | 描述 | 範例值 |
---|---|---|---|
showRoots | 布林值 | 在側邊欄中將最上層節點顯示為「根」 | false |
collapsedRoots | 陣列 | 預設視覺化摺疊的根節點 ID 集 | ['misc', 'other'] |
renderLabel | 函數 | 為樹狀結構節點建立自訂標籤;必須傳回 ReactNode | (item, api) => <abbr title="...">{item.name}</abbr> |
toolbar
命名空間下可設定下列選項
名稱 | 類型 | 描述 | 範例值 |
---|---|---|---|
id | 字串 | 切換工具列項目的可見性 | { hidden: false } |
Storybook Hooks
為了協助簡化附加元件開發並減少重複程式碼,API 公開了一組 Hook,以存取 Storybook 的內部元件。這些 Hook 是 @storybook/manager-api
套件的擴充功能。
useStorybookState
它允許存取 Storybook 的內部狀態。與 useglobals
Hook 類似,我們建議您最佳化附加元件,使其依賴 React.memo
或以下 Hook:useMemo
、useCallback
,以防止大量重新渲染循環。
import React from 'react';
import { AddonPanel } from '@storybook/components';
import { useStorybookState } from '@storybook/manager-api';
export const Panel = () => {
const state = useStorybookState();
return (
<AddonPanel {...props}>
{state.viewMode !== 'docs' ? (
<h2>Do something with the documentation</h2>
) : (
<h2>Show the panel when viewing the story</h2>
)}
</AddonPanel>
);
};
useStorybookApi
useStorybookApi
Hook 是一個方便的輔助程式,可讓您完全存取 Storybook API 方法。
import React, { useEffect, useCallback } from 'react';
import { useStorybookApi } from '@storybook/manager-api';
import { IconButton } from '@storybook/components';
import { ChevronDownIcon } from '@storybook/icons';
export const Panel = () => {
const api = useStorybookApi();
const toggleMyTool = useCallback(() => {
// Custom logic to toggle the addon here
}, []);
useEffect(() => {
api.setAddonShortcut('custom-toolbar-addon', {
label: 'Enable toolbar addon',
defaultShortcut: ['G'],
actionName: 'Toggle',
showInMenu: false,
action: toggleAddon,
});
}, [api]);
return (
<IconButton key="custom-toolbar" active="true" title="Show a toolbar addon">
<ChevronDownIcon />
</IconButton>
);
};
useChannel
允許設定事件的訂閱,並取得發射器以向通道發射自訂事件。
訊息可以在 iframe 和管理器上監聽。
import React from 'react';
import { AddonPanel, Button } from '@storybook/components';
import { STORY_CHANGED } from '@storybook/core-events';
import { useChannel } from '@storybook/manager-api';
export const Panel = () => {
// Creates a Storybook API channel and subscribes to the STORY_CHANGED event
const emit = useChannel({
STORY_CHANGED: (...args) => console.log(...args),
});
return (
<AddonPanel key="custom-panel" active="true">
<Button onClick={() => emit('my-event-type', { sampleData: 'example' })}>
Emit a Storybook API event with custom data
</Button>
</AddonPanel>
);
};
useAddonState
useAddonState
是一個有用的 Hook,適用於需要資料持久性的附加元件,無論是由於 Storybook 的 UI 生命週期,還是用於涉及多種類型(例如,工具列、面板)的更複雜附加元件。
import React from 'react';
import { useAddonState } from '@storybook/manager-api';
import { AddonPanel, IconButton } from '@storybook/components';
import { LightningIcon } from '@storybook/icons';
export const Panel = () => {
const [state, setState] = useAddonState('addon-unique-identifier', 'initial state');
return (
<AddonPanel key="custom-panel" active="true">
<Button onClick={() => setState('Example')}>
Click to update Storybook's internal state
</Button>
</AddonPanel>
);
};
export const Tool = () => {
const [state, setState] = useAddonState('addon-unique-identifier', 'initial state');
return (
<IconButton
key="custom-toolbar"
active="true"
title="Enable my addon"
onClick={() => setState('Example')}
>
<LightningIcon />
</IconButton>
);
};
useParameter
useParameter
擷取目前 story 的參數。如果未定義參數的值,則會自動預設為定義的第二個值。
import React from 'react';
import { AddonPanel } from '@storybook/components';
import { useParameter } from '@storybook/manager-api';
export const Panel = () => {
// Connects to Storybook's API and retrieves the value of the custom parameter for the current story
const value = useParameter('custom-parameter', 'initial value');
return (
<AddonPanel key="custom-panel" active="true">
{value === 'initial value' ? (
<h2>The story doesn't contain custom parameters. Defaulting to the initial value.</h2>
) : (
<h2>You've set {value} as the parameter.</h2>
)}
</AddonPanel>
);
};
useGlobals
對於依賴 Storybook全域變數的附加元件來說,這是非常有用的 Hook。它可讓您取得和更新 global
值。我們也建議您最佳化附加元件,使其依賴 React.memo
或以下 Hook:useMemo
、useCallback
,以防止大量重新渲染循環。
import React from 'react';
import { AddonPanel, Button } from '@storybook/components';
import { useGlobals } from '@storybook/manager-api';
export const Panel = () => {
const [globals, updateGlobals] = useGlobals();
const isActive = globals['my-param-key'] || false; // 👈 Sets visibility based on the global value.
return (
<AddonPanel key="custom-panel" active={isActive}>
<Button onClick={() => updateGlobals({ ['my-param-key']: !isActive })}>
{isActive ? 'Hide the addon panel' : 'Show the panel'}
</Button>
</AddonPanel>
);
};
useArgs
Hook,可讓您擷取或更新 story 的 args
。
import { useArgs } from '@storybook/manager-api';
const [args, updateArgs, resetArgs] = useArgs();
// To update one or more args:
updateArgs({ key: 'value' });
// To reset one (or more) args:
resetArgs((argNames: ['key']));
// To reset all args
resetArgs();
深入瞭解 Storybook 附加元件生態系統