加入直播会议:周四,美国东部时间上午 11 点,Storybook 9 发布 & 问答

标签徽章

在侧边栏和工具栏中将 Storybook 标签显示为徽章。

在 Github 上查看

📔 目录

🤔 我应该使用哪个徽章插件?

还有一些其他项目也用于在 Storybook 中显示徽章。本插件是 storybook-addon-badges(由 Jim Drury 开发)的重写版本,专注于利用 Storybook 的标签(tags)。我们将标签作为数据源来显示徽章,而不是使用专门的故事参数(story parameters),因为标签在 Storybook 中越来越普遍,并且与徽章的作用高度重叠。

这种架构选择开辟了新的可能性,但也导致原始插件的一些功能无法使用。下表总结了两个插件之间的差异。

storybook-addon-tag-badges storybook-addon-badges
在工具栏中显示徽章
在侧边栏中显示徽章 ⚠️ 仅针对当前故事
基于标签定义徽章
按故事自定义
工具提示支持 ⚠️ 仅在工具栏中
Storybook >= 8.4
Storybook < 8.3

[!NOTE] Storybook 8.5 需要 React 19。所有插件作者都必须采用 React 19 才能兼容 8.5。这就是为什么我的插件较新版本不再支持 Storybook 8.4 的原因。支持 Storybook 8.4 的最后一个版本是 v1.3.2。

📦 安装

yarn add -D storybook-addon-tag-badges
npm install -D storybook-addon-tag-badges
pnpm install -D storybook-addon-tag-badges

在你的 .storybook/main.ts 文件中,添加以下内容

// .storybook/main.ts
export default {
  addons: ['storybook-addon-tag-badges'],
}

🏁 默认配置

此插件附带默认配置,你可以立即通过为内容添加标签来开始使用。

预配置徽章

预览 标签模式 建议用途
new 最近添加的组件或属性/功能
alpha, beta, rc, experimental 警告组件或属性尚不稳定
deprecated 在新代码中应避免使用的组件或属性
outdated 设计已更改但尚未实现的组件,这可能会给用户带来额外的开发成本
danger 配置时需要特别注意的组件(例如,存在安全隐患的组件)
code-only 仅存在于代码中而没有设计稿的组件
version:* 按组件版本控制

显示逻辑

默认情况下,所有标签始终显示在工具栏上,但只在侧边栏中显示给组件条目以及出现在顶层文档或故事条目。它们不会显示在组件或分组条目内部的文档或故事条目中。

此外,该插件在侧边栏中每个条目只显示一个徽章。配置中排在前面的徽章将优先显示。例如,new 徽章将显示在 code-only 徽章之前。

👀 用法

要显示预配置徽章,请将相关标签添加到你的组件、故事或文档条目中。

组件徽章

要为一个组件(及其子故事)设置徽章,请在该组件的 meta 中定义 tags

// src/components/Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'

const meta: Meta<typeof Button> = {
  title: 'Example/Button',
  component: Button,
  tags: ['autodocs', 'version:1.0.0', 'new'],
}

故事徽章

要为特定故事添加徽章,请直接在故事对象中添加 tags

// src/components/Button.stories.ts
export const Tertiary: StoryObj<typeof Button> = {
  args: {
    variant: 'tertiary',
    size: 'md',
  },
  tags: ['experimental'],
}

文档徽章

要为文档条目设置徽章,请将 tags 数组传递给 docs 参数

// src/components/Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'

const meta: Meta<typeof Button> = {
  title: 'Example/Button',
  component: Button,
  parameters: {
    docs: {
      tags: ['outdated'],
    },
  },
}

🛠️ 自定义徽章配置

在你的 manager 文件中,你可以重新定义用于将标签映射到徽章的配置对象。每个标签只渲染一次,使用与其匹配的第一个徽章配置;因此,如果你也想保留默认配置,请务必将你的覆盖配置放在前面。

// .storybook/manager.ts
import { addons } from '@storybook/manager-api'
import {
  defaultConfig,
  type TagBadgeParameters,
} from 'storybook-addon-tag-badges'

addons.setConfig({
  tagBadges: [
    // Add an entry that matches 'frog' and displays a cool badge in the sidebar only
    {
      tags: 'frog',
      badge: {
        text: 'Frog 🐸',
        bgColor: '#001c13',
        fgColor: '#e0eb0b',
        tooltip: 'This component can catch flies!',
      },
      display: {
        sidebar: ['component'],
        toolbar: false,
      },
    },
    // Place the default config after your custom matchers.
    ...defaultConfig,
  ] satisfies TagBadgeParameters,
})

现在让我们详细了解 tagBadges 的各种属性。tagBadges 中的每个对象表示一个要匹配的标签列表,如果找到匹配项,则指定要使用的徽章配置以及徽章应该显示的位置。

标签

tags 属性定义了将显示徽章的标签模式。它可以是单个模式或模式数组。

标签模式可以是

模式类型 描述 示例模式 匹配结果
string 精确匹配 'new' 'new'
RegExp 正则表达式 /v\d+\d+\d+/ 'v1.0.0'
{ prefix: string | RegExp } 匹配标签中 : 分隔符之前的部分 { prefix: 'status' } 'status:done'
{ prefix: string | RegExp } 匹配标签中 : 分隔符之后的部分 { suffix: 'a11y' } 'compliant:a11y'

显示

display 属性控制徽章在哪里以及为哪种类型的内容渲染。它有两个子属性:sidebartoolbar。在侧边栏中,标签可以显示给组件、分组、文档或故事条目。在工具栏中,它们可以设置给文档或故事条目(因为其他条目类型不能在侧边栏外显示)。

Storybook 渲染以下条目类型

图标 名称 描述
story 在你的 CSF 文件中编写的组件故事之一。
docs 通过 MDX 文件或 autodocs 生成的文档页面。
component 一个组件的故事和 autodocs 页面的分组。
group 包含未附加的 MDX 文档、故事和/或组件的通用分组。

要控制徽章的显示位置,你可以向 sidebartoolbar 键传递条件。你可以指定单个条件,或条件数组(在这种情况下,匹配任何一个条件都会导致徽章显示)。

条件可以指定你想显示徽章的条目类型,或者是否允许显示父条目(例如侧边栏中的顶层组件)未显示过的任何标签的徽章。在默认配置中,继承自父级的标签会被跳过;否则,当你将标签添加到 CSF meta 导出时,每个故事都会有一个标签,这将非常冗长。

一个条件完整形式有两个属性

属性 描述 类型 示例值
type 要匹配的条目类型 string 'docs'
skipInherited 如果 UI 中的父条目已经为同一个标签显示了徽章,是否跳过显示该徽章 string true

支持语法快捷方式,总结如下表

类型 描述 示例 侧边栏结果 工具栏结果
ø (未设置) 使用默认行为 [{ skipInherited: true }, { type: 'component', skipInherited: false }, { type: 'group', skipInherited: false }] [{ type: 'docs' }, { type: 'story' }]
false 从不显示徽章 false [] []
true 在任何地方显示徽章 true [{ skipInherited: false }] [{ type: 'docs' }, { type: 'story' }]
string 仅显示给一种条目类型,并跳过继承的标签徽章 'docs' [{ type: 'docs', skipInherited: true }] [{ type: 'docs' }]

徽章

badge 属性定义了要显示的徽章的外观和内容。它可以是一个静态对象,也可以是一个根据匹配的内容和标签动态生成徽章的函数。

静态徽章对象

该对象具有以下属性

名称 类型 描述 示例
text string 徽章中显示的文本(必需)。 'New'
bgColor string? 传递给 background-color 的 CSS 属性。 '#aea黄绿色'
fgColor string? 传递给 color 的 CSS 属性。 '#2f2'
borderColor string? 边框颜色,渲染为 CSS box-shadow。 '#2f2'
tooltip string | TooltipMessageProps? 仅在工具栏中点击时显示的工具提示。 'This component is new!'{ title: 'New Component', desc: 'Recently added to the library' }

动态徽章函数

动态徽章函数允许你根据当前条目和匹配的标签自定义徽章。它们必须返回如上所述的有效徽章对象。它们接收一个包含以下属性的对象参数

  • entry: 当前的 HashEntry(组件、故事等),包含 id 和/或 nametypetags
  • getTagParts, getTagPrefix, getTagSuffix: 用于提取标签部分的实用函数
  • tag: 匹配的标签字符串

动态徽章函数示例

// .storybook/manager.ts
import { addons } from '@storybook/manager-api'
import {
  defaultConfig,
  type TagBadgeParameters,
} from 'storybook-addon-tag-badges'

addons.setConfig({
  tagBadges: [
    {
      tags: { prefix: 'version' },
      badge: ({ entry, getTagSuffix, tag }) => {
        const version = getTagSuffix(tag)
        const isUnstable = version.startsWith('0')
        return {
          text: `v${version}`,
          bgColor: version.startsWith('0') ? '#f0ccff' : '#cce0ff',
          tooltip: `Version ${version}${isUnstable ? ' (unstable)' : ''}`,
        }
      },
    },
    ...defaultConfig,
  ] satisfies TagBadgeParameters,
})

工具提示

徽章在工具栏中显示时可能带有工具提示。为避免与侧边栏的功能冲突,侧边栏中禁用了工具提示,但欢迎对此提供反馈。

你可以为简单的工具提示传递一个字符串。你也可以传递 Storybook 的 TooltipMessage 使用的相同对象

  • title: 工具提示的标题 [string]
  • desc: 工具提示的次要文本 [string]
  • links: 可选的链接对象数组,显示为按钮 [object[]]
    • title: 链接的标题
    • href: 链接指向的 URL(在当前位置导航)
    • onClick: 点击链接时的回调(可用于在新浏览器标签页中导航)

侧边栏配置

此插件使用 侧边栏 renderLabel 功能在侧边栏中显示徽章。如果你在 Storybook 实例中为其他目的定义了它,它将与此插件冲突,侧边栏徽章将不会显示。

要为未被你自己的 renderLabel 逻辑自定义的项显示徽章,你可以导入插件自己的 renderLabel 函数并在你的函数末尾调用它。

// .storybook/manager.ts
import { addons } from '@storybook/manager-api'
import type { API_HashEntry } from '@storybook/types'
import { renderLabel, Sidebar } from 'storybook-addon-tag-badges'

addons.setConfig({
  sidebar: {
    renderLabel: (item: API_HashEntry) => {
      // Customise your own items, with no badge support.
      if (item.name === 'Support') {
        return '🛟 Get Support'
      }

      // Customise items with badge support by wrapping in Sidebar.
      if (item.type === 'docs') {
        return <Sidebar item={item}>{item.name} [doc]</Sidebar>
      }

      // Badges for every item not customised by you.
      return renderLabel(item)
    },
  }
})

同样,如果你为 sidebar 选项定义配置但不包含 renderLabel,则此插件定义的渲染函数将被覆盖,徽章将不会显示在侧边栏中。像这样导入并添加 renderLabel 函数

// .storybook/manager.ts
import { addons } from '@storybook/manager-api'
import { renderLabel } from 'storybook-addon-tag-badges'

addons.setConfig({
  sidebar: {
    /* your own changes here... */
    renderLabel,
  }
})

📝 工作流程示例

此仓库包含如何使用 Storybook 徽章支持各种工作流程的示例

  • 市场细分
  • 区分功能组件和品牌组件
  • a11y、品牌、QA 等检查的合规状态
  • 组件组合模式
  • 使用外部依赖
  • 智能组件

要查看这些示例的实际效果,请查看仓库并运行本地 Storybook 实例

git clone https://github.com/Sidnioulz/storybook-addon-tag-badges.git
cd storybook-addon-tag-badges
pnpm i
pnpm start

🐌 限制

按故事配置

此插件不支持更改特定故事的徽章配置,并且永远不会支持。这是因为 Storybook UI 的部分,例如侧边栏,是在未加载故事数据的上下文中渲染的。Storybook 在 v7 中停止预加载所有故事数据,以提高性能。

因此,我们需要在没有访问故事特定数据的情况下创建侧边栏标签。此插件使用 核心插件 API 读取你的配置,因此自定义特定徽章渲染的方式是使用动态徽章函数。这些函数可以利用故事的 ID、标题或标签内容来自定义渲染的徽章,如下面的示例所示。

组件标签

在 Storybook 中,你的 MDX 和 CSF 文件被转换为 docscomponentgroupstory 条目以渲染侧边栏,每个都有自己的语义。docsstory 条目分别直接继承 parameters.docs.tagsCSF meta 中定义的标签。

对于 component 条目,标签是间接计算的:它们是组件所有故事中存在的标签的交集。例如,对于在其 meta 中定义了标签 version:1.2.0 并且有一个故事定义了附加标签 deprecated 的组件,该组件条目将仅具有定义的 version:1.2.0 标签。

特别是,如果组件 meta 定义了两个标签 outdatedversion:1.1.0,但有一个故事明确移除了标签 outdated(通过添加 !outdated),则组件条目将仅具有标签 version:1.1.0

👩🏽‍💻 贡献

行为准则

请先阅读行为准则

开发者来源证书

为确保贡献者在本项目许可条款下合法分享其贡献内容,贡献者必须遵守开发者来源证书 (DCO)。所有贡献都必须签名以满足 DCO。这由 Pull Request 检查处理。

通过签署你的提交,你证明以下内容

  1. 该贡献全部或部分由你创建,并且你有权在文件中指明的开源许可下提交它;或
  2. 该贡献基于以前的工作,据你所知,该工作受适当的开源许可覆盖,并且根据该许可,你有权在相同的开源许可下提交该工作及其修改(除非允许你在不同许可下提交),无论修改是全部还是部分由你创建,如文件中所示;或
  3. 该贡献由其他证明了第 1、2 或 3 条的人直接提供给你,并且你没有对其进行修改。
  4. 你理解并同意此项目和贡献是公开的,并且贡献记录(包括你随附提交的所有个人信息,包括你的签名)将无限期保留,并可能根据本项目或所涉及的开源许可进行再分发。

入门

本项目使用 PNPM 作为包管理器。

常用命令

  • pnpm start 启动本地 Storybook
  • pnpm build 构建并打包插件代码
  • pnpm pack:local 生成一个本地 tarball,可在其他地方用作 NPM 依赖项
  • pnpm test 运行单元测试

迁移到更高版本的 Storybook

如果你想迁移插件以支持最新版本的 Storybook,可以查阅插件迁移指南

发布系统

此包通过 semantic-release 在推送到 main 分支时自动发布。不维护更新日志,并且 package.json 中的版本号不同步。

🆘 支持

请通过开一个 Issue 来提交 bug 报告或代码建议。请确保为 bug 报告包含一个可运行的最小工作示例 (Minimal Working Example)。你可以使用 storybook.new 来快速搭建一个复现环境。

✉️ 联系方式

Steve Dodier-Lazaro · Storybook Discord 上的 @Frog - 领英 (LinkedIn)

项目链接:https://github.com/Sidnioulz/storybook-addon-tag-badges

💛 致谢

感谢

构建工具

Dependabot ESLint GitHub Prettier Semantic-Release Storybook tsup TypeScript Vitest