Skip to content

Commit 496d281

Browse files
committed
chore: storybook docs
1 parent 9859100 commit 496d281

File tree

12 files changed

+1104
-1734
lines changed

12 files changed

+1104
-1734
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ coverage
2424

2525
*storybook.log
2626
storybook-static
27+
.vite-cache
2728
/dist-lib
2829
/dist-vite
2930
/dist-vite-zip

.storybook/main.ts

Lines changed: 45 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,57 @@
1-
import type { StorybookConfig } from '@storybook/react-webpack5'
1+
import type { StorybookConfig } from '@storybook/react-vite'
22
import path from 'path'
3+
import { mergeConfig } from 'vite'
4+
35
const rootDir = process.cwd()
6+
47
const config: StorybookConfig = {
58
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
6-
addons: ['@storybook/addon-webpack5-compiler-swc', '@storybook/addon-a11y', '@storybook/addon-docs'],
9+
addons: [
10+
'@storybook/addon-links', // 推荐加上:支持故事间跳转
11+
'@storybook/addon-a11y', // 保持
12+
'@storybook/addon-docs', // 如果你用了 MDX3,可以去掉(essentials 已包含)
13+
],
714
docs: {},
8-
framework: '@storybook/react-webpack5',
9-
webpackFinal: async (config) => {
10-
config.resolve = config.resolve || {}
11-
config.resolve.alias = {
12-
...(config.resolve.alias || {}),
13-
'@': path.resolve(rootDir, 'src'),
14-
'@src/*': path.resolve(rootDir, 'src/*'),
15-
'@stateless/*': path.resolve(rootDir, 'src/components/stateless/*'),
16-
'@stateful/*': path.resolve(rootDir, 'src/components/stateful/*'),
17-
'@hooks/*': path.resolve(rootDir, 'src/components/hooks/*'),
18-
'@app-hooks/*': path.resolve(rootDir, 'src/app-hooks/*'),
19-
'@container/*': path.resolve(rootDir, 'src/components/container/*'),
20-
'@assets/*': path.resolve(rootDir, 'src/assets/*'),
21-
'@pages/*': path.resolve(rootDir, 'src/pages/*'),
22-
'@routers/*': path.resolve(rootDir, 'src/routers/*'),
23-
'@utils/*': path.resolve(rootDir, 'src/utils/*'),
24-
'@theme/*': path.resolve(rootDir, 'src/theme/*'),
25-
}
26-
// If building for a subpath (e.g. /pro-react-admin/storybook/)
27-
// you can set the STORYBOOK_BASE_HREF env var when running the build.
28-
// This will make webpack emit assets with the correct publicPath.
29-
// Example: STORYBOOK_BASE_HREF=/pro-react-admin/storybook/ npm run build-storybook
30-
// const baseHref = process.env.STORYBOOK_BASE_HREF;
31-
// if (baseHref) {
32-
// config.output = config.output || {};
33-
// // webpack expects a string, ensure it ends with a slash
34-
// config.output.publicPath = baseHref.endsWith('/') ? baseHref : baseHref + '/';
35-
// }
36-
37-
// add Less and Less Module support for project styles
38-
config.module = config.module || { rules: [] }
39-
40-
const excludeProjectStyleFromDefaultRules = (rules: any[]) => {
41-
const visit = (rule: any) => {
42-
if (!rule) return
43-
if (Array.isArray(rule.oneOf)) rule.oneOf.forEach(visit)
44-
if (Array.isArray(rule.rules)) rule.rules.forEach(visit)
45-
46-
const test = rule.test
47-
const testStr = typeof test?.toString === 'function' ? test.toString() : ''
48-
49-
// Storybook may have implicit style loaders. Exclude project-handled patterns
50-
// to prevent double-processing (which can break CSS Modules output).
51-
if (testStr.includes('\\.css')) {
52-
const moduleCss = /\.module\.css$/
53-
if (!rule.exclude) {
54-
rule.exclude = moduleCss
55-
} else if (Array.isArray(rule.exclude)) {
56-
rule.exclude = [...rule.exclude, moduleCss]
57-
} else {
58-
rule.exclude = [rule.exclude, moduleCss]
59-
}
60-
}
61-
62-
if (testStr.includes('\\.less')) {
63-
const allLess = /\.less$/
64-
if (!rule.exclude) {
65-
rule.exclude = allLess
66-
} else if (Array.isArray(rule.exclude)) {
67-
rule.exclude = [...rule.exclude, allLess]
68-
} else {
69-
rule.exclude = [rule.exclude, allLess]
70-
}
71-
}
72-
}
73-
74-
rules.forEach(visit)
75-
}
76-
77-
const cssModuleRule = {
78-
test: /\.module\.css$/,
79-
use: [
80-
'style-loader',
81-
{
82-
loader: 'css-loader',
83-
options: {
84-
modules: { localIdentName: '[local]__[hash:base64:5]' },
85-
importLoaders: 1,
86-
},
15+
framework: {
16+
name: '@storybook/react-vite',
17+
options: {},
18+
},
19+
viteFinal: async (viteConfig) =>
20+
mergeConfig(viteConfig, {
21+
resolve: {
22+
alias: {
23+
'@': path.resolve(rootDir, 'src'),
24+
'@src/*': path.resolve(rootDir, 'src/*'),
25+
'@stateless/*': path.resolve(rootDir, 'src/components/stateless/*'),
26+
'@stateful/*': path.resolve(rootDir, 'src/components/stateful/*'),
27+
'@hooks/*': path.resolve(rootDir, 'src/components/hooks/*'),
28+
'@app-hooks/*': path.resolve(rootDir, 'src/app-hooks/*'),
29+
'@container/*': path.resolve(rootDir, 'src/components/container/*'),
30+
'@assets/*': path.resolve(rootDir, 'src/assets/*'),
31+
'@pages/*': path.resolve(rootDir, 'src/pages/*'),
32+
'@routers/*': path.resolve(rootDir, 'src/routers/*'),
33+
'@utils/*': path.resolve(rootDir, 'src/utils/*'),
34+
'@theme/*': path.resolve(rootDir, 'src/theme/*'),
8735
},
88-
'postcss-loader',
89-
],
90-
}
91-
92-
const lessModuleRule = {
93-
test: /\.module\.less$/,
94-
use: [
95-
'style-loader',
96-
{
97-
loader: 'css-loader',
98-
options: {
99-
modules: { localIdentName: '[local]__[hash:base64:5]' },
100-
importLoaders: 2,
36+
},
37+
css: {
38+
preprocessorOptions: {
39+
less: {
40+
javascriptEnabled: true,
10141
},
10242
},
103-
'postcss-loader',
104-
{
105-
loader: 'less-loader',
106-
options: { lessOptions: { javascriptEnabled: true } },
107-
},
108-
],
109-
}
110-
111-
const lessRule = {
112-
test: /\.less$/,
113-
exclude: /\.module\.less$/,
114-
use: [
115-
'style-loader',
116-
'css-loader',
117-
'postcss-loader',
118-
{
119-
loader: 'less-loader',
120-
options: { lessOptions: { javascriptEnabled: true } },
43+
},
44+
cacheDir: path.resolve(rootDir, '.vite-cache/storybook'),
45+
optimizeDeps: {
46+
esbuildOptions: {
47+
target: 'es2020',
12148
},
122-
],
123-
}
49+
},
12450

125-
// Prevent Storybook's default style rules from also processing project-handled patterns.
126-
// IMPORTANT: only apply these excludes to the existing/default rules, not the custom rules
127-
// we prepend below.
128-
const baseRules = config.module.rules || []
129-
excludeProjectStyleFromDefaultRules(baseRules)
130-
131-
// prepend to ensure project rules take precedence
132-
config.module.rules = [cssModuleRule, lessModuleRule, lessRule, ...baseRules]
133-
134-
return config
135-
},
51+
build: {
52+
chunkSizeWarningLimit: 2000,
53+
},
54+
}),
13655
}
56+
13757
export default config

.storybook/preview.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

.storybook/preview.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React, { Suspense } from 'react'
2-
import type { Preview } from '@storybook/react-webpack5'
3-
import { MemoryRouter } from 'react-router-dom'
4-
import { I18nextProvider } from 'react-i18next'
1+
// .storybook/preview.tsx
2+
import React, { Suspense } from 'react';
3+
import type { Preview } from '@storybook/react'; // 注意:Vite 版本用 @storybook/react
4+
import { MemoryRouter } from 'react-router-dom';
5+
import { I18nextProvider } from 'react-i18next';
56

6-
import i18n from '../src/i18n/i18n'
7+
import i18n from '../src/i18n/i18n';
78

89
const preview: Preview = {
910
decorators: [
@@ -17,14 +18,27 @@ const preview: Preview = {
1718
</I18nextProvider>
1819
),
1920
],
21+
2022
parameters: {
2123
controls: {
2224
matchers: {
2325
color: /(background|color)$/i,
2426
date: /Date$/i,
2527
},
2628
},
29+
// 推荐:加一些常用参数,提升体验
30+
options: {
31+
storySort: {
32+
order: ['Introduction', 'Components'], // 自定义故事排序
33+
},
34+
},
35+
// 如果用 addon-docs,推荐加
36+
docs: {
37+
canvas: {
38+
sourceState: 'shown', // 默认显示代码
39+
},
40+
},
2741
},
28-
}
42+
};
2943

30-
export default preview
44+
export default preview;

docs/components-index.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# components/index.ts 维护说明(组件库入口)
2+
3+
本项目的组件库构建入口是 `src/components/index.ts`
4+
5+
- `vite.config.lib.ts``build.lib.entry` 指向它
6+
- `npm run build:lib` 会基于它生成 `dist-lib/*`(包含 JS bundle 与 d.ts)
7+
8+
因此:**只有在 `src/components/index.ts` 里导出的组件/工具,才会成为对外可用的库 API**
9+
10+
## 什么时候需要改这个文件
11+
12+
当你新增了一个要对外发布的组件(或对外发布的 hooks / utils / types),需要:
13+
14+
1. 确保组件本体在 `src/components/stateless/*``src/components/stateful/*``src/components/*`(核心组件)下有可导入的入口文件(通常是 `index.tsx` / `index.ts` / `index.jsx`
15+
2.`src/components/index.ts` 增加对应的 `export`
16+
3. 运行一次 `npm run build:lib` 验证能生成类型与产物
17+
18+
> 注意:Storybook 的 `*.stories.*` / `*.mdx` **不应** 被导出,它们只是文档/演示。
19+
20+
## 推荐目录与导出约定
21+
22+
### 1) 组件实现位置
23+
24+
- UI/展示类:优先放 `src/components/stateless/<ComponentName>/`
25+
- 需要状态/数据处理:放 `src/components/stateful/<ComponentName>/`
26+
- 项目级“核心壳/容器”能力:放 `src/components/<ComponentName>/`(例如 `ErrorBoundary``KeepAlive` 等)
27+
28+
### 2) 入口文件
29+
30+
推荐每个组件目录提供一个稳定入口(至少一个):
31+
32+
- `index.tsx`:导出 React 组件(默认导出)
33+
- `index.ts`:导出类型、常量、hooks 等
34+
35+
保持一致的好处:`components/index.ts` 只需要写一行清晰的 re-export。
36+
37+
### 3) 对外导出的命名
38+
39+
- 默认导出组件:使用 `export { default as Xxx } from '...'`
40+
- 命名导出:使用 `export { Foo, Bar } from '...'``export * from '...'`
41+
42+
尽量保证导出名与目录名一致(`SmartVideoPlayer``stateless/SmartVideoPlayer`)。
43+
44+
## 修改 checklist(建议每次都跑)
45+
46+
1. `src/components/index.ts` 已新增 export
47+
2. `npm run check:components-index` 通过(避免漏加/写错导出路径)
48+
2. `npm run build:lib` 成功(产物 + d.ts)
49+
3. 如果该组件有样式(`.module.less/.css`),在消费侧能正常引入
50+
4. 如果该组件依赖路由/i18n 等上下文,在 Storybook 里可正常预览(可选,但建议)
51+
52+
## 常见问题
53+
54+
### Q1: 为什么我新增了组件但打出来的 lib 里没有?
55+
56+
因为 `vite.config.lib.ts` 的入口是 `src/components/index.ts`
57+
58+
- 你没在这里 export → Rollup 不会把它当作库 API 打包
59+
- dts 插件也不会把它作为入口类型导出到 `dist-lib/index.d.ts`
60+
61+
### Q2: 新增组件放在 `src/components/OneTimePasscode/` 里,为何还要从 `stateless/OneTimePasscode` export?
62+
63+
`src/components/OneTimePasscode/` 这种目录在当前项目里主要是 **Storybook 文档/演示(mdx/stories)**
64+
真正组件实现位于 `src/components/stateless/OneTimePasscode/`,所以库入口应该导出实现目录,而不是导出 docs。
65+
66+
### Q3: 以后这个文件会越来越大,怎么维护更舒服?
67+
68+
建议遵循两条简单规则:
69+
70+
1. **只导出“要发布的公共 API”**(组件/类型/hooks),避免把 demo、页面级组件、内部路由壳导出。
71+
2. **新增组件就顺手加一行 export + 跑 build:lib**,用构建作为“验收”。
72+
73+
如果后续你希望进一步自动化,可以新增一个脚本(可选):
74+
75+
- 扫描 `src/components/stateless/*``src/components/stateful/*`
76+
- 生成(或校验)`src/components/index.ts`
77+
78+
但因为当前存在少量特殊导出(例如非 default 导出、注释掉的 export),完全自动生成需要先统一约定;否则更推荐“半自动校验”而不是强制重写文件。

0 commit comments

Comments
 (0)