Docs
Storybook Docs

插件 API

Storybook 的 API 允许开发者以编程方式与 Storybook 交互。使用 API,开发者可以构建和部署自定义插件和其他工具来增强 Storybook 的功能。

¥Storybook's API allows developers to interact programmatically with Storybook. With the API, developers can build and deploy custom addons and other tools that enhance Storybook's functionality.

核心插件 API

¥Core Addon API

我们的 API 通过两个不同的包公开,每个包都有不同的用途:

¥Our API is exposed via two distinct packages, each one with a different purpose:

  • @storybook/manager-api 用于与 Storybook 管理器 UI 交互或访问 Storybook API。

    ¥@storybook/manager-api used to interact with the Storybook manager UI or access the Storybook API.

  • @storybook/preview-api 用于控制和配置插件的行为。

    ¥@storybook/preview-api used to control and configure the addon's behavior.

my-addon/src/manager.js|ts
import { addons } from '@storybook/preview-api';
 
import { useStorybookApi } from '@storybook/manager-api';

addons.add()

add 方法允许你注册与插件关联的 UI 组件的类型(例如面板、工具栏、选项卡)。对于最小可行的 Storybook 插件,你应该提供以下参数:

¥The add method allows you to register the type of UI component associated with the addon (e.g., panels, toolbars, tabs). For a minimum viable Storybook addon, you should provide the following arguments:

  • type:要注册的 UI 组件类型。

    ¥type: The type of UI component to register.

  • title:要在插件面板中显示的标题。

    ¥title: The title to feature in the Addon Panel.

  • render:渲染插件 UI 组件的函数。

    ¥render: The function that renders the addon's UI component.

my-addon/src/manager.js|ts
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>
    ),
  });
});

使用 active 调用渲染函数。当面板聚焦于 UI 时,active 值将为 true。

¥The render function is called with active. The active value will be true when the panel is focused on the UI.

addons.register()

用作所有插件的入口点。它允许你注册插件并访问 Storybook API。例如:

¥Serves as the entry point for all addons. It allows you to register an addon and access the Storybook API. For example:

my-addon/src/manager.js|ts
import { addons } from '@storybook/preview-api';
 
// Register the addon with a unique name.
addons.register('my-organisation/my-addon', (api) => {});

现在你将获得我们的 StorybookAPI 的一个实例。有关如何使用 Storybook API,请参阅 api 文档

¥Now you'll get an instance to our StorybookAPI. See the api docs for Storybook API regarding using that.

addons.getChannel()

获取一个实例到通道,与管理器和预览进行通信。你可以在插件注册代码和插件的封装器组件(在故事中使用)中找到它。

¥Get an instance to the channel to communicate with the manager and the preview. You can find this in both the addon register code and your addon’s wrapper component (where used inside a story).

它具有与 NodeJS EventEmitter 兼容的 API。因此,你可以使用它来触发事件并监听事件。

¥It has a NodeJS EventEmitter compatible API. So, you can use it to emit events and listen to events.

my-addon/src/manager.js|ts
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 创建官方插件风格的装饰器。像这样:

¥Use the makeDecorator API to create decorators in the style of the official addons. Like so:

my-addon/src/decorator.js|ts
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);
  },
});

如果故事的参数包含 { exampleParameter: { disable: true } }(其中 exampleParameter 是你的插件的 parameterName),则不会调用你的装饰器。

¥If the story's parameters include { exampleParameter: { disable: true } } (where exampleParameter is the parameterName of your addon), your decorator will not be called.

makeDecorator API 需要以下参数:

¥The makeDecorator API requires the following arguments:

  • name:用于标识自定义插件装饰器的唯一名称。

    ¥name: Unique name to identify the custom addon decorator.

  • parameterName:设置插件要使用的唯一参数。

    ¥parameterName: Sets a unique parameter to be consumed by the addon.

  • skipIfNoParametersOrOptions:(可选)如果用户没有通过 decorators参数 的选项,则不会运行装饰器。

    ¥skipIfNoParametersOrOptions: (Optional) Doesn't run the decorator if the user hasn't options either via decorators or parameters.

  • wrapper:你的装饰器函数。采用 getStorycontext 以及 optionsparameters(如上文 skipIfNoParametersOrOptions 中所定义)。

    ¥wrapper: your decorator function. Takes the getStory, context, and both the options and parameters (as defined in skipIfNoParametersOrOptions above).


Storybook API

Storybook 的 API 允许你访问 Storybook UI 的不同功能。

¥Storybook's API allows you to access different functionalities of Storybook UI.

api.selectStory()

selectStory API 方法允许你选择一个故事。它接受以下两个参数;故事类型名称和可选故事名称。例如:

¥The selectStory API method allows you to select a single story. It accepts the following two parameters; story kind name and an optional story name. For example:

Button.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  /* 👇 The title prop is optional.
   * See https://storybook.js.org/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.js.org/docs/api/csf
 * to learn how to use render functions.
 */
export const Basic: Story = {
  render: () => <Button>Hello</Button>,
};

这是你可以选择上述故事的方式:

¥This is how you can select the above story:

my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
  api.selectStory('Button', 'Default');
});

api.selectInCurrentKind()

selectStory API 方法类似,但它只接受故事作为唯一参数。

¥Similar to the selectStory API method, but it only accepts the story as the only parameter.

my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
  api.selectInCurrentKind('Default');
});

api.setQueryParams()

此方法允许你设置查询字符串参数。你可以将其用作附加组件的临时存储。以下是定义查询参数的方法:

¥This method allows you to set query string parameters. You can use that as temporary storage for addons. Here's how you define query params:

my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
  api.setQueryParams({
    exampleParameter: 'Sets the example parameter value',
    anotherParameter: 'Sets the another parameter value',
  });
});

此外,如果你需要删除查询参数,请将其设置为 null,而不是从插件中删除它们。例如:

¥Additionally, if you need to remove a query parameter, set it as null instead of removing them from the addon. For example:

my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
  api.setQueryParams({
    exampleParameter: null,
  });
});

api.getQueryParam()

允许检索通过 setQueryParams API 方法启用的查询参数。例如:

¥Allows retrieval of a query parameter enabled via the setQueryParams API method. For example:

my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
  api.getQueryParam('exampleParameter');
});

api.getUrlState(overrideParams)

此方法允许你获取应用 URL 状态,包括任何覆盖或自定义参数值。例如:

¥This method allows you to get the application URL state, including any overridden or custom parameter values. For example:

my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
  const href = api.getUrlState({
    selectedKind: 'kind',
    selectedStory: 'story',
  }).url;
});

api.on(eventName, fn)

此方法允许你注册一个处理程序函数,每当用户在故事之间导航时都会调用该函数。

¥This method allows you to register a handler function called whenever the user navigates between stories.

my-addon/src/manager.js|ts
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 配置(例如,设置 theme 或隐藏 UI 元素):

¥This method allows you to override the default Storybook UI configuration (e.g., set up a theme or hide UI elements):

.storybook/manager.js
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 值:

¥The following table details how to use the API values:

名称类型描述示例值
navSize数字(像素)显示故事列表的侧边栏的大小300
bottomPanelHeight数字(像素)插件面板处于底部位置时的大小200
rightPanelWidth数字(像素)插件面板处于正确位置时的大小200
panelPosition字符串在哪里显示插件面板'bottom''right'
enableShortcuts布尔值启用/禁用快捷方式true
showToolbar布尔值显示/隐藏工具栏true
theme对象Storybook 主题,请参阅下一节undefined
selectedPanel字符串用于选择插件面板的 IDstorybook/actions/panel
initialActive字符串选择移动设备上的默认活动选项卡sidebarcanvasaddons
sidebar对象侧边栏选项,见下文{ showRoots: false }
toolbar对象使用插件 ID 修改工具栏中的工具{ fullscreen: { hidden: false } }

以下选项可在 sidebar 命名空间下配置:

¥The following options are configurable under the sidebar namespace:

名称类型描述示例值
showRoots布尔值在侧边栏中将顶层节点显示为 "root"false
collapsedRoots数组设置默认在视觉上折叠的根节点 ID['misc', 'other']
renderLabel函数为树节点创建自定义标签;必须返回一个 ReactNode(item, api) => <abbr title="...">{item.name}</abbr>

以下选项可在 toolbar 命名空间下配置:

¥The following options are configurable under the toolbar namespace:

名称类型描述示例值
id字符串切换工具栏项的可见性{ hidden: false }

Storybook 钩子

¥Storybook hooks

为了帮助简化插件开发并减少样板代码,API 公开了一组钩子来访问 Storybook 的内部结构。这些钩子是 @storybook/manager-api 包的扩展。

¥To help streamline addon development and reduce boilerplate code, the API exposes a set of hooks to access Storybook's internals. These hooks are an extension of the @storybook/manager-api package.

useStorybookState

它允许访问 Storybook 的内部状态。与 useglobals 钩子类似,我们建议优化你的插件以依赖 React.memo 或以下钩子;useMemouseCallback 可防止大量重新渲染循环。

¥It allows access to Storybook's internal state. Similar to the useglobals hook, we recommend optimizing your addon to rely on React.memo, or the following hooks; useMemo, useCallback to prevent a high volume of re-render cycles.

my-addon/src/manager.js|ts
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 钩子是一个方便的助手,可让你完全访问 Storybook API 方法。

¥The useStorybookApi hook is a convenient helper to allow you full access to the Storybook API methods.

my-addon/manager.js|ts
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

允许设置对事件的订阅并让触发器向通道发出自定义事件。

¥Allows setting subscriptions to events and getting the emitter to emit custom events to the channel.

可以在 iframe 和管理器上收听消息。

¥The messages can be listened to on both the iframe and the manager.

my-addon/manager.js|ts
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 是需要数据持久性的插件的有用钩子,无论是由于 Storybook 的 UI 生命周期,还是涉及多种类型的更复杂的插件(例如工具栏、面板)。

¥The useAddonState is a useful hook for addons that require data persistence, either due to Storybook's UI lifecycle or for more complex addons involving multiple types (e.g., toolbars, panels).

my-addon/manager.js|ts
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 检索当前故事的参数。如果未定义参数的值,它将自动默认为定义的第二个值。

¥The useParameter retrieves the current story's parameters. If the parameter's value is not defined, it will automatically default to the second value defined.

my-addon/manager.js|ts
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 全局 的附加组件来说,这是一个非常有用的钩子。它允许你获取和更新 global 值。我们还建议优化你的插件以依赖 React.memo 或以下钩子;useMemouseCallback 可防止大量重新渲染循环。

¥Extremely useful hook for addons that rely on Storybook Globals. It allows you to obtain and update global values. We also recommend optimizing your addon to rely on React.memo, or the following hooks; useMemo, useCallback to prevent a high volume of re-render cycles.

my-addon/manager.js|ts
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

允许你检索或更新故事的 args 的钩子。

¥Hook that allows you to retrieve or update a story's args.

my-addon/src/manager.js|ts
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 插件生态系统的更多信息

¥Learn more about the Storybook addon ecosystem