参加直播:美国东部时间周四上午 11 点,Storybook 9 发布及问答
文档
Storybook Docs

Angular 版 Storybook

Angular 版 Storybook 是一个框架,可以轻松地为 Angular 应用开发和隔离测试 UI 组件。它包括

  • 🧱 使用 Angular 构建器
  • 🎛️ 集成 Compodoc
  • 💫 还有更多!

要求

  • Angular ≥ 18.0 < 20.0
  • Webpack ≥ 5.0

开始使用

在没有 Storybook 的项目中

在你的 Angular 项目根目录运行此命令后,按照提示进行操作

npm create storybook@latest

关于 Storybook 入门的更多信息。

在已有 Storybook 的项目中

此框架设计用于 Storybook 7+ 版本。如果你尚未使用 v7,请运行此命令进行升级

npx storybook@next upgrade

自动迁移

运行上面的 upgrade 命令时,你应该会收到一个提示,要求迁移到 @storybook/angular,它应该能为你处理好一切。如果自动迁移对你的项目无效,请参考下面的手动迁移。

手动迁移

首先,安装框架

npm install --save-dev @storybook/angular

然后,更新你的 .storybook/main.js|ts 以更改 framework 属性

.storybook/main.ts
import { StorybookConfig } from '@storybook/angular';
 
const config: StorybookConfig = {
  // ...
  framework: '@storybook/angular', // 👈 Add this
};
 
export default config;

最后,更新你的 angular.json 以包含 Storybook 构建器

angular.json
{
  "projects": {
    "your-project": {
      "architect": {
        "storybook": {
          "builder": "@storybook/angular:start-storybook",
          "options": {
            // The path to the storybook config directory
            "configDir": ".storybook",
            // The build target of your project
            "browserTarget": "your-project:build",
            // The port you want to start Storybook on
            "port": 6006
            // More options available, documented here:
            // https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular/src/builders/start-storybook/schema.json
          }
        },
        "build-storybook": {
          "builder": "@storybook/angular:build-storybook",
          "options": {
            "configDir": ".storybook",
            "browserTarget": "your-project:build",
            "outputDir": "dist/storybook/your-project"
            // More options available, documented here:
            // https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular/src/builders/build-storybook/schema.json
          }
        }
      }
    }
  }
}

运行 Storybook

要为特定项目运行 Storybook,请运行以下命令

ng run <your-project>:storybook

要构建 Storybook,请运行

ng run <your-project>:build-storybook

你将在配置的 outputDir 中找到输出(默认为 dist/storybook/<你的项目名>)。

设置 Compodoc

你可以在组件、指令和 Angular 代码的其他部分上方添加 JSDoc 注释,以便为这些元素提供文档。Compodoc 使用这些注释为你的应用生成文档。在 Storybook 中,在 @Inputs@Outputs 上方添加解释性注释非常有用,因为这些是 Storybook 在用户界面中显示的主要元素。@Inputs@Outputs 是你可以在 Storybook 中与之交互的元素,例如控件

自动设置

通过 npx storybook@latest init 安装 Storybook 时,你可以自动设置 Compodoc。

手动设置

如果你已经安装了 Storybook,可以手动设置 Compodoc。

安装以下依赖

npm install --save-dev @compodoc/compodoc

将以下选项添加到你的 Storybook Builder 中

angular.json
{
  "projects": {
    "your-project": {
      "architect": {
        "storybook": {
          "builder": "@storybook/angular:start-storybook",
          "options": {
            // 👇 Add these
            "compodoc": true,
            "compodocArgs": [
              "-e",
              "json",
              "-d",
              // Where to store the generated documentation. It's usually the root of your Angular project. It's not necessarily the root of your Angular Workspace!
              "."
            ],
          }
        },
        "build-storybook": {
          "builder": "@storybook/angular:build-storybook",
          "options": {
            // 👇 Add these
            "compodoc": true,
            "compodocArgs": [
              "-e",
              "json",
              "-d",
              "."
            ],
          }
        }
      }
    }
  }
}

转到你的 .storybook/preview.ts 并添加以下内容

.storybook/preview.ts
import type { Preview } from '@storybook/angular';
 
// 👇 Add these
import { setCompodocJson } from '@storybook/addon-docs/angular';
import docJson from '../documentation.json';
setCompodocJson(docJson);
 
const preview: Preview = {};
export default preview;

applicationConfig 装饰器

如果你的组件依赖于应用范围的提供者,例如由 BrowserAnimationsModule 或任何其他使用 forRoot 模式提供 ModuleWithProviders 的模块定义的提供者,你可以将 applicationConfig 装饰器应用于该组件的所有故事。这将为它们提供 bootstrapApplication 函数,用于在 Storybook 中引导组件。

ChipsModule.stories.ts
import { Meta, applicationConfig, StoryObj } from '@storybook/angular';
 
import { BrowserAnimationsModule, provideAnimations } from '@angular/platform-browser/animations';
import { importProvidersFrom } from '@angular/core';
 
import { ChipsModule } from './angular-src/chips.module';
 
const meta: Meta<ChipsModule> = {
  component: ChipsModule,
  decorators: [
    // Apply application config to all stories
    applicationConfig({
      // List of providers and environment providers that should be available to the root component and all its children.
      providers: [
        ...
        // Import application-wide providers from a module
        importProvidersFrom(BrowserAnimationsModule)
        // Or use provide-style functions if available instead, e.g.
        provideAnimations()
      ],
    }),
  ],
};
 
export default meta;
type Story = StoryObj<ChipsModule>;
 
export const WithCustomApplicationProvider: Story = {
  render: () => ({
    // Apply application config to a specific story
    applicationConfig: {
      // The providers will be merged with the ones defined in the applicationConfig decorator's providers array of the global meta object
      providers: [...],
    }
  })
}

moduleMetadata 装饰器

如果你的组件依赖于其他 Angular 指令或模块,可以通过 moduleMetadata 装饰器为组件的所有故事或单独故事提供这些依赖。

YourComponent.stories.ts
import { Meta, moduleMetadata, StoryObj } from '@storybook/angular';
 
import { YourComponent } from './your.component';
 
const meta: Meta<YourComponent> = {
  component: YourComponent,
  decorators: [
    // Apply metadata to all stories
    moduleMetadata({
      // import necessary ngModules or standalone components
      imports: [...],
      // declare components that are used in the template
      declarations: [...],
      // List of providers that should be available to the root component and all its children.
      providers: [...],
    }),
  ],
};
export default meta;
type Story = StoryObj<YourComponent>;
 
export const Base: Story = {};
 
export const WithCustomProvider: Story = {
  decorators: [
    // Apply metadata to a specific story
    moduleMetadata({
      imports: [...],
      declarations: [...],
      providers: [...],
    }),
  ],
};

常见问题

如何迁移到 Angular Storybook 构建器?

Storybook 的 Angular 构建器是在 Angular workspace 中运行 Storybook 的一种方式。它可以直接替代运行 storybook devstorybook build

你可以运行 npx storybook@next automigrate 尝试让 Storybook 检测并自动修复你的配置。否则,你可以按照接下来的步骤手动调整配置。

你的 workspace 中只有一个 Angular 项目吗?

首先,打开你的 angular.json,并在项目的 architect 部分添加 storybookbuild-storybook 条目,如上所示

其次,调整你的 package.json 脚本部分。通常它看起来像这样

package.json
{
  "scripts": {
    "storybook": "start-storybook -p 6006", // or `storybook dev -p 6006`
    "build-storybook": "build-storybook" // or `storybook build`
  }
}

现在,你可以使用 ng run <你的项目名>:storybook 运行 Storybook,并使用 ng run <你的项目名>:build-storybook 构建它。相应地调整 package.json 中的脚本。

package.json
{
  "scripts": {
    "storybook": "ng run <project-name>:storybook",
    "build-storybook": "ng run <project-name>:build-storybook"
  }
}

此外,compodoc 现在已内置到 @storybook/angular 中;你无需显式调用它。如果我们在 package.json 脚本中像这样运行 compodoc

package.json
{
  "scripts": {
    "docs:json": "compodoc -p tsconfig.json -e json -d ./documentation",
    "storybook": "npm run docs:json && start-storybook -p 6006",
    "build-storybook": "npm run docs:json && build-storybook"
  }
}

将其更改为

package.json
{
  "scripts": {
    "storybook": "ng run <project-name>:storybook",
    "build-storybook": "ng run <project-name>:build-storybook"
  }
}

我的 Angular workspace 中有多个项目

在这种情况下,你必须按照上述说明为每个要使用 Storybook 的项目调整 angular.jsonpackage.json。请注意,每个项目应在其根目录下有一个专门的 .storybook 文件夹。

你可以为每个项目依次运行 npx storybook@latest init,以便为它们各自设置 Storybook,自动创建 .storybook 文件夹,并在 angular.json 中创建必要的配置。

然后,你可以使用Storybook 组合来合并多个 Storybook。

如何为 Storybook 配置 Angular 的构建器?

这些是 Angular 构建器可能需要的常用选项

配置元素描述
"browserTarget"使用以下格式提供构建目标。
"example-project:builder:config"
"debugWebpack"调试 Webpack 配置
"debugWebpack": true
"tsConfig"TypeScript 配置文件相对于当前 workspace 的位置。
"tsConfig": "./tsconfig.json".
"preserveSymlinks"解析模块时不使用真实路径。如果为 true,则符号链接被解析为其真实路径;否则,它们被解析为其符号链接路径。
"preserveSymlinks": true
"port"Storybook 使用的端口。
"port": 6006
"host"为 Storybook 设置自定义 host。
"host": "http://my-custom-host"
"configDir"Storybook 配置目录位置。
"configDir": ".storybook"
"https"启用 HTTPS 启动 Storybook。
"https": true
需要自定义证书信息。
"sslCa"提供 SSL 证书颁发机构。
"sslCa": "你的自定义证书颁发机构"
可与 "https" 配合使用(可选)
"sslCert"提供 SSL 证书。
"sslCert": "你的自定义证书"
https 必需
"sslKey"提供用于服务 Storybook 的 SSL 密钥。
"sslKey": "你的 ssl 密钥"
"smokeTest"成功启动后退出 Storybook。
"smokeTest": true
"ci"在 CI 模式下启动 Storybook(跳过交互式提示,不会打开浏览器窗口)。
"ci": true
"open"是否在浏览器中自动打开 Storybook。
"open": true
"quiet"过滤 Storybook 的详细构建输出。
"quiet": true
"enableProdMode"禁用 Angular 的开发模式,这会关闭框架内的断言和其他检查。
"enableProdMode": true
"docs"文档模式下启动 Storybook。
"docs": true
"compodoc"之前执行 compodoc。
"compodoc": true
"compodocArgs"Compodoc 选项。总是会给出包含 tsconfig 路径的 -p 选项和包含 workspace 根目录的 -d 选项。
"compodocArgs": ["-e", "json"]
"styles"提供与 Storybook 一起使用的应用程序样式的位置。
"styles": ["src/styles.css", "src/styles.scss"]
"stylePreprocessorOptions"为解析到 workspace 根目录的样式预处理器提供进一步的自定义。
"stylePreprocessorOptions": { "includePaths": ["src/styles"] }
"assets"静态应用程序资产列表。
"assets": ["src/assets"]
"initialPath"首次访问 Storybook 时要附加的 URL 路径。
"initialPath": "docs/configure-your-project--docs"
"webpackStatsJson"将 Webpack Stats JSON 写入磁盘。
"webpackStatsJson": true
"previewUrl"禁用默认的 Storybook 预览,并允许你使用自己的预览。
"previewUrl": "iframe.html"
"loglevel"控制构建期间的日志级别。可以是以下之一:[silly, verbose, info (默认), warn, error, silent]。
"loglevel": "info"
"sourceMap"配置源代码映射
"sourceMap": true

完整的选项列表可以在 Angular builder schema 中找到

API

选项

如果需要,可以传递一个 options 对象进行额外配置

.storybook/main.ts
import type { StorybookConfig } from '@storybook/angular';
 
const config: StorybookConfig = {
  framework: {
    name: '@storybook/angular',
    options: {
      // ...
    },
  },
};

可用的选项有

builder

类型:Record<string, any>

配置框架的构建器的选项。对于此框架,可用选项可在Webpack 构建器文档中找到。