关注【索引目录】服务号,更多精彩内容等你来探索!
设置
首先,通过在终端中运行以下命令来完成初始故事书配置的设置:npx storybook init。
此命令将检测项目依赖项并显示三个步骤:
storybook指示将添加哪个版本 -
询问项目是否使用 webpack或vite -
询问是否 storybook用于文档或测试
关于第一点,请继续。对于第二点,该项目使用rollup,因此实际上两个选项都不直接适用,但由于storybook它没有与 直接集成rollup,vite因此将选择 选项,该选项将用于运行storybook。对于最后一个问题,请指定 storybook 将用于文档。
完成此过程后,将自动添加允许运行 storybook 的库。
在撰写本文时,它生成了以下版本:
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-onboarding": "^8.6.12",
"@storybook/blocks": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-vite": "^8.6.12",
"@storybook/test": "^8.6.12",
"eslint-plugin-storybook": "^0.12.0",
"storybook": "^8.6.12",
并且在 中package.json会自动添加两个脚本:
"scripts": {
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
第一个脚本在本地运行故事书,第二个脚本构建故事书。
它还会生成一个.storybook文件夹,其中包含两个允许 Storybook 运行的配置文件:main.ts和preview.ts。
此外,stories还会创建一个文件夹,其中包含一些示例文档。由于我们不会使用它,因此将删除此文件夹。
这样,如果应用使用 Vite,storybook 就已经成功配置了。但是,由于它使用了 Rollup,因此需要添加两个库才能使其成功运行:
yarn add vite @storybook/builder-vite --dev
在撰写本文时,已生成以下版本:
"@storybook/builder-vite": "^8.6.12",
"vite": "^6.3.5",
组件修改
在继续文档之前,我们将对库组件进行调整,定义默认属性并进行相应的调整。
从以下内容开始Tag.tsx:
-
定义默认 props 值并删除 textWeight(因为已经有一个属性可以自定义控制文本的字体粗细:)textFontWeight:
// ...
const Tag = ({
text = "Tag",
type = "default",
textColor = "#fff",
textFontWeight = 600,
textFontSize,
textFontFamily,
backgroundColor,
format = "semiRounded",
borderRadius,
size = "medium",
padding,
}: TagProps) => {
-
调整组件的类型定义,因为 textWeight不再是组件中的 props,默认format是 semiRounded:
// ...
export interface TagProps {
type?: "default" | "success" | "alert" | "error";
text: string;
textColor?: string;
textFontWeight?: number;
textFontSize?: string;
textFontFamily?: string;
backgroundColor?: string;
format?: "square" | "semiRounded" | "rounded";
borderRadius?: string;
size?: "small" | "medium" | "large";
padding?: string;
}
export interface StyledTagProps {
$type?: "default" | "success" | "alert" | "error";
$textColor?: string;
$textFontWeight?: number;
$textFontSize?: string;
$textFontFamily?: string;
$backgroundColor?: string;
$format?: "square" | "semiRounded" | "rounded";
$borderRadius?: string;
$size?: "small" | "medium" | "large";
$padding?: string;
}
-
使用 styled-components 调整属性定义,以更清楚地考虑 small size、 defaulttype和 squareformat:
// ...
export const StyledTag = styled.div<StyledTagProps>`
border: none;
padding: ${(props) =>
props.$padding
? props.$padding
: props.$size === "large"
? "15px 20px"
: props.$size === "medium"
? "10px 12px"
: props.$size === "small" && "7px"};
background-color: ${(props) =>
props.$backgroundColor
? props.$backgroundColor
: props.$type === "error"
? "#e97451"
: props.$type === "alert"
? "#f8de7e"
: props.$type === "success"
? "#50c878"
: props.$type === "default" && "#d3d3d3"};
pointer-events: none;
border-radius: ${(props) =>
props.$borderRadius
? props.$borderRadius
: props.$format === "rounded"
? "30px"
: props.$format === "semiRounded"
? "5px"
: props.$format === "square" && "0"};
width: fit-content;
`;
-
在组件的渲染中进行调整,根据 textFontSizeprops或者sizeprops设置文本字体大小:
// ...
}: TagProps) => {
const fontSize = textFontSize
? textFontSize
: size === "large"
? "18px"
: size === "small"
? "14px"
: "16px";
return (
<StyledTag
data-testid="tag"
$type={type}
$backgroundColor={backgroundColor}
$format={format}
$borderRadius={borderRadius}
$size={size}
$padding={padding}
>
<Text
color={textColor}
fontWeight={textFontWeight}
fontSize={fontSize}
fontFamily={textFontFamily}
>
{text}
</Text>
</StyledTag>
);
};
由于组件的默认属性Tag已更改,因此需要更新其测试文件Tag.test.tsx:
-
调整 expect第一个测试中的语句以验证默认属性:
it("should render component with default properties", () => {
render(<Tag text="Tag" />);
const element = screen.getByTestId("tag");
expect(element).toBeInTheDocument();
expect(element).toHaveStyleRule("background-color", "#d3d3d3");
expect(element).toHaveStyleRule("border-radius", "0");
expect(element).toHaveStyleRule("padding", "7px");
expect(within(element).getByText("Tag")).toHaveStyleRule("color", "#fff");
});
-
删除测试 should render component with bold text font weight,因为它所依赖的 props 已被删除。
现在关于Text.tsx:
-
默认 prop 值的定义:
// ...
const Text = ({
children,
color="#000",
weight="normal",
fontWeight,
fontSize="16px",
fontFamily,
}: TextProps) => (
-
使用 styled-components 调整属性定义,以便在某些情况下仅当提供相关 props 时才设置属性:
// ...
export const StyledText = styled.span<StyledTextProps>`
${(props) => props.$color && `color: ${props.$color};`}
${(props) => props.$fontSize && `font-size: ${props.$fontSize};`}
font-weight: ${(props) =>
props.$fontWeight
? props.$fontWeight
: props.$weight
? props.$weight
: "normal"};
${(props) => props.$fontFamily && `font-family: ${props.$fontFamily};`}
`;
组件文档
对于组件文档,将为每个组件创建两种类型的文件:
-
故事:定义文档中显示的定制场景 -
mdx:组件文档,其中将写入文本并嵌入故事文件中的场景
在进入文档之前,我们将创建一个组件,用于集中文档中渲染的各个组件,并在它们之间添加一个小空间。组件StorybookContainer内将创建一个名为 的文件夹src/components:
-
文件 StorybookContainer.tsx:
import React from "react";
import styled from "styled-components";
export interface StorybookContainerProps {
children: React.ReactNode;
}
export const StyledStorybookContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
`;
const StorybookContainer = ({ children }: StorybookContainerProps) => (
<StyledStorybookContainer>
{children}
</StyledStorybookContainer>
);
export default StorybookContainer;
-
文件 index.ts:
export { default } from "./StorybookContainer";
现在,我们可以通过在文件夹中创建以下文件来开始记录 Tag 组件src/components/Tag:
-
标签.stories.tsx
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import Tag from "./Tag";
import StorybookContainer from "../StorybookContainer/StorybookContainer";
const meta: Meta<typeof Tag> = {
title: "Tag",
component: Tag,
argTypes: {},
};
export default meta;
type Story = StoryObj<typeof Tag>;
export const Default: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
</StorybookContainer>
),
};
export const PredefinedType: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} type="success" />
<Tag {...args} type="alert" />
<Tag {...args} type="error" />
</StorybookContainer>
),
};
export const PredefinedFormat: Story = {
args: {
format: "square",
},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} format="semiRounded" />
<Tag {...args} format="rounded" />
</StorybookContainer>
),
};
export const PredefinedSize: Story = {
args: {
size: "small",
},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} size="medium" />
<Tag {...args} size="large" />
</StorybookContainer>
),
};
export const Text: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} text="Example" />
</StorybookContainer>
),
};
export const BackgroundColor: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} backgroundColor="#3b82f6" />
</StorybookContainer>
),
};
export const CustomFormat: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} borderRadius="15px 2px" />
</StorybookContainer>
),
};
export const CustomSize: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} padding="20px 40px" />
</StorybookContainer>
),
};
export const TextColor: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} textColor="#000" />
</StorybookContainer>
),
};
export const TextFontWeight: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} textFontWeight={100} />
</StorybookContainer>
),
};
export const TextFontFamily: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} textFontFamily="Arial" />
</StorybookContainer>
),
};
export const TextFontSize: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Tag {...args} />
<Tag {...args} textFontSize="20px" />
</StorybookContainer>
),
};
在其中的 下Meta,定义了要记录的组件,以及将出现在 Storybook 侧边栏菜单中的标题。每个标题都export const定义了一个将包含在文档中的场景。引用两个示例场景,第一个场景Default将使用其默认属性渲染组件。第二个场景PredefinedType将展示组件如何根据type属性的变化进行操作。
-
标签.mdx
import { Canvas, Controls, Meta } from "@storybook/blocks";
import * as Stories from "./Tag.stories";
<Meta of={Stories} />
# Tag
Tag base component.
<Canvas of={Stories.Default} withToolbar />
<Controls of={Stories.Default} />
## Predefined properties
### Type
There are four type predefined properties: default, success, alert and error.
<Canvas of={Stories.PredefinedType} withToolbar />
### Format
There are three format predefined properties: square, semiRounded (default) and rounded.
<Canvas of={Stories.PredefinedFormat} withToolbar />
### Size
There are three size predefined properties: small, medium (default) and large.
<Canvas of={Stories.PredefinedSize} withToolbar />
## Custom properties
### Text
Tag text can be modified.
<Canvas of={Stories.Text} withToolbar />
### Background Color
Tag background color can be modified.
<Canvas of={Stories.BackgroundColor} withToolbar />
### Format
Tag format can be modified by border radius definition.
<Canvas of={Stories.CustomFormat} withToolbar />
### Size
Tag size can be modified by padding definition.
<Canvas of={Stories.CustomSize} withToolbar />
### Text Color
Tag text color can be modified.
<Canvas of={Stories.TextColor} withToolbar />
### Text Font Weight
Tag text font weight can be modified.
<Canvas of={Stories.TextFontWeight} withToolbar />
### Text Font Family
Tag text font family can be modified.
<Canvas of={Stories.TextFontFamily} withToolbar />
### Text Font Size
Tag text font size can be modified.
<Canvas of={Stories.TextFontSize} withToolbar />
在上述文件中,<Canvas of={Stories.Default} withToolbar />使用其场景渲染组件Default。下方,<Controls of={Stories.Default} />显示一个表格,其中包含组件所有可供自定义的 props。通过调整这些 props,可以实时查看它们如何影响表格上方的组件。
在 中Predefined properties,显示了基于修改后的 props,应用程序本身定义关联的 CSS 的场景。
在中Custom properties,显示了修改后的 props 直接影响已定义的 CSS 的场景。
使用以下方式在本地运行故事书yarn storybook:
现在,通过在文件夹中添加以下文件来创建文本组件的文档src/components/Text:
-
文本.故事.tsx
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import Text from "./Text";
import StorybookContainer from "../StorybookContainer/StorybookContainer";
const meta: Meta<typeof Text> = {
title: "Text",
component: Text,
argTypes: {},
};
export default meta;
type Story = StoryObj<typeof Text>;
export const Default: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Text {...args}>Text</Text>
</StorybookContainer>
),
};
export const PredefinedFontWeight: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Text {...args}>Text</Text>
<Text {...args} weight="bold">
Text
</Text>
</StorybookContainer>
),
};
export const Color: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Text {...args}>Text</Text>
<Text {...args} color="#800080">
Text
</Text>
</StorybookContainer>
),
};
export const CustomFontWeight: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Text {...args}>Text</Text>
<Text {...args} fontWeight={900}>
Text
</Text>
</StorybookContainer>
),
};
export const FontSize: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Text {...args}>Text</Text>
<Text {...args} fontSize="30px">
Text
</Text>
</StorybookContainer>
),
};
export const FontFamily: Story = {
args: {},
render: (args) => (
<StorybookContainer>
<Text {...args}>Text</Text>
<Text {...args} fontFamily="Arial">
Text
</Text>
</StorybookContainer>
),
};
-
文本.mdx
import { Canvas, Controls, Meta } from "@storybook/blocks";
import * as Stories from "./Text.stories";
<Meta of={Stories} />
# Text
Text base component.
<Canvas of={Stories.Default} withToolbar />
<Controls of={Stories.Default} />
## Predefined properties
### Font Weight
There are two font weight predefined properties: normal(default) and bold.
<Canvas of={Stories.PredefinedFontWeight} withToolbar />
## Custom properties
### Color
Text color can be modified.
<Canvas of={Stories.Color} withToolbar />
### Font Weight
Text font weight can be modified.
<Canvas of={Stories.CustomFontWeight} withToolbar />
### Font Size
Text font size can be modified.
<Canvas of={Stories.FontSize} withToolbar />
### Font Family
Text font family can be modified.
<Canvas of={Stories.FontFamily} withToolbar />
它看起来如下:
部署故事书
现在,我们已经为组件编写了文档,Storybook 也已在本地成功运行。但我们的目标并非是让文档仅供库的开发人员使用——它应该能够向考虑添加库的用户展示库提供的组件。为了实现这一点,我们将使用 Chromatic 来部署文档并使其易于访问,这是 Storybook 维护人员提供的一项发布服务。首先,我们将添加库:
yarn add chromatic --dev
在撰写本文时,它生成了以下版本:
"chromatic": "^12.0.0",
现在,按照以下步骤进行部署:
-
前往注册页面 -
选择 Connect with GitHub(假设您的存储库托管在那里) -
注册后,选择 Choose GitHub repo -
从您的 github 帐户中选择您的库的存储库。 -
复制并保存下面显示的命令 Publish your Storybook。此时,如果不在终端中运行此命令,您将无法继续,但在此之前,让我们先构建故事书 -
在终端中运行 yarn build-storybook -
然后,在终端中,运行 chromatic 提供的命令(每次需要部署时,都必须在构建后执行此命令)
一旦该过程成功完成,终端将确认 storybook 已发布,并在 下显示一个 URL View your Storybook at "url"。
通过访问此 URL,您将能够查看组件文档。
自述文件.md
现在故事书已经发布,它所在的 URL 将被添加到应用程序的 README 中(我将在此处包含我生成的 URL,对于您来说,您应该在该位置插入您的 URL):
.gitignore 文件
在 .gitignore 文件中,storybook-static将添加要忽略的文件夹,因为它是在运行时生成的yarn build-storybook:
dist
node_modules
*storybook.log
storybook-static
包.json
package.json由于新版本的库即将发布,因此里面的版本将更改为 0.5.0:
{
"name": "react-example-lib",
"version": "0.5.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/griseduardo/react-example-lib.git"
},
"scripts": {
"build": "rollup -c --bundleConfigAsCjs",
"lint-src": "eslint src",
"lint-src-fix": "eslint src --fix",
"lint-fix": "eslint --fix",
"format-src": "prettier src --check",
"format-src-fix": "prettier src --write",
"format-fix": "prettier --write",
"test": "jest",
"prepare": "husky",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"lint-staged": {
"src/components/**/*.{ts,tsx}": [
"yarn lint-fix",
"yarn format-fix"
],
"src/components/**/*.tsx": "yarn test --findRelatedTests --bail"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@eslint/js": "^9.19.0",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "11.1.6",
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-onboarding": "^8.6.12",
"@storybook/blocks": "^8.6.12",
"@storybook/builder-vite": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-vite": "^8.6.12",
"@storybook/test": "^8.6.12",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/jest": "^29.5.14",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"chromatic": "^12.0.0",
"eslint": "^9.19.0",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-storybook": "^0.12.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-styled-components": "^7.2.0",
"lint-staged": "^15.5.0",
"prettier": "^3.4.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rollup": "^4.30.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"storybook": "^8.6.12",
"styled-components": "^6.1.14",
"typescript": "^5.7.3",
"typescript-eslint": "^8.23.0",
"vite": "^6.3.5"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"styled-components": "^6.1.14"
},
"eslintConfig": {
"extends": [
"plugin:storybook/recommended"
]
}
}
CHANGELOG 文件
由于即将发布新版本,因此CHANGELOG.md将更新已更改的内容:
## 0.5.0
_May. 29, 2025_
- change Tag and Text default behavior
- add storybook
- add Tag and Text storybook docs
## 0.4.0
_Abr. 29, 2025_
- setup husky and lint-staged
- define pre-commit actions
## 0.3.0
_Mar. 24, 2025_
- setup jest and testing-library
- add components tests
## 0.2.0
_Fev. 24, 2025_
- setup typescript-eslint and prettier
- add custom rules
## 0.1.0
_Jan. 29, 2025_
- initial config
文件夹结构
文件夹结构如下:
发布新版本
第一步是检查汇总构建是否成功运行。为此,请yarn build在终端中运行,如 中所述package.json。如果构建成功,请继续使用以下命令发布新版本的库:npm publish --access public
结论
本文的目的是设置 Storybook,为应用程序中的组件创建文档并进行部署,因为文档对于展示库提供的组件非常重要。以下是GitHub上的代码库以及npmjs上包含新修改的库。

