高亮
Storybook 的高亮功能是一个非常有用的组件视觉调试工具。它可以直接用于高亮故事中的特定 DOM 节点,或增强诸如 可访问性插件 等附加组件的功能,以通知你组件中存在的可访问性问题。
高亮 DOM 元素
要使用此功能高亮 DOM 元素,你需要从故事或附加组件中发出 HIGHLIGHT
事件。事件负载必须包含一个 selectors
属性,该属性被赋值为一个选择器数组,用于匹配你想高亮的元素。例如
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Highlighted: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
selectors: ['h2', 'a', '.storybook-button'],
});
return storyFn();
},
],
};
我们建议选择尽可能具体的选择器,以避免高亮其他插件使用的元素。这是因为此功能会尝试将选择器与整个 DOM 树进行匹配。
自定义样式
默认情况下,高亮元素会应用标准轮廓样式。但是,你可以通过向负载对象添加额外属性来启用自定义样式,从而自定义高亮元素的外观。例如
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const StyledHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
selectors: ['h2', 'a', '.storybook-button'],
styles: {
backgroundColor: `color-mix(in srgb, hotpink, transparent 90%)`,
outline: '3px solid hotpink',
animation: 'pulse 3s linear infinite',
transition: 'outline-offset 0.2s ease-in-out',
},
hoverStyles: {
outlineOffset: '3px',
},
focusStyles: {
backgroundColor: 'transparent',
},
keyframes: `@keyframes pulse {
0% { outline-color: rgba(255, 105, 180, 1); }
50% { outline-color: rgba(255, 105, 180, 0.2); }
100% { outline-color: rgba(255, 105, 180, 1); }
}`,
});
return storyFn();
},
],
};
这些属性是可选的,你可以使用它们来自定义高亮元素的外观。建议将 hoverStyles
和 focusStyles
属性与 menu
属性一起使用。不支持伪类和伪元素。
高亮菜单
高亮功能包含一个内置调试选项,允许你在点击高亮元素时选中它们。这对于检查受该功能影响的元素特别有用,因为它允许你预览与你提供的选择器匹配的元素列表。要启用它,请在负载对象中添加一个 menu
属性,其中包含有关元素或触发动作的附加信息。每个项目必须包含一个 id
和一个 title
,你还可以提供一个可选的 selectors
属性,将菜单项限制于特定的高亮元素。
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const StyledHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
selectors: ['h2', 'a', '.storybook-button'],
menu: [
[
{
id: 'button-name',
title: 'Login',
description: 'Navigate to the login page',
clickEvent: 'my-menu-click-event',
},
{
id: 'h2-home',
title: 'Acme',
description: 'Navigate to the home page',
},
],
],
});
return storyFn();
},
],
};
启用后,当你点击与你提供的选择器匹配的选中元素时,将显示菜单。但是,如果你不想显示任何信息,可以省略这些项目或将 menu
属性设置为空数组以显示默认菜单。
移除高亮
如果你需要从特定元素中移除高亮,可以通过发出 REMOVE_HIGHLIGHT
事件并提供你想移除的高亮的 id
来实现。例如
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT, REMOVE_HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const RemoveHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
id: 'my-unique-id',
selectors: ['header', 'section', 'footer'],
});
emit(REMOVE_HIGHLIGHT, 'my-unique-id');
return storyFn();
},
],
};
从 useChannel
API Hook 派生的 emit
函数在 Storybook 的 UI 中创建了一个通信通道,用于监听事件并相应地更新 UI。高亮功能使用此通道监听自定义事件并相应地更新高亮元素(如果有)。
重置高亮元素
默认情况下,Storybook 在故事之间切换时会自动移除高亮元素。但是,如果你需要手动清除它们,可以从故事或附加组件中发出 RESET_HIGHLIGHT
事件。这将移除所有高亮,包括由其他插件创建的高亮。例如
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT, RESET_HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const ResetHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(RESET_HIGHLIGHT); //👈 Remove previously highlighted elements
emit(HIGHLIGHT, {
selectors: ['header', 'section', 'footer'],
});
return storyFn();
},
],
};
滚动元素到视图中
高亮功能允许你将元素滚动到视图中并高亮它。要启用它,请从故事或附加组件中发出 SCROLL_INTO_VIEW
事件。事件负载必须包含一个 selector 属性,其值为你想滚动到视图中的元素的选择器。此外,你可以提供一个 options
对象来自定义滚动行为。请参见上面的使用示例。
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { SCROLL_INTO_VIEW } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const ScrollIntoView: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(SCROLL_INTO_VIEW, '#footer');
return storyFn();
},
],
};
API
参数
此功能为 Storybook 贡献了以下 参数,在 highlight
命名空间下
disable
类型:boolean
禁用此功能的行为。如果你想为整个 Storybook 关闭此功能,你应该在你的主配置文件中这样做。
此参数最适用于在更具体的级别进行覆盖。例如,如果在项目级别将此参数设置为 true
,则可以通过在元数据(组件)或故事级别将其设置为 false
来重新启用它。
导出
此功能为 Storybook 贡献了以下导出
import { HIGHLIGHT, REMOVE_HIGHLIGHT, RESET_HIGHLIGHT, SCROLL_INTO_VIEW } from 'storybook/highlight';
HIGHLIGHT
一个用于高亮 DOM 元素的事件。事件负载必须包含一个 selectors
属性,该属性被赋值为一个选择器数组,用于匹配你想高亮的元素。它可以扩展一个包含额外配置选项的可选对象。请参见上面的使用示例。
import { HIGHLIGHT, type HighlightOptions } from 'storybook/highlight';
channel.emit(
HIGHLIGHT,
options // The available configuration options inheriting from the HighlightOptions API
);
options
对象包含以下属性
interface HighlightOptions {
/** Unique identifier for the highlight, required if you want to remove the highlight later */
id?: string;
/** HTML selectors of the elements */
selectors: string[];
/** Priority of the highlight, higher takes precedence, defaults to 0 */
priority?: number;
/** CSS styles to apply to the highlight */
styles?: Record<string, string>;
/** CSS styles to apply to the highlight when it is hovered */
hoverStyles?: Record<string, string>;
/** CSS styles to apply to the highlight when it is focused or selected */
focusStyles?: Record<string, string>;
/** Keyframes required for animations */
keyframes?: string;
/** Groups of menu items to show when the highlight is selected */
menu?: HighlightMenuItem[][];
}
interface HighlightMenuItem {
/** Unique identifier for the menu item */
id: string;
/** Title of the menu item */
title: string;
/** Description of the menu item */
description?: string;
/** Icon for the menu item, left side */
iconLeft?: "chevronLeft" | "chevronRight" | "info" | "shareAlt";
/** Icon for the menu item, right side */
iconRight?: "chevronLeft" | "chevronRight" | "info" | "shareAlt";
/** Name for a channel event to trigger when the menu item is clicked */
clickEvent?: string;
/** HTML selectors for which this menu item should show (subset of HighlightOptions['selectors']) */
selectors?: HighlightOptions['selectors'];
}
菜单项可以指定一个 clickEvent
,当项目被点击时会在通道上发出。通道事件将接收两个参数:菜单项的 id
以及一个包含以下属性的 ClickEventDetails
对象
interface ClickEventDetails {
// Position and dimensions of the element on the page
top: number;
left: number;
width: number;
height: number;
// Selector(s) which matched the element
selectors: string[];
// DOM element details
element: {
attributes: Record<string, string>;
localName: string;
tagName: string;
outerHTML: string;
};
}
要监听此事件(假设 clickEvent: 'MY_CLICK_EVENT'
)
import type { ClickEventDetails } from 'storybook/highlight';
const handleClickEvent = (itemId: string, details: ClickEventDetails) => {
// Handle the menu item click event
}
// When you have a channel instance:
channel.on('MY_CLICK_EVENT', handleClickEvent)
// Or from a decorator:
useChannel({
MY_CLICK_EVENT: handleClickEvent,
}, [handleClickEvent])
REMOVE_HIGHLIGHT
一个移除先前创建的高亮的事件。事件负载必须包含一个 id
属性,其值为你想移除的高亮的 ID。请参见上面的使用示例。
import { REMOVE_HIGHLIGHT } from 'storybook/highlight';
channel.emit(
REMOVE_HIGHLIGHT,
id // The id of the previously created highlight to be removed
);
RESET_HIGHLIGHT
一个清除所有高亮元素的事件。请参见上面的使用示例。
import { RESET_HIGHLIGHT } from 'storybook/highlight';
channel.emit(RESET_HIGHLIGHT);
SCROLL_INTO_VIEW
一个将 DOM 元素滚动到视图中并短暂高亮的事件。事件负载必须包含一个 selector 属性,其值为你想滚动到视图中的元素的选择器。此外,你可以提供一个 options
对象来自定义滚动行为。请参见上面的使用示例。
import { SCROLL_INTO_VIEW } from 'storybook/highlight';
channel.emit(
SCROLL_INTO_VIEW,
selector // Element selector to scroll into view
options // An object inheriting from ScrollIntoViewOptions API to customize the scroll behavior
);