diff --git a/.github/workflows/pr-comment-ci.yml b/.github/workflows/pr-comment-ci.yml index bcca5e167..88cb71c0d 100644 --- a/.github/workflows/pr-comment-ci.yml +++ b/.github/workflows/pr-comment-ci.yml @@ -3,6 +3,8 @@ name: PR_COMMENT_CI on: issue_comment: types: [created] +env: + HUSKY: '0' jobs: check: @@ -11,29 +13,32 @@ jobs: next_action: ${{ steps.get-action.outputs.next_action }} if: ${{ github.event.issue.pull_request }} steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - uses: actions/github-script@v7 - id: get-action - with: + - uses: actions/checkout@v4 + with: + repository: Tencent/tdesign + sparse-checkout: | + .github/.pr-comment-ci-whitelist + sparse-checkout-cone-mode: false + + - uses: actions/github-script@v7 + id: get-action + with: script: | const user = context.payload.comment.user.login - core.debug(`user: ${user}`) + core.info(`user: ${user}`) - const fs = require('fs') - const CODEOWNERS = fs.readFileSync('.github/CODEOWNERS', 'utf8') - core.debug(`CODEOWNERS: ${CODEOWNERS}`) + const fs = require('fs'); + const whitelist = fs.readFileSync('.github/.pr-comment-ci-whitelist', 'utf8'); - let isReviewer = false; - CODEOWNERS.match(/@\w+/g).forEach((owner) => { - if (owner === `@${user}`) { - isReviewer = true + let isWhitelist = false; + whitelist.split('\n').forEach((owner) => { + if (owner === user) { + isWhitelist = true; } - }) + }); let next_action = '' - if (isReviewer) { + if (isWhitelist) { const body = context.payload.comment.body core.info(`body: ${body}`) if (body.startsWith('/update-common')) { @@ -41,13 +46,25 @@ jobs: } if (body.startsWith('/update-snapshot')) { next_action='update-snapshot' - } + } + if (body.startsWith('/update-coverage')) { + next_action='update-coverage' + } + + if(next_action){ + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'rocket', + }) + } } else { core.warning('You are not collaborator'); } core.info(`next_action: ${next_action}`) core.setOutput('next_action', next_action) - + update-common: needs: check runs-on: ubuntu-latest @@ -55,20 +72,74 @@ jobs: steps: - uses: actions/checkout@v4 with: + fetch-depth: 0 token: ${{ secrets.PERSONAL_TOKEN }} + - name: gh checkout pr env: GH_TOKEN: ${{ secrets.PERSONAL_TOKEN }} - run: gh pr checkout ${{ github.event.issue.number }} --recurse-submodules - - run: git submodule update --remote --merge - - name: Commit Common + run: gh pr checkout ${{ github.event.issue.number }} + + - name: git config run: | - git add . git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" - git commit -m "chore: update common" - git push - + + - name: submodule init and sync remote + run: | + git submodule update --init --remote + + - name: Commit common + run: | + git status + working_tree_clean=$(git status | grep -c 'nothing to commit, working tree clean') || true + if [ "$working_tree_clean" -eq "0" ]; then + git add . + git commit -m "chore: update common" + fi + git status + + - name: merge develop + run: | + git merge develop --no-commit || true + + - name: check conflicts + run: | + git status + conflict_count=$(git status | grep -c 'both modified:') || true + working_tree_clean=$(git status | grep -c 'nothing to commit, working tree clean') || true + common_conflict=$(git status | grep 'both modified:' | grep -c '_common') || true + conflicts_sum=$((common_conflict)) + echo "conflict_count: $conflict_count" + echo "working_tree_clean: $working_tree_clean" + echo "common_conflict: $common_conflict" + echo "conflicts_sum: $conflicts_sum" + + if [ "$working_tree_clean" -eq "1" ]; then + echo "nothing to commit, working tree clean" + exit 0 + fi + + if [ "$conflict_count" -gt "0" ]&&[ "$conflicts_sum" -eq "0" ]; then + echo "Unknown conflict " + git status + exit 1 + fi + + if [ "$common_conflict" -eq "1" ];then + git checkout --ours src/_common + git add src/_common + echo "resolve conflict _common" + fi + + git status + git commit -am "chore: merge develop" + + - name: git push + run: | + git status + git push || true + update-snapshot: needs: check runs-on: ubuntu-latest @@ -76,20 +147,163 @@ jobs: steps: - uses: actions/checkout@v4 with: + fetch-depth: 0 token: ${{ secrets.PERSONAL_TOKEN }} + - name: gh checkout pr env: GH_TOKEN: ${{ secrets.PERSONAL_TOKEN }} run: gh pr checkout ${{ github.event.issue.number }} --recurse-submodules + + - name: bot commtent + id: bot-comment + uses: actions/github-script@v7 + with: + script: | + const url = `${context.serverUrl}//${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` + const urlLink = `[Open](${url})` + const { data: comment } = await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `⏳ 正在运行快照更新。。。 CI: ${urlLink}` + }) + return comment.id + + - name: git config + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + - name: merge develop + run: | + git merge develop --no-commit || true + + - name: check conflicts + run: | + git status + conflict_count=$(git status | grep -c 'both modified:') || true + working_tree_clean=$(git status | grep -c 'nothing to commit, working tree clean') || true + csr_snap_conflict=$(git status | grep 'both modified:' | grep -c 'csr.test.jsx.snap') || true + ssr_snap_conflict=$(git status | grep 'both modified:' | grep -c 'ssr.test.jsx.snap') || true + common_conflict=$(git status | grep 'both modified:' | grep -c '_common') || true + conflicts_sum=$((csr_snap_conflict + ssr_snap_conflict + common_conflict)) + echo "conflict_count: $conflict_count" + echo "working_tree_clean: $working_tree_clean" + echo "csr_snap_conflict: $csr_snap_conflict" + echo "ssr_snap_conflict: $ssr_snap_conflict" + echo "common_conflict: $common_conflict" + echo "conflicts_sum: $conflicts_sum" + + if [ "$working_tree_clean" -eq "1" ]; then + echo "nothing to commit, working tree clean" + exit 0 + fi + + if [ "$conflict_count" -gt "0" ]&&[ "$conflicts_sum" -eq "0" ]; then + echo "Unknown conflict " + git status + exit 1 + fi + + if [ "$csr_snap_conflict" -eq "1" ];then + git checkout --theirs test/snap/__snapshots__/csr.test.jsx.snap + git add test/snap/__snapshots__/csr.test.jsx.snap + echo "resolve conflict csr.test.jsx.snap" + fi + + if [ "$ssr_snap_conflict" -eq "1" ];then + git checkout --theirs test/snap/__snapshots__/ssr.test.jsx.snap + git add test/snap/__snapshots__/ssr.test.jsx.snap + echo "resolve conflict ssr.test.jsx.snap" + fi + + if [ "$common_conflict" -eq "1" ];then + git checkout --theirs src/_common + git add src/_common + echo "resolve conflict _common" + fi + + git status + git commit -am "chore: merge develop" + - uses: actions/setup-node@v4 with: node-version: 18 + - run: npm install + - run: npm run test:update + - name: Commit Snapshot run: | - git add . + git status + working_tree_clean=$(git status | grep -c 'nothing to commit, working tree clean') || true + if [ "$working_tree_clean" -eq "0" ]; then + git add . + git commit -m "chore: update snapshot" + fi + git status + + - name: git push + run: | + git status + git push || true + + update-coverage: + needs: check + runs-on: ubuntu-latest + if: ${{ needs.check.outputs.next_action == 'update-coverage' }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.PERSONAL_TOKEN }} + + - name: gh checkout pr + env: + GH_TOKEN: ${{ secrets.PERSONAL_TOKEN }} + run: gh pr checkout ${{ github.event.issue.number }} --recurse-submodules + + - name: git config + run: | git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" - git commit -m "chore: update snapshot" - git push + + - name: bot commtent + id: bot-comment + uses: actions/github-script@v7 + with: + script: | + const url = `${context.serverUrl}//${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` + const urlLink = `[Open](${url})` + const { data: comment } = await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `⏳ 正在运行 coverage badge 更新。。。 CI: ${urlLink}` + }) + return comment.id + + - uses: actions/setup-node@v4 + with: + node-version: 18 + + - run: npm install + + - run: npm run generate:coverage-badge + + - name: commit coverage badge + run: | + git status + working_tree_clean=$(git status | grep -c 'nothing to commit, working tree clean') || true + if [ "$working_tree_clean" -eq "0" ]; then + git add . + git commit -m "chore: update coverage badge" + fi + git status + + - name: git push + run: | + git status + git push || true diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f7ff7553..daf0e4ba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ toc: false docClass: timeline --- +## 🌈 1.10.1 `2024-09-24` + +### 🚀 Features + +- `Button`: 新增`form` API,原生的 form 属性,支持用于通过 form 属性触发对应 id 的 form 的表单事件 @uyarn ([#3310](https://github.com/Tencent/tdesign-vue/pull/3310)) +- `Cascader`: 支持在打开菜单时滚动到首个已选项所在节点的能力 @uyarn ([#3335](https://github.com/Tencent/tdesign-vue/pull/3335)) +- `DatePicker`: 支持`readonly`属性 @myronliu347 ([#3311](https://github.com/Tencent/tdesign-vue/pull/3311)) +- `Form`: 新增`id` API,表单原生的 id 属性,支持用于配合非表单内的按钮通过 form 属性来触发表单事件 @uyarn ([#3310](https://github.com/Tencent/tdesign-vue/pull/3310)) +- `Menu`: `expandType`为`normal`时,支持三级子菜单的展示 @setcy ([#3296](https://github.com/Tencent/tdesign-vue/pull/3296)) +- `Select`: 支持在过滤情况下使用`checkAll`配置全选的功能 @zhengchengshi ([#3295](https://github.com/Tencent/tdesign-vue/pull/3295)) +- `Table`: 可展开收起场景下新增 `t-table__row--expanded` 和 `t-table__row--folded` 用于区分展开和收起的行 @uyarn ([#3331](https://github.com/Tencent/tdesign-vue/pull/3331)) +- `Tag`: 新增 `title` API 控制鼠标悬停显示的文本 @liweijie0812 ([#3309](https://github.com/Tencent/tdesign-vue/pull/3309)) +- `TimePicker`: 支持`readonly`属性 @myronliu347 ([#3311](https://github.com/Tencent/tdesign-vue/pull/3311)) +- `TimeRangePicker`: 当结束时间大于开始时间时,自动调整时间范围的顺序 @myronliu347 ([#3327](https://github.com/Tencent/tdesign-vue/pull/3327)) +- `TreeSelect`: 修改多选状态下默认点击父节点选项的行为为选中父节点,如果需要点击展开的交互效果,请配置`treeProps.expandOnClickNode` @uyarn ([#3330](https://github.com/Tencent/tdesign-vue/pull/3330)) +- `Rate`: 新增支持`clearable` API,用于清空评分 @myronliu347 ([#3332](https://github.com/Tencent/tdesign-vue/pull/3332)) + +### 🐞 Bug Fixes + +- `Cascader`: 修复过滤条件下选择父节点导致样式异常的问题 @uyarn ([#3333](https://github.com/Tencent/tdesign-vue/pull/3333)) +- `DatePicker`: 修复周选择器下,年份边界日期返回格式错误的问题 @uyarn ([#3336](https://github.com/Tencent/tdesign-vue/pull/3336)) +- `Select`: 修复下拉面板存在自定义节点且存在回车等操作时与组件自身键盘事件冲突的问题 @uyarn ([#3303](https://github.com/Tencent/tdesign-vue/pull/3303)) +- `SelectInput`: 修复动态变化输入框宽度的情况下,下拉菜单宽度没有动态跟随变化的问题 @myronliu347 ([#3325](https://github.com/Tencent/tdesign-vue/pull/3325)) +- `Slider`: 修复`change-end`事件回到没有正确`emit`的问题 @myronliu347 ([#3320](https://github.com/Tencent/tdesign-vue/pull/3320)) +- `Table`: 修复表格开启虚拟滚动和 `loading` 后,分页和表格内容顺序错乱的问题 @myronliu347 ([#3319](https://github.com/Tencent/tdesign-vue/pull/3319)) +- `TimePicker`: 修复 12 小时制切换在 `dayjs` 切换中文情况下失效的问题 @myronliu347 ([#3326](https://github.com/Tencent/tdesign-vue/pull/3326)) +- `TreeSelect`: 修复无法支持深层的 `keys` 设置的问题 @myronliu347 ([#3313](https://github.com/Tencent/tdesign-vue/pull/3313)) +- `Upload`: 修复 `uploadPastedFiles = false` 时第一次上传文件后报错导致响应式丢失的问题 @myronliu347 ([#3308](https://github.com/Tencent/tdesign-vue/pull/3308)) + +### 🚧 Others + +- `DatePicker`: 优化周选择器配合`firstDayOfWeek`使用的问题,详情请查看示例代码 @uyarn ([#3336](https://github.com/Tencent/tdesign-vue/pull/3336)) +- `Dialog`: 优化非模态模式下的展示样式 @RSS1102 ([common#1945](https://github.com/Tencent/tdesign-common/pull/1945)) +- `Popup`: 修复文档内容错误 @novlan1 ([common#1941](https://github.com/Tencent/tdesign-common/pull/1941)) +- `i18n`: 新增俄语和意大利语的语言配置支持 @liweijie0812 ([#3334](https://github.com/Tencent/tdesign-vue/pull/3334)) + + ## 🌈 1.10.0 `2024-08-29` ### 🚀 Features - `Empty`: 新增空状态组件 `Empty`,用于空状态时的占位提示 @HaixingOoO ([#3287](https://github.com/Tencent/tdesign-vue/pull/3287)) diff --git a/package.json b/package.json index 90ce357ff..336711a91 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tdesign-vue", "purename": "tdesign", - "version": "1.10.0-naruto", + "version": "1.10.1-naruto", "description": "tdesign-vue", "title": "tdesign-vue", "keywords": [ @@ -63,6 +63,7 @@ "lint": "npm run lint:tsc && eslint --ext .vue,.js,.ts,.tsx ./ --max-warnings 0", "lint:tsc": "tsc --emitDeclarationOnly", "generate:usage": "node script/generate-usage/index.js", + "generate:coverage-badge": "npm run test:coverage && node script/test/generate-coverage.js", "test": "npm run test:unit && npm run test:snap", "test:unit": "vitest run --config vitest.config.js", "test:update": "vitest run -u && npm run test:snap-update", @@ -156,7 +157,7 @@ "gray-matter": "^4.0.3", "husky": "^7.0.4", "hyphenate": "^0.2.4", - "jsdom": "^20.0.1", + "jsdom": "^25.0.0", "less": "^4.1.2", "lint-staged": "^12.3.7", "mockdate": "^3.0.2", diff --git a/site/src/App.vue b/site/src/App.vue index ca3f2ba4a..e04eacd4d 100644 --- a/site/src/App.vue +++ b/site/src/App.vue @@ -1,10 +1,24 @@ + + diff --git a/src/menu/_example/multi-head.vue b/src/menu/_example/multi-head.vue new file mode 100644 index 000000000..12299ade7 --- /dev/null +++ b/src/menu/_example/multi-head.vue @@ -0,0 +1,170 @@ + + + + diff --git a/src/menu/head-menu.tsx b/src/menu/head-menu.tsx index 7e90a0f4c..8fc307fb7 100644 --- a/src/menu/head-menu.tsx +++ b/src/menu/head-menu.tsx @@ -7,6 +7,7 @@ import { TdMenuInterface, TdOpenType } from './const'; import { Tabs, TabPanel } from '../tabs'; import { renderContent, renderTNodeJSX } from '../utils/render-tnode'; import VMenu from './v-menu'; +import type { VMenuData } from './v-menu'; import { usePrefixClass } from '../hooks/useConfig'; export default defineComponent({ @@ -131,14 +132,19 @@ export default defineComponent({ }; }, methods: { - renderNormalSubmenu() { - if (this.submenu.length === 0) return null; + renderNormalSubmenu(node: VMenuData[], depth: number) { + if (node.length === 0) return null; + return (