# Contributing

# 关于

这是一个基于@radix-ui/primitive 二次封装的组件库，目标是通过 css 变量的形式，实现多主题，以达到同时支持多品牌站点开发的能力。

# 代码文件结构

```shell
apps
├── playground
packages
├── storybook
│   └── public
└── ui
    └── src
        ├── components
        ├── helpers
        ├── icons
        ├── props
        ├── stories
        └── styles

```

| 路径                       | 描述                      |
| -------------------------- | ------------------------- |
| apps/playground            | playground,一个nextjs项目 |
| packages/storybook         | storybook 配置            |
| packages/ui                | 组件根目录                |
| packages/ui/src/components | 所有的组件都在这个目录    |
| packages/ui/src/stories    | 组件对应的storybook 文件  |
| packages/ui/src/styles     | 样式文件                  |

# 运行

本地开发

先clone 代码到本地

```shell
git clone
```

安装依赖

```shell
pnpm install
```

如果本地没有pnpm , 需要先安装

```shell
npm install -g pnpm
```

ui 如果需要添加新的依赖， 其他package 同理，替换后面的 @anker-in/headless-ui 为对应的package name

```shell
pnpm add {packages} --filter @anker-in/headless-ui
```

进入根目录

````
本地启动

```shell
pnpm run dev
````

这会运行storybook和playground，可以一边修改组件源码，一边查看效果。

# 如何新增组件

## 1. 编写组件

新增组件之前先看下仓库目录`packages/ui/src/components`有没有类似的组件已经存在。
如果不存在，需要新增组件。

**建议新组件基于 [@radix-ui/primitive](https://www.radix-ui.com/primitives) 或者手动复制[shadcn](https://ui.shadcn.com/) 的组件 进行二次封装。**

组件目录位于`packages/ui/src`。

运行`pnpm run dev` 之后，会运行storybook 和 playground 项目，在storybook 中测试组件的功能，编写文档。
在playground 中可以添加组件的展示，测试组件在nextjs 项目中的使用。

组件需要覆盖全品牌，如果新增css 变量，需要在`packages/ui/src/styles/global.css` 补充在各个品牌的作用域之下。
如果某个品牌没有添加，会默认使用root 下的定义作为fallback。

比如新的组件`Card`
需要新加一个Card 的背景颜色

```css
--card-bg-color: #fefefe;
```

请在不同的品牌的作用域下添加
比如

```css
:root[data-theme='anker'] {
  --card-bg-color: #fefefe;
}
```

## 2. 完善组件的storybook

如果你还不知道storybook 是什么，请查看[storybook入门介绍](https://juejin.cn/post/7145482546363990029)。  
storybook 会发布成一个[网站](https://headless-ui-doc.netlify.app/)，提供给所有的前端开发来查阅组件的用法，所以请尽量完善组件props 和 描述信息。  
在`packages/ui/src/stories`文件夹下给新加的组件添加一个`stories.tsx`文件。  
补充组件对应的props 定义，这会在对应的组件页面生成一个可以预览组件的表单。

## 3. 编写组件的单元测试

在`packages/ui/tests` 目录下添加组件对应的测试文件。  
单测内容包括功能和Ui。  
使用的单测库是 [`@testing-library/react`](https://testing-library.com/docs/react-testing-library/intro)。

功能测试比如添加的click 事件会不会触发。
UI 测试（重点）需要记录快照。 [什么是快照测试](https://jestjs.io/zh-Hans/docs/snapshot-testing)。  
人工做UI检查非常不可控，为了避免UI层被误修改，每个组件都需要做快照测试。  
比如:

```tsx
test('renders heading with text content', () => {
  const { getByRole } = render(<Heading>Test</Heading>)
  expect(getByRole('heading')).toMatchSnapshot()
})
```

如果确实是ui 上需要更新，需要人为手动更新下快照:

```shell
pnpm --filter=@anker-in/headless-ui run test:snapshots:update
```

这会更新快照文件。

_在每次提交代码（`git commit`）的时候，本地会自动跑一边单测，有不通过的单测会阻止代码提交_

## 4. 在playground 编写组件的预览

在目录`apps/playgrounds/app/components-overview` 目录下 添加一个 `Section{你的组件名}`的文件，加到此目录对应的页面中。  
用表格或列表的形式把组件所有的样式和变体都展示出来，方便设计师查看和检查。
playground 也会发布成一个[网站](https://headless-ui-playground.netlify.app/components)。
这里可以参考radix-ui 的排版 https://www.radix-ui.com/themes/playground

## 5. 在playground 编写组件的文档页面

[WIP]: 目前用的是app 路由加上写死的组件预览页面，合理的设计应该是只用写markdown 和 demo ，用动态路由&渲染的方式实现预览文档。
[headless-ui-playground](https://headless-ui-playground.netlify.app)

## 6. 提交MR

我们使用changeset 自动管理changelog 和版本号的更新, 在提交MR 之前需要先执行一下

`pnpm changeset add `
空格选择要发布的包 @anker-in/headless-ui  
回车

会依次出现要增加的语意版本号  
`{major -> minor -> patch}`  
这里如果只增加patch 可以一路回车。

这会在changesets 目录下生成对应的提交信息。后续发布版本的时候，会自动生成changelog, 这个changelog 方便追踪项目的更新记录。

然后push代码，提交MR。

根据MR 的模版编写此MR包含的提交内容,找其他的前端开发帮你review 代码，通过后即可合并到main 分支。

## 7. 发布

我们通过coding 的持续集成发布代码。对应的jenkin 文件是`Jenkinsfile`。  
有三个可选项`DEPLOY_NPM*`,`DEPLOY_DOC*`,`DEPLOY_SITE*`, 分别是ui 库本身的npm 包，storybook 和 playground,
需要发布哪一个就把变量值设置为Y。
点击确定就会自动构建和发布。

![alt text](image.png)

⚠️注意
组件编写后请按照checklist 进行检查

## checklist

- [ ] 已经为组件编写了storybook API文档
- [ ] 组件在storybook accessibility 通过测试
- [ ] 完成组件的单元测试
- [ ] 组件在不同品牌下的样式正常显示
- [ ] 组件在不同设备，分辨率下显示正常
- [ ] 组件不包含业务代码，包括文案，接口等

# 分支规范

git flow 和code review

1. 主分支 (main)：存储正式发布的历史。
2. 开发分支 (develop)：集成各种功能的分支。
3. 功能分支 (feature/x)：从develop分支出来，用于新功能的开发。
4. 修补分支 (hotfix/x)：从main分支出来，用于快速修补生产环境的问题。

# 代码规范

请遵循 eslint 的配置编写代码。

提交代码前运行lint

```shell
pnpm lint
```

如果遇到eslint 报错，但是坚持自己的写法的，可以通过注释的方式，阻止eslint 校验, 并且要写明阻止的规则
比如

```js
// eslint-disable-next-line no-alert
alert('Example of disabled line for no-alert')
```

如果希望修改eslint 规则，修改根目录下的`.eslintrc.json`

❗ 在ui/目录下的代码导入的时候，要带上.js后缀，比如 import { cn } from '../helpers/index.js' 否则在使用的时候会出现 ERR_UNSUPPORTED_DIR_IMPORT 问题

# Tesing

unit test 使用 [jest](https://jestjs.io/) 和 [@test-library/react](https://testing-library.com/)。

运行单元测试

```shell
pnpm test
```

# Deploy

到`coding`网站的持续集成找到项目，手动触发ci/cd。
