加入直播会话:周四,美国东部时间上午 11 点,Storybook 9 发布及 AMA

实时代码编辑器

用于实时编辑 Storybook stories 的插件。支持 React 和 TypeScript。

在 Github 上查看

storybook-addon-code-editor

用于实时编辑 Storybook stories 的插件。支持 React 和 TypeScript。

查看实际效果。

查看使用此插件的示例项目。

这是什么?

这是一个 Storybook 插件,支持实时编辑 React 组件并实时预览。可以把它想象成一个轻量级的 CodeSandbox,直接集成在 stories 或 MDX 页面中。

它使用 Monaco Editor(浏览器版 VS Code),提供卓越的 TypeScript 编辑体验。

开始使用

  1. 安装为开发依赖
npm install --save-dev storybook-addon-code-editor
# Or yarn:
yarn add --dev storybook-addon-code-editor
  1. 在你的 .storybook/main.ts 文件中添加 storybook-addon-code-editor,并确保 staticDirsaddonsframework 字段包含以下内容
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
import { getCodeEditorStaticDirs } from 'storybook-addon-code-editor/getStaticDirs';

const config: StorybookConfig = {
  staticDirs: [...getCodeEditorStaticDirs(__filename)],
  addons: ['storybook-addon-code-editor'],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
};

export default config;

staticDirs 设置了 Storybook 加载的静态文件目录列表。编辑器 (monaco-editor) 需要这些额外的静态文件在运行时可用。

可以使用 storybook-addon-code-editor/getStaticDirs 中的 getExtraStaticDir 助手函数添加额外的静态文件

// .storybook/main.ts
import {
  getCodeEditorStaticDirs,
  getExtraStaticDir,
} from 'storybook-addon-code-editor/getStaticDirs';

const config: StorybookConfig =  {
  staticDirs: [
    ...getCodeEditorStaticDirs(__filename),
    // files will be available at: /monaco-editor/esm/*
    getExtraStaticDir('monaco-editor/esm'),

重要提示

目前只支持 @storybook/react-vite 框架。

API

Playground

MDX 格式中使用 Playground 组件。

// MyComponent.stories.mdx
import { Playground } from 'storybook-addon-code-editor'

<Playground code="export default () => <h1>Hello</h1>" />
// MyComponent.stories.mdx
import { Playground } from 'storybook-addon-code-editor';
import \* as MyLibrary from './index';
import storyCode from './MyStory.source.tsx?raw';

// TypeScript might complain about not finding this import or
// importing things from .d.ts files wihtout `import type`.
// Ignore this, we need the string contents of this file.
// @ts-ignore
import MyLibraryTypes from '../dist/types.d.ts?raw';

<Playground
  availableImports={{ 'my-library': MyLibrary }}
  code={storyCode}
  height="560px"
  id="unique id used to save edited code until the page is reloaded"
  modifyEditor={(monaco, editor) => {
    // editor docs: https://msdocs.cn/monaco-editor/api/interfaces/monaco.editor.IStandaloneCodeEditor.html
    // monaco docs: https://msdocs.cn/monaco-editor/api/modules/monaco.html
    editor.getModel().updateOptions({ tabSize: 2 });
    monaco.editor.setTheme('vs-dark');
    monaco.languages.typescript.typescriptDefaults.addExtraLib(
      MyLibraryTypes,
      'file:///node_modules/my-library/index.d.ts',
    );
  }}
/>

Playground 属性

interface PlaygroundProps {
  availableImports?: {
    [importSpecifier: string]: {
      [namedImport: string]: any;
    };
  };
  code?: string;
  defaultEditorOptions?: Monaco.editor.IEditorOptions;
  height?: string;
  id?: string | number | symbol;
  modifyEditor?: (monaco: Monaco, editor: Monaco.editor.IStandaloneCodeEditor) => any;
}

如果 code 没有导入 React,它会自动导入。如果 @types/react 可用,React TypeScript 定义也会自动加载。

makeLiveEditStory

在传统 stories 中使用 makeLiveEditStory 函数来显示代码编辑器面板

// MyComponent.stories.ts
import type { Meta, StoryObj } from '@storybook/react';
import { makeLiveEditStory } from 'storybook-addon-code-editor';
import * as MyLibrary from './index';
import storyCode from './MyStory.source.tsx?raw';

const meta = {
  // Story defaults
} satisfies Meta<typeof MyLibrary.MyComponent>;

export default meta;

type Story = StoryObj<typeof meta>;

export const MyStory: Story = {
  // Story config
};

makeLiveEditStory(MyStory, {
  availableImports: { 'my-library': MyLibrary },
  code: storyCode,
});

makeLiveEditStory 选项

interface LiveEditStoryOptions {
  availableImports?: {
    [importSpecifier: string]: {
      [namedImport: string]: any;
    };
  };
  code: string;
  modifyEditor?: (monaco: Monaco, editor: Monaco.editor.IStandaloneCodeEditor) => any;
  defaultEditorOptions?: Monaco.editor.IEditorOptions;
}

setupMonaco

setupMonaco 允许自定义 monaco-editor

在你的 .storybook/preview.ts 中使用此函数来添加类型定义或集成。

查看具有不同配置的 monaco-editor 示例

// .storybook/preview.ts
import { setupMonaco } from 'storybook-addon-code-editor';

setupMonaco({
  // https://msdocs.cn/monaco-editor/typedoc/interfaces/Environment.html
  monacoEnvironment: {
    getWorker(moduleId, label) {
      ...
    },
  },
  // onMonacoLoad is called when monaco is first loaded, before an editor instance is created.
  onMonacoLoad(monaco) {
    ...
  },
});

setupMonaco 选项

interface MonacoSetup {
  monacoEnvironment?: Monaco.Environment;
  onMonacoLoad?: (monaco: Monaco) => any;
}

贡献

安装依赖

npm install

运行示例

npm run start-example

当对库进行更改时,需要手动重启服务器。

运行测试

npm run test

格式化代码

npm run format

构建库

npm run build

提交

使用 Conventional Commits 允许自动版本发布。

  • fix: 表示 bug 修复,对应于 SemVer 的补丁版本。
  • feat: 表示新功能,对应于 SemVer 的次版本。
  • feat!:,或 fix!:refactor!: 等,表示破坏性更改(由 ! 标示),将导致 SemVer 的主版本升级。

发布

将自动化生成的指向主分支的 release-please PR 合并即可部署发布。