首页
/ Storybook `tags` 配置实战:让 CSF Next 的 `.test` 用例默认不占用侧边栏

Storybook `tags` 配置实战:让 CSF Next 的 `.test` 用例默认不占用侧边栏

2026-09-07 19:20:41作者:尤峻淳Whitney

本文讲解 Storybook 官方 tags 配置中的一个典型场景:当你在项目中使用 CSF Factories 的实验性 .test 方法编写随附测试时,这些测试默认会以条目形式出现在 Storybook 侧边栏,干扰组件开发时的浏览体验。通过在主配置文件(.storybook/main.js.storybook/main.ts)中为内建 _test 标签声明 defaultFilterSelection: 'exclude',即可让测试用例默认从侧边栏隐藏,同时又完全保留其“可运行、可筛选、可临时调出”的能力。读完本文你将掌握 tags 配置项的字段语义、CSF 3 与 CSF Next 两种写法、以及它在 Storybook 管理器源码中的执行链路。

背景:这个配置要解决的问题

本配置片段出自 Storybook 官方文档「Tags」指南的 Recipes 章节,小节名为 Test cases that don't clutter the sidebardocs/writing-stories/tags.mdx)。其描述的场景是:

  • 你使用了 CSF Factories 的实验性 .test 方法 为某个 story 挂载随附测试;
  • 这类测试会被自动打上系统标签(内部名为 _test,在侧边栏 URL 中的简写为 $test);
  • 默认情况下它们会与普通 story 一同出现在侧边栏中,使左侧导航被大量“测试条目”占据;
  • 通过在主配置中调整 _test 标签的默认筛选行为,你可以让它们默认不可见,只在需要时通过侧边栏筛选器临时调出。

这种“同一批故事素材、多种使用视角”正是 Storybook tags 体系的核心价值:标签不仅用于给故事分类,还能控制哪些条目默认出现在侧边栏、哪些默认被排除,从而支撑一个 sidebar/dev 模式测试模式并行的工作流。

⚠️ 需要特别说明:该配置对所有标签(含自定义标签)都适用,但其中内置的 _test 标签本身仍处于实验阶段。

前置知识:tags 体系与 _test 标签

在深入配置之前,先厘清 Storybook 的标签机制。官方内置标签如下(详见 tags.mdx 中的 Built-in tags 表格):

标签 默认是否自动应用 作用
dev dev 标签的 story 会渲染在 Storybook 侧边栏
manifest 纳入 component / docs 清单输出
test 纳入测试运行(test runner、Vitest addon)范围
autodocs 控制是否生成 docs 页面
play-fn 自动应用到定义了 play function 的 story
test-fn 自动应用到用实验性 .test 方法定义的测试

其中,由 .test 方法产生的测试条目在侧边栏过滤语境中对应内部系统标签 _test(从 manager-api 侧边栏模块BUILT_IN_URL_TAG_MAP 可以看到 $test: '_test' 这一映射关系)。本文涉及的配置正是围绕这个 _test 标签展开。

区分两个易混淆的用法:在主配置 .storybook/main.*tags 字段里,你声明的是“标签的默认行为”(即本段代码做的事情);而真正给 story、组件或项目打标签,则是在 CSF 文件的 meta/story 或 .storybook/preview.* 中通过 tags: [...] 数组完成。

前提:启用实验性 .test 语法

_test 标签所对应的测试条目的来源,是 CSF Factories(CSF Next)中新增的 <Story>.test(...) 方法。这是一个实验性 API,需通过 experimentalTestSyntax feature flag 开启:

// .storybook/main.ts(片段,仅示意 feature 开关)
export default {
  features: {
    experimentalTestSyntax: true,
  },
};

该 flag 的完整说明见 main-config-features.mdx。启用后,你可以在 CSF Factory 的 story 上这样挂测试(示例见 csf-next.mdx):

export const PrimaryDisabled = Primary.extend({ args: { disabled: true } });

// .test 方法:为 story 挂载随附测试
PrimaryDisabled.test('should be disabled', async ({ canvas, userEvent, args }) => {
  const button = await canvas.findByRole('button');
  await userEvent.click(button);

  await expect(button).toHaveAttribute('aria-disabled', 'true');
  await expect(args.onClick).not.toHaveBeenCalled();
});

启用并编写了这类测试之后,即可通过下一节的 tags 配置把它们默认请出侧边栏。

核心配置:为 _test 声明默认排除

本配置片段(docs/_snippets/main-config-tags-test-fn-exclude.md)的本质,是在主配置的 tags 对象里,给 _test 设置 defaultFilterSelection: 'exclude'。下面是两种配置形态下的完整写法。

CSF 3:.storybook/main.js

export default {
  // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    // 👇 Adjust the default configuration of this tag
    _test: {
      defaultFilterSelection: 'exclude',
    },
  },
};

CSF 3:.storybook/main.ts(类型化写法)

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';

const config: StorybookConfig = {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    // 👇 Adjust the default configuration of this tag
    _test: {
      defaultFilterSelection: 'exclude',
    },
  },
};

export default config;

占位符 @storybook/your-framework 需替换为你实际使用的框架包,例如 @storybook/react-vite@storybook/nextjs@storybook/vue3-vite@storybook/angular 等。

CSF Next:.storybook/main.tsdefineMain 写法)

如果你的项目已经切换到 CSF Next(CSF Factories),主配置文件需要使用 defineMain 并从 @storybook/<framework>/node 导入。以 React 为例:

// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    // 👇 Adjust the default configuration of this tag
    _test: {
      defaultFilterSelection: 'exclude',
    },
  },
});

其他框架的 CSF Next 写法与此完全同构,唯一的差别是 defineMain 的导入来源与 framework 字段:

框架 导入 framework 取值
Vue 3 import { defineMain } from '@storybook/vue3-vite/node' @storybook/vue3-vite
Angular import { defineMain } from '@storybook/angular/node' @storybook/angular
Web Components import { defineMain } from '@storybook/web-components-vite/node' @storybook/web-components-vite

片段中的 Vue、Angular、Web Components 用例代码如下(与 React 版除导入来源外一致):

import { defineMain } from '@storybook/vue3-vite/node';

export default defineMain({
  framework: '@storybook/vue3-vite',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    _test: {
      defaultFilterSelection: 'exclude',
    },
  },
});
import { defineMain } from '@storybook/angular/node';

export default defineMain({
  framework: '@storybook/angular',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    _test: {
      defaultFilterSelection: 'exclude',
    },
  },
});
import { defineMain } from '@storybook/web-components-vite/node';

export default defineMain({
  framework: '@storybook/web-components-vite',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    _test: {
      defaultFilterSelection: 'exclude',
    },
  },
});

在 CSF Next 下同样支持 .storybook/main.jsdefineMain 写法(import { defineMain } from '@storybook/your-framework/node'),JavaScript 版本只是省略类型标注,配置结构完全一致。

defaultFilterSelection 的三种取值语义

tags 配置项的类型为 { [tagName: string]: { defaultFilterSelection?: 'include' | 'exclude' } }(见 main-config-tags.mdx)。defaultFilterSelection 决定某个标签在侧边栏筛选菜单中的初始选中状态

  • 'exclude':带该标签的条目在侧边栏默认被排除(筛选菜单中该标签默认处于“排除”状态)。需要查看时,可在侧边栏筛选菜单中显式“包含”该标签,把它们临时调回;
  • 'include':带该标签的条目默认被选中包含在侧边栏中;
  • 未设置(undefined:标签没有默认筛选状态,Storybook 不做任何额外处理。

与纯配置层面的字段说明相比,从源码可以看得更具体:TagOptions 类型定义中除了 defaultFilterSelection,还存在 excludeFromSidebarexcludeFromDocsStories 两个布尔字段——后者用于彻底阻止带该标签的条目进入侧边栏渲染,而 defaultFilterSelection: 'exclude' 只是设定一个“默认过滤条件”,条目本身仍然可以被用户手动包含回来。这也是本配置方案区别于“硬隐藏”的关键:它保证测试随时可以按需显示,而不是被永久抹掉。

exclude 配置到 _test 标签后的实际效果是:打开 Storybook 时侧边栏默认不再展示那些 .test 用例条目,组件列表保持干净;而运行测试(如 Vitest addon 或 test runner)时这些用例照常执行,不受侧边栏可见性影响。

源码视角:这段配置是如何生效的

在 Storybook 管理器(manager)侧,标签的默认筛选逻辑集中在 code/core/src/manager-api/modules/tags.ts。与本节配置直接相关的调用链可以梳理为:

  1. 解析预设默认筛选getDefaultTagsFromPreset(presets) 遍历主配置传入的 tagsTagsOptions),凡是 option.defaultFilterSelection === 'include' 的标签进入默认 included 集合,=== 'exclude' 的进入默认 excluded 集合。也就是说,本节在 _test 上写的 exclude,最终会变成侧边栏初始化时一组“默认排除”的标签集合。

  2. 生成实际过滤函数computeTagsFilterFn(includedTagFilters, excludedTagFilters) 把标签集合编译成对索引条目(API_PreparedIndexEntry)的过滤判定。值得注意的是,它会区分内建过滤器(BUILT_IN_FILTERS)与用户标签过滤器(USER_TAG_FILTER),并按分组进行“包含组任一命中、排除组全部命中”的组合判断,从而支持侧边栏里 include/exclude 混用的交互(详见 tags.mdx 中 “Filtering the sidebar by tags” 一节)。

  3. URL 状态同步parseTagsParamserializeTagsParam 负责把 $test_test)这类标签简写在 URL 参数中序列化/反序列化,使得用户当前的筛选状态可被收藏、可被分享。

  4. UI 层交互:侧边栏筛选菜单的组件行为可以在 Filter.stories.tsx 中看到对应的组件级 Story 用例。

配置字段 defaultFilterSelection 的类型声明位置为 core-common.ts,而 Tag/TagsOptions 的字段含义与“未设置即无默认筛选”的行为说明可对照 main-config-tags.mdx

与自定义标签配置的同构性

需要指出的是,本节做法并不局限于 _test。同一份 tags 配置对象可以用于任意自定义标签,例如定义一个默认排除的 experimental 标签(官方示例见 docs/_snippets/main-config-tags.md):

export default {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  tags: {
    // 👇 Define a custom tag named "experimental"
    experimental: {
      defaultFilterSelection: 'exclude', // Or 'include'
    },
  },
};

这类配置同样有 CSF 3(export default / 类型化 StorybookConfig)与 CSF Next(defineMain)两套写法,模式与上节完全一致。因此,掌握本文的 _test 配置,就等于掌握了在 Storybook 中“让任何一类标签默认不占侧边栏”的通用方法。

排除后如何按需查看与配合测试

配置 defaultFilterSelection: 'exclude' 之后,当你想临时审查某个 .test 用例时:

  • 打开侧边栏顶部的标签筛选菜单,在筛选器中选择 _test(界面中常以 $test 或对应语义名称呈现)并将它切换为“包含”,相应条目即会重新出现;
  • 筛选器支持多标签组合:同时选择多个标签会显示“包含任意一个标签”的条目;多选“排除”则排除命中任一标签的条目;也可以 include/exclude 混用;
  • 未做任何选择时,侧边栏展示全部条目;
  • 标签筛选会先于搜索生效——当你在侧边栏输入关键词搜索时,结果已经被当前筛选限定,这在大项目中能够显著缩小匹配范围。

下方截图展示了侧边栏标签筛选器的交互形态(筛选菜单中同时存在“包含”与“排除”两类标签时,侧边栏只显示“被包含且未被排除”的条目):

Storybook 侧边栏标签筛选器:被排除的 experimental 标签不显示,autodocs 标签的条目保留,这与 defaultFilterSelection 的默认筛选机制一致

需要留意的是,这套配置只影响侧边栏的展示/默认筛选,不影响 .test 用例是否参与测试运行。_test 条目的执行由测试链路(Vitest addon、test runner 等)依据标签与配置驱动;本方案真正的收益是让“写测试用的故事集”与“开发浏览用的故事集”在侧边栏各得其所,互不干扰。

注意事项与适用边界

  • 实验性 API_test 标签与 .test 方法、CSF Next 相关能力均处于实验阶段,接口细节可能随版本演进变化,建议关注官方 CHANGELOG 与本仓库的文档更新;
  • 格式前提:主配置中的 tags 字段是对标签“默认行为的声明”,要正确展示 .test 条目还需保证 experimentalTestSyntax feature 已开启,并在 .test 方法被调用的 CSF Factory 文件中使用了该语法;
  • 标签应用层面:若你想改变某个 story、组件或整个项目的具体标签集合(例如去掉 dev 实现“仅文档”故事),应到 .storybook/preview.*、meta 或 story 的 tags 数组里操作,与本文所讲的主配置声明并不冲突;
  • 脚本替换注意:将本文示例直接复制进项目前,务必把 @storybook/your-framework 替换为真实的框架包名,并确认 stories glob 与你项目实际的索引位置一致。

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388