diff --git a/.github/workflows/issue-assignees.temp.yml b/.github/workflows/issue-assignees.temp.yml
index 83d5d6b87e..29b803a06a 100644
--- a/.github/workflows/issue-assignees.temp.yml
+++ b/.github/workflows/issue-assignees.temp.yml
@@ -15,38 +15,3 @@ jobs:
issuesOpened: |
👋 @{{ author }},感谢给 TDesign 提出了 issue。
请根据 issue 模版确保背景信息的完善,我们将调查并尽快回复你。
-
- # https://docs.github.com/cn/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#issues
- - uses: 94dreamer/issue-assignees@main
- id: assignees
- with:
- project_name: ${{github.event.repository.name}}
- issue_title: ${{github.event.issue.title}}
-
- - run: echo ${{ steps.assignees.outputs.contributors }}
- - name: Add assigness
- if: steps.assignees.outputs.contributors != ''
- uses: actions-cool/issues-helper@v3
- with:
- actions: 'add-assignees'
- token: ${{ secrets.GITHUB_TOKEN }}
- issue-number: ${{ github.event.issue.number }}
- assignees: ${{ steps.assignees.outputs.contributors }}
-
- - run: |
- contributors=${{ steps.assignees.outputs.contributors }}
- contributorstring=${contributors//,/ @}
- echo "::set-output name=string::@$contributorstring"
- id: contributors
-
- - name: 通知贡献者
- if: steps.assignees.outputs.contributors != ''
- uses: actions-cool/maintain-one-comment@v2.0.0
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- body: |
- ♥️ 有劳 ${{ steps.contributors.outputs.string }} 尽快确认问题。
- 确认有效后将下一步计划和可能需要的时间回复给 @${{ github.event.issue.user.login }} 。
-
- number: ${{ github.event.issue.number }}
- body-include: ""
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4ca63e8b91..beeb08a71e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -25,4 +25,4 @@
"source.fixAll.eslint": "explicit"
},
"cSpell.words": ["activable", "actived", "borderless", "Cascader", "Popconfirm", "Swiper", "tdesign"]
-}
\ No newline at end of file
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49703778a0..e44bdf7aec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,49 @@ toc: false
spline: explain
---
+## 🌈 1.5.5 `2024-03-28`
+### 🐞 Bug Fixes
+- `ImageViewer`: 修复 `imageReferrerpolicy` 没有对顶部缩略图生效的问题 @uyarn ([#2815](https://github.com/Tencent/tdesign-react/pull/2815))
+
+## 🌈 1.5.4 `2024-03-28`
+### 🚀 Features
+- `ImageViewer`: 新增`imageReferrerpolicy` API,支持配合 Image 组件的需要配置 Referrerpolicy 的场景 @uyarn ([#2813](https://github.com/Tencent/tdesign-react/pull/2813))
+### 🐞 Bug Fixes
+- `Select`: 修复 `onRemove` 事件没有正常触发的问题 @Ali-ovo ([#2802](https://github.com/Tencent/tdesign-react/pull/2802))
+- `Skeleton`: 修复`children`为必须的类型问题 @uyarn ([#2805](https://github.com/Tencent/tdesign-react/pull/2805))
+- `Tabs`: 提供 `action` 区域默认样式 @HaixingOoO ([#2808](https://github.com/Tencent/tdesign-react/pull/2808))
+- `Locale`: 修复`image`和`imageViewer` 英语语言包异常的问题 @uyarn @HaixingOoO ([#2808](https://github.com/Tencent/tdesign-react/pull/2808))
+- `Image`: `referrerpolicy` 参数被错误传递到外层 `div` 上,实际传递目标为原生 `image` 标签 @NWYLZW ([#2811](https://github.com/Tencent/tdesign-react/pull/2811))
+
+## 🌈 1.5.3 `2024-03-14`
+### 🚀 Features
+- `Breadcrumb`: `BreadcrumbItem` 支持 `onClick` 事件 @HaixingOoO ([#2795](https://github.com/Tencent/tdesign-react/pull/2795))
+- `tag`: `Tag`组件新增`color`API,支持自定义颜色 @maoyiluo @uyarn ([#2799](https://github.com/Tencent/tdesign-react/pull/2799))
+### 🐞 Bug Fixes
+- `FormList`: 修复多个`FormList` 卡死的问题 @HaixingOoO ([#2788](https://github.com/Tencent/tdesign-react/pull/2788))
+- `DatePicker`: 修复 `format` 与 `valueType` 不一致的场景下计算错误的问题 @uyarn ([#2798](https://github.com/Tencent/tdesign-react/pull/2798))
+### 🚧 Others
+- `Portal`: 添加Portal测试用例 @HaixingOoO ([#2791](https://github.com/Tencent/tdesign-react/pull/2791))
+- `List`: 完善 List 测试用例 @HaixingOoO ([#2792](https://github.com/Tencent/tdesign-react/pull/2792))
+- `Alert`: 完善 Alert 测试,优化代码 @HaixingOoO ([#2793](https://github.com/Tencent/tdesign-react/pull/2793))
+
+## 🌈 1.5.2 `2024-02-29`
+### 🚀 Features
+- `Cascader`: 新增`valueDisplay`和`label` API的支持 @HaixingOoO ([#2736](https://github.com/Tencent/tdesign-react/pull/2736))
+- `Descriptions`: `Descriptions` 组件支持嵌套 @HaixingOoO ([#2777](https://github.com/Tencent/tdesign-react/pull/2777))
+- `Tabs`: 调整激活 `Tab`下划线与 `TabHeader`边框的层级关系 @uyarn ([#2780](https://github.com/Tencent/tdesign-react/pull/2780))
+### 🐞 Bug Fixes
+- `Grid`: 尺寸计算错误,宽度兼容异常 @NWYLZW ([#2738](https://github.com/Tencent/tdesign-react/pull/2738))
+- `Cascader`: 修复`clearable`点击清除按钮触发三次`onChange`的问题 @HaixingOoO ([#2736](https://github.com/Tencent/tdesign-react/pull/2736))
+- `Dialog`: 修复`useDialogPosition`渲染多次绑定事件 @HaixingOoO ([#2749](https://github.com/Tencent/tdesign-react/pull/2749))
+- `Guide`: 修复`Guide`自定义内容功能失效 @zhangpaopao0609 ([#2752](https://github.com/Tencent/tdesign-react/pull/2752))
+- `Tree`: 修复设置 `keys.children` 后展开图标没有正常变化的问题 @uyarn ([#2746](https://github.com/Tencent/tdesign-react/pull/2746))
+- `Tree`: 修复`Tree` 自定义label `setData` 没有渲染的问题 @HaixingOoO ([#2776](https://github.com/Tencent/tdesign-react/pull/2776))
+- `Tree`: 修复设置 `Tree` 宽度,`TreeItem` 的 `checkbox` 会被压缩,`label` 省略号失效的问题 @HaixingOoO @uyarn ([#2780](https://github.com/Tencent/tdesign-react/pull/2780))
+- `Select`: @uyarn
+ - 修复通过滚动加载选项选中后滚动行为异常的问题 ([#2779](https://github.com/Tencent/tdesign-react/pull/2779))
+ - 修复使用`size` API时,虚拟滚动的功能异常问题 ([#2756](https://github.com/Tencent/tdesign-react/pull/2756))
+
## 🌈 1.5.1 `2024-01-25`
### 🚀 Features
- `Popup`: 支持`Plugin`方式使用。 @HaixingOoO ([#2717](https://github.com/Tencent/tdesign-react/pull/2717))
diff --git a/README-zh_CN.md b/README-zh_CN.md
index 0b58259129..0f6038a3dd 100644
--- a/README-zh_CN.md
+++ b/README-zh_CN.md
@@ -95,7 +95,7 @@ TDesign 欢迎任何愿意参与贡献的参与者。如果需要本地运行代
有任何问题,建议通过 [Github issues](https://github.com/Tencent/tdesign-react/issues) 反馈或扫码加入用户微信群。
-
+
# 开源协议
diff --git a/README.md b/README.md
index 925480a03a..1329570293 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ Contributing is welcome. Read [guidelines for contributing](https://github.com/T
Create your [Github issues](https://github.com/Tencent/tdesign-react/issues) or scan the QR code below to join our user groups
-
+
# License
diff --git a/package.json b/package.json
index 3b38305d4d..545420680c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "tdesign-react",
"purename": "tdesign",
- "version": "1.5.1",
+ "version": "1.5.5",
"description": "TDesign Component for React",
"title": "tdesign-react",
"main": "lib/index.js",
@@ -184,7 +184,7 @@
"start-server-and-test": "^1.14.0",
"tdesign-icons-view": "^0.2.0",
"tdesign-publish-cli": "^0.0.10",
- "tdesign-site-components": "^0.13.1",
+ "tdesign-site-components": "^0.14.6",
"tdesign-theme-generator": "^1.0.0",
"ts-morph": "^13.0.02",
"ts-node": "^10.4.0",
@@ -194,7 +194,7 @@
"vite-plugin-pwa": "^0.12.8",
"vite-plugin-tdoc": "^2.0.1",
"vitest": "^0.24.1",
- "workbox-precaching": "^6.3.0"
+ "workbox-precaching": "^7.0.0"
},
"dependencies": {
"@babel/runtime": "~7.17.2",
diff --git a/script/init-component/config.js b/script/init-component/config.js
index 5df9ccf478..963d927fb4 100644
--- a/script/init-component/config.js
+++ b/script/init-component/config.js
@@ -12,14 +12,6 @@ function getToBeCreatedFiles(component, pascalCaseName) {
file: `${pascalCaseName}.tsx`,
template: 'component.tsx.tpl',
},
- {
- file: `api.md`,
- template: 'api.md.tpl',
- },
- {
- file: `README.md`,
- template: 'readme.md.tpl',
- },
],
},
[`src/${component}/_example`]: {
diff --git a/script/init-component/tpl/api.md.tpl b/script/init-component/tpl/api.md.tpl
deleted file mode 100644
index 78f6e3bd6c..0000000000
--- a/script/init-component/tpl/api.md.tpl
+++ /dev/null
@@ -1,3 +0,0 @@
-### <%= PascalCaseComponent %> Props
-名称 | 类型 | 默认值 | 说明 | 必传
--- | -- | -- | -- | --
diff --git a/script/init-component/tpl/component.tsx.tpl b/script/init-component/tpl/component.tsx.tpl
index 14854d7413..855244893e 100644
--- a/script/init-component/tpl/component.tsx.tpl
+++ b/script/init-component/tpl/component.tsx.tpl
@@ -1,5 +1,5 @@
import React from 'react';
-import { Td<%= PascalCaseComponent %>Props } from '../_type/components/<%= component %>';
+import type { Td<%= PascalCaseComponent %>Props } from './type';
export type <%= PascalCaseComponent %>Props = Td<%= PascalCaseComponent %>Props;
diff --git a/script/init-component/tpl/readme.md.tpl b/script/init-component/tpl/readme.md.tpl
deleted file mode 100644
index e5ac36e603..0000000000
--- a/script/init-component/tpl/readme.md.tpl
+++ /dev/null
@@ -1,3 +0,0 @@
-:: BASE_DOC ::
-
-:: BASE_PROPS ::
diff --git a/site/test-coverage.js b/site/test-coverage.js
index 1814fedc95..5a8d2a6482 100644
--- a/site/test-coverage.js
+++ b/site/test-coverage.js
@@ -12,8 +12,8 @@ module.exports = {
"lines": "85.93%"
},
"alert": {
- "statements": "96.96%",
- "branches": "72.72%",
+ "statements": "100%",
+ "branches": "100%",
"functions": "100%",
"lines": "100%"
},
@@ -48,10 +48,10 @@ module.exports = {
"lines": "100%"
},
"breadcrumb": {
- "statements": "85.1%",
- "branches": "54.83%",
- "functions": "83.33%",
- "lines": "88.88%"
+ "statements": "84.31%",
+ "branches": "53.12%",
+ "functions": "85.71%",
+ "lines": "89.58%"
},
"button": {
"statements": "100%",
@@ -72,10 +72,10 @@ module.exports = {
"lines": "100%"
},
"cascader": {
- "statements": "92.66%",
- "branches": "71.42%",
- "functions": "92.3%",
- "lines": "94.11%"
+ "statements": "93.12%",
+ "branches": "75.8%",
+ "functions": "90.62%",
+ "lines": "94.21%"
},
"checkbox": {
"statements": "90.27%",
@@ -114,10 +114,10 @@ module.exports = {
"lines": "68.75%"
},
"datePicker": {
- "statements": "62.38%",
- "branches": "42.79%",
+ "statements": "62.47%",
+ "branches": "43.3%",
"functions": "60%",
- "lines": "66.07%"
+ "lines": "66.16%"
},
"descriptions": {
"statements": "98.82%",
@@ -150,10 +150,10 @@ module.exports = {
"lines": "92.4%"
},
"form": {
- "statements": "83.54%",
+ "statements": "83.5%",
"branches": "70.73%",
"functions": "81.51%",
- "lines": "87.2%"
+ "lines": "87.17%"
},
"grid": {
"statements": "84.21%",
@@ -216,10 +216,10 @@ module.exports = {
"lines": "100%"
},
"list": {
- "statements": "78.78%",
- "branches": "53.84%",
- "functions": "66.66%",
- "lines": "78.78%"
+ "statements": "100%",
+ "branches": "100%",
+ "functions": "100%",
+ "lines": "100%"
},
"loading": {
"statements": "86.07%",
@@ -264,10 +264,10 @@ module.exports = {
"lines": "76.92%"
},
"popup": {
- "statements": "95.45%",
- "branches": "92.85%",
- "functions": "86.36%",
- "lines": "94.82%"
+ "statements": "47.01%",
+ "branches": "41.26%",
+ "functions": "45.23%",
+ "lines": "45%"
},
"progress": {
"statements": "89.23%",
@@ -318,10 +318,10 @@ module.exports = {
"lines": "90.47%"
},
"space": {
- "statements": "87.5%",
+ "statements": "87.75%",
"branches": "84.37%",
"functions": "100%",
- "lines": "86.95%"
+ "lines": "87.75%"
},
"statistic": {
"statements": "84.44%",
@@ -330,40 +330,40 @@ module.exports = {
"lines": "85.71%"
},
"steps": {
- "statements": "87.65%",
- "branches": "66.66%",
+ "statements": "87.8%",
+ "branches": "66.07%",
"functions": "100%",
- "lines": "100%"
+ "lines": "87.8%"
},
"swiper": {
- "statements": "72.13%",
- "branches": "42.6%",
+ "statements": "71.93%",
+ "branches": "43.28%",
"functions": "85.71%",
- "lines": "71.5%"
+ "lines": "71.35%"
},
"switch": {
- "statements": "96.29%",
+ "statements": "96.55%",
"branches": "92%",
"functions": "100%",
- "lines": "100%"
+ "lines": "96.55%"
},
"table": {
- "statements": "48.13%",
+ "statements": "48.36%",
"branches": "33.74%",
"functions": "45.91%",
- "lines": "49.31%"
+ "lines": "49.56%"
},
"tabs": {
- "statements": "91.11%",
+ "statements": "90.96%",
"branches": "78.64%",
"functions": "86.36%",
- "lines": "91.32%"
+ "lines": "91.17%"
},
"tag": {
- "statements": "65%",
- "branches": "59.09%",
- "functions": "46.66%",
- "lines": "64.86%"
+ "statements": "56.25%",
+ "branches": "48.21%",
+ "functions": "47.05%",
+ "lines": "55.55%"
},
"tagInput": {
"statements": "85.11%",
@@ -396,22 +396,22 @@ module.exports = {
"lines": "90.56%"
},
"transfer": {
- "statements": "86.06%",
- "branches": "66.66%",
+ "statements": "86.27%",
+ "branches": "67.61%",
"functions": "84.28%",
- "lines": "87.77%"
+ "lines": "87.97%"
},
"tree": {
- "statements": "85.94%",
- "branches": "70.09%",
- "functions": "84.61%",
- "lines": "88.08%"
+ "statements": "86.22%",
+ "branches": "70.64%",
+ "functions": "84.9%",
+ "lines": "88.33%"
},
"treeSelect": {
- "statements": "95.45%",
+ "statements": "95.48%",
"branches": "86.17%",
"functions": "97.61%",
- "lines": "97.2%"
+ "lines": "97.22%"
},
"upload": {
"statements": "96.66%",
@@ -420,10 +420,10 @@ module.exports = {
"lines": "100%"
},
"watermark": {
- "statements": "95.65%",
- "branches": "82.92%",
+ "statements": "95.77%",
+ "branches": "79.41%",
"functions": "100%",
- "lines": "98.46%"
+ "lines": "98.5%"
},
"utils": {
"statements": "75.43%",
diff --git a/src/alert/Alert.tsx b/src/alert/Alert.tsx
index 0d5a45de90..df6e317211 100644
--- a/src/alert/Alert.tsx
+++ b/src/alert/Alert.tsx
@@ -67,7 +67,7 @@ const Alert = forwardRef((props, ref) => {
const renderIconNode = () => {
if (React.isValidElement(icon)) return icon;
- return React.createElement(iconMap[theme] || iconMap.info);
+ return React.createElement(iconMap[theme]);
};
const renderMessage = () => {
@@ -84,11 +84,9 @@ const Alert = forwardRef((props, ref) => {
}
return true;
})}
- {+maxLine > 0 ? (
-
- {!collapsed ? t(local.expandText) : t(local.collapseText)}
-
- ) : null}
+
+ {!collapsed ? t(local.expandText) : t(local.collapseText)}
+
);
}
diff --git a/src/alert/__tests__/alert.test.tsx b/src/alert/__tests__/alert.test.tsx
index b5b3a69d11..baa9a65bf0 100644
--- a/src/alert/__tests__/alert.test.tsx
+++ b/src/alert/__tests__/alert.test.tsx
@@ -10,26 +10,93 @@ describe('Alert 组件测试', () => {
const onClose = vi.fn();
const onClosed = vi.fn();
- const { queryByTestId, container } = render(
+ const { queryByTestId, container, queryByText } = render(
{text}}
onClose={onClose}
onClosed={onClosed}
+ operation={test content}
/>,
);
+
expect(container.querySelector('.t-alert--closing')).not.toBeInTheDocument();
+ expect(container.querySelector('#operation-test')).not.toBeNull();
+ expect(container.querySelector('#operation-test')).toBeInTheDocument();
+ expect(queryByText('title content')).not.toBeNull();
+ expect(queryByText('title content')).toBeInTheDocument();
+ expect(container.querySelector('.t-alert--error')).not.toBeNull();
+ expect(container.querySelector('.t-alert--error')).toBeInTheDocument();
act(() => {
fireEvent.click(queryByTestId(testId));
});
- expect(onClose).toHaveBeenCalledTimes(1);
expect(container.querySelector('.t-alert--closing')).toBeInTheDocument();
+ expect(onClose).toHaveBeenCalledTimes(1);
await mockTimeout(() => expect(onClosed).toHaveBeenCalledTimes(1), 300);
});
+ test('custom close icon render', () => {
+ const { queryByTestId } = render(
+ {text}} />,
+ );
+
+ expect(queryByTestId(testId)).not.toBeNull();
+ expect(queryByTestId(testId)).toBeInTheDocument();
+ });
+
+ test('default close icon render', () => {
+ const { container } = render();
+
+ expect(container.querySelector('.t-icon-close')).not.toBeNull();
+ expect(container.querySelector('.t-icon-close')).toBeInTheDocument();
+ });
+
+ test('custom icon render', () => {
+ const { queryByText } = render(custom icon} title="title content" />);
+
+ expect(queryByText('custom icon')).not.toBeNull();
+ expect(queryByText('custom icon')).toBeInTheDocument();
+ });
+
+ test('theme icon render', () => {
+ const { container } = render();
+
+ expect(container.querySelector('.t-icon-check-circle-filled')).not.toBeNull();
+ expect(container.querySelector('.t-icon-check-circle-filled')).toBeInTheDocument();
+ });
+
+ test('default theme icon render', () => {
+ const { container } = render();
+
+ expect(container.querySelector('.t-icon-info-circle-filled')).not.toBeNull();
+ expect(container.querySelector('.t-icon-info-circle-filled')).toBeInTheDocument();
+ });
+
+ test('maxLine', () => {
+ const { container } = render();
+
+ expect(container.querySelector('.t-alert__collapse')).toBeNull();
+ expect(container.querySelector('.t-alert__collapse')).not.toBeInTheDocument();
+ });
+
+ test('message not collapsed', () => {
+ const massage = [
+ {text}
,
+ {text}
,
+
+ {text}
+
,
+ ];
+ const { container } = render();
+
+ expect(container.querySelector('.t-alert__collapse')).toBeNull();
+ expect(container.querySelector('.t-alert__collapse')).not.toBeInTheDocument();
+ });
+
test('Alert 展开收起操作', async () => {
const massage = [
{text}
,
@@ -39,6 +106,9 @@ describe('Alert 组件测试', () => {
,
];
const { queryByText, queryByTestId } = render();
+
+ expect(queryByText('展开更多')).not.toBeNull();
+ expect(queryByText('展开更多')).toBeInTheDocument();
const element = await waitFor(() => queryByTestId(testId));
expect(element).toBeNull();
@@ -47,6 +117,8 @@ describe('Alert 组件测试', () => {
fireEvent.click(btn);
});
+ expect(queryByText('收起')).not.toBeNull();
+ expect(queryByText('收起')).toBeInTheDocument();
const element1 = await waitFor(() => queryByTestId(testId));
expect(element1).not.toBeNull();
diff --git a/src/breadcrumb/BreadcrumbItem.tsx b/src/breadcrumb/BreadcrumbItem.tsx
index 4eef279d4b..a9b7c9fa88 100644
--- a/src/breadcrumb/BreadcrumbItem.tsx
+++ b/src/breadcrumb/BreadcrumbItem.tsx
@@ -32,6 +32,7 @@ const BreadcrumbItem = forwardRef((props, r
replace,
className,
content,
+ onClick,
...restProps
} = useDefaultProps(props, breadcrumbItemDefaultProps);
@@ -89,8 +90,13 @@ const BreadcrumbItem = forwardRef((props, r
);
+ const handleClick = (e: React.MouseEvent) => {
+ if (disabled) return;
+ onClick?.(e);
+ };
+
return (
-
+
{isCutOff ? {itemContent} : itemContent}
{separatorContent}
diff --git a/src/breadcrumb/breadcrumb.en-US.md b/src/breadcrumb/breadcrumb.en-US.md
index a7c881b5d8..364ded77fd 100644
--- a/src/breadcrumb/breadcrumb.en-US.md
+++ b/src/breadcrumb/breadcrumb.en-US.md
@@ -1,22 +1,24 @@
:: BASE_DOC ::
## API
+
### Breadcrumb Props
name | type | default | description | required
-- | -- | -- | -- | --
-className | String | - | 类名 | N
-style | Object | - | 样式,Typescript:`React.CSSProperties` | N
+className | String | - | className of component | N
+style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
maxItemWidth | String | undefined | \- | N
options | Array | - | Typescript:`Array
` | N
separator | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
+
### BreadcrumbItem Props
name | type | default | description | required
-- | -- | -- | -- | --
-className | String | - | 类名 | N
-style | Object | - | 样式,Typescript:`React.CSSProperties` | N
+className | String | - | className of component | N
+style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
children | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
content | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
disabled | Boolean | - | \- | N
@@ -25,5 +27,6 @@ icon | TElement | - | prefix icon in breadcrumb item。Typescript:`TNode`。[s
maxWidth | String | undefined | \- | N
replace | Boolean | false | \- | N
router | Object | - | Typescript:`any` | N
-target | String | _self | options:_blank/_self/_parent/_top | N
-to | String / Object | - | Typescript:`Route` `interface Route { path?: string; name?: string; hash?: string; query?: RouteData; params?: RouteData }` `type RouteData = { [key: string]: string \| string[] }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/breadcrumb/type.ts) | N
+target | String | _self | options: _blank/_self/_parent/_top | N
+to | String / Object | - | Typescript:`string \| Route` `interface Route { path?: string; name?: string; hash?: string; query?: RouteData; params?: RouteData }` `type RouteData = { [key: string]: string \| string[] }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/breadcrumb/type.ts) | N
+onClick | Function | | Typescript:`(e: MouseEvent) => void`
trigger on click | N
diff --git a/src/breadcrumb/breadcrumb.md b/src/breadcrumb/breadcrumb.md
index ce63ef9bc2..a87ff10054 100644
--- a/src/breadcrumb/breadcrumb.md
+++ b/src/breadcrumb/breadcrumb.md
@@ -1,9 +1,10 @@
:: BASE_DOC ::
## API
+
### Breadcrumb Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
@@ -11,9 +12,10 @@ maxItemWidth | String | undefined | 单项最大宽度,超出后会以省略
options | Array | - | 面包屑项,功能同 BreadcrumbItem。TS 类型:`Array` | N
separator | TNode | - | 自定义分隔符。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
+
### BreadcrumbItem Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
@@ -26,4 +28,5 @@ maxWidth | String | undefined | 最大宽度,超出后会以省略号形式呈
replace | Boolean | false | 路由跳转是否采用覆盖的方式(覆盖后将没有浏览器历史记录) | N
router | Object | - | 路由对象。如果项目存在 Router,则默认使用 Router。TS 类型:`any` | N
target | String | _self | 链接或路由跳转方式。可选项:_blank/_self/_parent/_top | N
-to | String / Object | - | 路由跳转目标,当且仅当 Router 存在时,该 API 有效。TS 类型:`Route` `interface Route { path?: string; name?: string; hash?: string; query?: RouteData; params?: RouteData }` `type RouteData = { [key: string]: string \| string[] }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/breadcrumb/type.ts) | N
+to | String / Object | - | 路由跳转目标,当且仅当 Router 存在时,该 API 有效。TS 类型:`string \| Route` `interface Route { path?: string; name?: string; hash?: string; query?: RouteData; params?: RouteData }` `type RouteData = { [key: string]: string \| string[] }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/breadcrumb/type.ts) | N
+onClick | Function | | TS 类型:`(e: MouseEvent) => void`
点击时触发 | N
diff --git a/src/breadcrumb/type.ts b/src/breadcrumb/type.ts
index 93697472d1..ba4d309592 100644
--- a/src/breadcrumb/type.ts
+++ b/src/breadcrumb/type.ts
@@ -5,6 +5,7 @@
* */
import { TNode, TElement } from '../common';
+import { MouseEvent } from 'react';
export interface TdBreadcrumbProps {
/**
@@ -64,7 +65,11 @@ export interface TdBreadcrumbItemProps {
/**
* 路由跳转目标,当且仅当 Router 存在时,该 API 有效
*/
- to?: Route | string;
+ to?: string | Route;
+ /**
+ * 点击时触发
+ */
+ onClick?: (e: MouseEvent) => void;
}
export interface Route {
diff --git a/src/common/__tests__/portal.test.tsx b/src/common/__tests__/portal.test.tsx
new file mode 100644
index 0000000000..8419b5a4b2
--- /dev/null
+++ b/src/common/__tests__/portal.test.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { renderToString } from 'react-dom/server';
+import { render } from '@test/utils';
+import Portal from '../Portal';
+
+describe('Portal', () => {
+ test('Portal render', () => {
+ const { unmount } = render(
+
+ Hello World
+ ,
+ );
+
+ expect(document.querySelector('#portal')).not.toBeNull();
+ expect(document.querySelector('#portal')).toBeInTheDocument();
+ unmount();
+ expect(document.querySelector('#portal')).toBeNull();
+ });
+
+ test('Portal ssr render', () => {
+ const renderOnServer = () =>
+ renderToString(
+
+ Hello World
+ ,
+ );
+
+ // 目前test会出错,待后续添加lazy可support ssr
+ expect(renderOnServer).toThrow();
+ });
+});
diff --git a/src/date-picker/DatePicker.tsx b/src/date-picker/DatePicker.tsx
index c334f6e823..536f15f659 100644
--- a/src/date-picker/DatePicker.tsx
+++ b/src/date-picker/DatePicker.tsx
@@ -8,7 +8,7 @@ import SelectInput from '../select-input';
import SinglePanel from './panel/SinglePanel';
import useSingle from './hooks/useSingle';
import { parseToDayjs, getDefaultFormat, formatTime, formatDate } from '../_common/js/date-picker/format';
-import { subtractMonth, addMonth, extractTimeObj } from '../_common/js/date-picker/utils';
+import { subtractMonth, addMonth, extractTimeObj, covertToDate } from '../_common/js/date-picker/utils';
import { datePickerDefaultProps } from './defaultProps';
import useDefaultProps from '../hooks/useDefaultProps';
@@ -64,8 +64,9 @@ const DatePicker = forwardRef((originalProps, r
useEffect(() => {
// 面板展开重置数据
- setCacheValue(formatDate(value, { format }));
- setInputValue(formatDate(value, { format }));
+ const dateValue = value ? covertToDate(value as string, valueType) : value;
+ setCacheValue(formatDate(dateValue, { format }));
+ setInputValue(formatDate(dateValue, { format }));
if (popupVisible) {
setYear(parseToDayjs(value, format).year());
diff --git a/src/date-picker/panel/RangePanel.tsx b/src/date-picker/panel/RangePanel.tsx
index 6d3c0d4951..72b33001cb 100644
--- a/src/date-picker/panel/RangePanel.tsx
+++ b/src/date-picker/panel/RangePanel.tsx
@@ -9,6 +9,7 @@ import type { TdTimePickerProps } from '../../time-picker';
import { getDefaultFormat, parseToDayjs } from '../../_common/js/date-picker/format';
import useTableData from '../hooks/useTableData';
import useDisableDate from '../hooks/useDisableDate';
+import useDefaultProps from '../../hooks/useDefaultProps';
export interface RangePanelProps extends TdDateRangePickerProps, StyledProps {
hoverValue?: string[];
@@ -32,9 +33,15 @@ export interface RangePanelProps extends TdDateRangePickerProps, StyledProps {
onTimePickerChange?: TdTimePickerProps['onChange'];
}
-const RangePanel = forwardRef((props, ref) => {
+const RangePanel = forwardRef((originalProps, ref) => {
const { classPrefix, datePicker: globalDatePickerConfig } = useConfig();
const panelName = `${classPrefix}-date-range-picker__panel`;
+ const props = useDefaultProps(originalProps, {
+ mode: 'date',
+ panelPreselection: true,
+ enableTimePicker: false,
+ presetsPlacement: 'bottom',
+ });
const {
value = [],
hoverValue = [],
@@ -200,11 +207,5 @@ const RangePanel = forwardRef((props, ref) => {
});
RangePanel.displayName = 'RangePanel';
-RangePanel.defaultProps = {
- mode: 'date',
- panelPreselection: true,
- enableTimePicker: false,
- presetsPlacement: 'bottom',
-};
export default RangePanel;
diff --git a/src/date-picker/panel/SinglePanel.tsx b/src/date-picker/panel/SinglePanel.tsx
index 14cf5695d1..bee9d57fce 100644
--- a/src/date-picker/panel/SinglePanel.tsx
+++ b/src/date-picker/panel/SinglePanel.tsx
@@ -9,6 +9,7 @@ import type { TdTimePickerProps } from '../../time-picker';
import { getDefaultFormat, parseToDayjs } from '../../_common/js/date-picker/format';
import useTableData from '../hooks/useTableData';
import useDisableDate from '../hooks/useDisableDate';
+import useDefaultProps from '../../hooks/useDefaultProps';
export interface SinglePanelProps extends TdDatePickerProps, StyledProps {
year?: number;
@@ -27,9 +28,14 @@ export interface SinglePanelProps extends TdDatePickerProps, StyledProps {
onTimePickerChange?: TdTimePickerProps['onChange'];
}
-const SinglePanel = forwardRef((props, ref) => {
+const SinglePanel = forwardRef((originalProps, ref) => {
const { classPrefix, datePicker: globalDatePickerConfig } = useConfig();
const panelName = `${classPrefix}-date-picker__panel`;
+ const props = useDefaultProps(originalProps, {
+ mode: 'date',
+ enableTimePicker: false,
+ presetsPlacement: 'bottom',
+ });
const {
value,
mode,
@@ -109,10 +115,4 @@ const SinglePanel = forwardRef((props, ref) =>
SinglePanel.displayName = 'SinglePanel';
-SinglePanel.defaultProps = {
- mode: 'date',
- enableTimePicker: false,
- presetsPlacement: 'bottom',
-};
-
export default SinglePanel;
diff --git a/src/descriptions/Descriptions.tsx b/src/descriptions/Descriptions.tsx
index 05b328a6c5..3e94396922 100644
--- a/src/descriptions/Descriptions.tsx
+++ b/src/descriptions/Descriptions.tsx
@@ -7,7 +7,7 @@ import { descriptionItemDefaultProps, descriptionsDefaultProps } from './default
import useDefaultProps from '../hooks/useDefaultProps';
import useConfig from '../hooks/useConfig';
import useCommonClassName from '../hooks/useCommonClassName';
-import { LayoutEnum } from '../common';
+import { LayoutEnum, StyledProps } from '../common';
import { DescriptionsContext } from './DescriptionsContext';
import DescriptionsItem from './DescriptionsItem';
import Row from './Row';
@@ -26,14 +26,15 @@ import Row from './Row';
* TDescriptionsItem:获取 item 数据(span, label, content)
*/
-export type DescriptionsProps = TdDescriptionsProps & {
- children?: React.ReactNode;
-};
+export type DescriptionsProps = TdDescriptionsProps &
+ StyledProps & {
+ children?: React.ReactNode;
+ };
const Descriptions = (DescriptionsProps: DescriptionsProps) => {
const props = useDefaultProps(DescriptionsProps, descriptionsDefaultProps);
- const { title, bordered, column, layout, items: rowItems, children } = props;
+ const { className, style, title, bordered, column, layout, items: rowItems, children } = props;
const { classPrefix } = useConfig();
@@ -131,7 +132,7 @@ const Descriptions = (DescriptionsProps: DescriptionsProps) => {
return (
-
+
{renderHeader()}
{renderBody()}
diff --git a/src/descriptions/__tests__/descriptions.test.tsx b/src/descriptions/__tests__/descriptions.test.tsx
index 6a712868d6..0b408f98a2 100644
--- a/src/descriptions/__tests__/descriptions.test.tsx
+++ b/src/descriptions/__tests__/descriptions.test.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { render } from '@test/utils';
+import { render, screen } from '@test/utils';
import Descriptions from '../index';
import { SizeEnum } from '../../common';
@@ -242,4 +242,40 @@ describe('Descriptions 组件测试', () => {
expect(container.querySelector('.t-descriptions')).toBeInTheDocument();
});
+
+ // nest
+ test('nest', () => {
+ const itemsContent = [
+ {
+ label: 'City',
+ content: 'Shenzhen',
+ },
+ {
+ label: 'Detail',
+ content: 'Penguin Island D1 4A Mail Center',
+ },
+ ];
+
+ const items = [
+ {
+ label: 'Name',
+ content: 'TDesign',
+ },
+ {
+ label: 'Telephone Number',
+ content: '139****0609',
+ },
+ {
+ label: 'Area',
+ content: 'China Tencent Headquarters',
+ },
+ {
+ label: 'Address',
+ content:
,
+ },
+ ];
+ render(
);
+ expect(screen.getByText('Shenzhen')).not.toBeNull();
+ expect(screen.getByText('Shenzhen')).toBeInTheDocument();
+ });
});
diff --git a/src/descriptions/_example/base.jsx b/src/descriptions/_example/base.jsx
index b14ffda714..3d537fa461 100644
--- a/src/descriptions/_example/base.jsx
+++ b/src/descriptions/_example/base.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import { Descriptions, Space } from 'tdesign-react';
-const { DescriptionsItem } = Descriptions;
+// const { DescriptionsItem } = Descriptions;
export default function BasicDescriptions() {
const items = [
@@ -24,9 +24,9 @@ export default function BasicDescriptions() {
];
return (
- 推荐:数据写法
+ {/* 推荐:数据写法
*/}
- JSX写法
+ {/* JSX写法
TDesign
139****0609
@@ -34,7 +34,7 @@ export default function BasicDescriptions() {
Shenzhen Penguin Island D1 4A Mail Center
-
+ */}
);
}
diff --git a/src/descriptions/_example/layout.jsx b/src/descriptions/_example/layout.jsx
index a94d02eb02..9cf7d43bc0 100644
--- a/src/descriptions/_example/layout.jsx
+++ b/src/descriptions/_example/layout.jsx
@@ -1,7 +1,10 @@
import React from 'react';
-import { Descriptions, Space } from 'tdesign-react';
+import { Descriptions, Space, Row, Col, Radio } from 'tdesign-react';
export default function Layout() {
+ const [layout, setLayout] = React.useState('horizontal');
+ const [itemLayout, setItemLayout] = React.useState('horizontal');
+
const items = [
{
label: 'Name',
@@ -21,27 +24,33 @@ export default function Layout() {
},
];
+ const layoutOptions = ['horizontal', 'vertical'];
+ const itemLayoutOptions = ['horizontal', 'vertical'];
+
return (
-
- 整体左右布局,item 左右布局
-
-
-
-
- 整体左右布局,item 上下布局
-
-
-
-
- 整体上下布局,item 左右布局
-
-
-
-
- 整体上下布局,item 上下布局
-
-
+
+
+ layout:
+
+
+
+
+
+
+
+ itemLayout:
+
+
+
+
+
+
);
}
diff --git a/src/descriptions/_example/nest.jsx b/src/descriptions/_example/nest.jsx
new file mode 100644
index 0000000000..b0a7df912b
--- /dev/null
+++ b/src/descriptions/_example/nest.jsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { Descriptions, Space } from 'tdesign-react';
+
+// const { DescriptionsItem } = Descriptions;
+
+export default function Nest() {
+ const itemsContent = [
+ {
+ label: 'City',
+ content: 'Shenzhen',
+ },
+ {
+ label: 'Detail',
+ content: 'Penguin Island D1 4A Mail Center',
+ },
+ ];
+
+ const items = [
+ {
+ label: 'Name',
+ content: 'TDesign',
+ },
+ {
+ label: 'Telephone Number',
+ content: '139****0609',
+ },
+ {
+ label: 'Area',
+ content: 'China Tencent Headquarters',
+ },
+ {
+ label: 'Address',
+ content:
,
+ },
+ ];
+ return (
+
+
+ {/*
+ TDesign
+ 139****0609
+ China Tencent Headquarters
+
+
+ Shenzhen
+ Penguin Island D1 4A Mail Center
+
+
+ */}
+
+ );
+}
diff --git a/src/form/FormList.tsx b/src/form/FormList.tsx
index 1d6c1df2f8..25f6453bd0 100644
--- a/src/form/FormList.tsx
+++ b/src/form/FormList.tsx
@@ -145,7 +145,8 @@ const FormList: React.FC
= (props) => {
Promise.resolve().then(() => {
if (!fieldsTaskQueueRef.current.length) return;
- const currentQueue = fieldsTaskQueueRef.current[0];
+ // fix multiple formlist stuck
+ const currentQueue = fieldsTaskQueueRef.current.pop();
const { fieldData, callback, originData } = currentQueue;
[...formListMapRef.current.values()].forEach((formItemRef) => {
@@ -155,7 +156,6 @@ const FormList: React.FC = (props) => {
const data = get(fieldData, itemName);
callback(formItemRef, data);
});
- fieldsTaskQueueRef.current.pop();
// formList 嵌套 formList
if (!formMapRef || !formMapRef.current) return;
diff --git a/src/guide/Guide.tsx b/src/guide/Guide.tsx
index 1e0ef69f67..c16b6750ed 100644
--- a/src/guide/Guide.tsx
+++ b/src/guide/Guide.tsx
@@ -338,7 +338,7 @@ const Guide: React.FC = (originalProps) => {
current: innerCurrent,
total: stepsTotal,
};
- renderBody = isFunction(content) ? content(contentProps) : content;
+ renderBody = isFunction(content) ? content(contentProps) : React.cloneElement(content, contentProps);
} else {
renderBody = renderPopupContent();
}
@@ -354,7 +354,7 @@ const Guide: React.FC = (originalProps) => {
zIndex={zIndex}
placement={currentStepInfo.placement as StepPopupPlacement}
{...currentStepInfo.popupProps}
- overlayClassName={currentStepInfo.stepOverlayClass}
+ overlayClassName={[`${prefixCls}__popup`, currentStepInfo.stepOverlayClass]}
overlayInnerClassName={innerClassName.concat(currentStepInfo.popupProps?.overlayInnerClassName)}
>
diff --git a/src/guide/__tests__/__snapshots__/vitest-guide.test.jsx.snap b/src/guide/__tests__/__snapshots__/vitest-guide.test.jsx.snap
index 63e07e8802..d24171f6d7 100644
--- a/src/guide/__tests__/__snapshots__/vitest-guide.test.jsx.snap
+++ b/src/guide/__tests__/__snapshots__/vitest-guide.test.jsx.snap
@@ -113,7 +113,7 @@ exports[`Guide Component > GuideStep.body works fine 1`] = `
class="t-portal-wrapper appear appear-active"
>
);
diff --git a/src/swiper/SwiperItem.tsx b/src/swiper/SwiperItem.tsx
index fda99c6ea0..6a2af5969b 100644
--- a/src/swiper/SwiperItem.tsx
+++ b/src/swiper/SwiperItem.tsx
@@ -42,7 +42,7 @@ const calculateTranslate = (index: number, currentIndex: number, parentWidth: nu
return ((2 + itemWidth * (CARD_SCALE - 1)) * parentWidth) / 2;
};
-const getZindex = (isActivity, inStage) => {
+const getZindex = (isActivity: boolean, inStage: boolean) => {
if (isActivity) {
return 2;
}
@@ -52,7 +52,7 @@ const getZindex = (isActivity, inStage) => {
return 0;
};
-const SwiperItem = (props: SwiperItemProps) => {
+const SwiperItem: React.FC = (props) => {
const {
children,
currentIndex,
@@ -68,7 +68,7 @@ const SwiperItem = (props: SwiperItemProps) => {
const [, setUpdate] = useState({});
const isFirstFirstRender = useIsFirstRender();
- const getSwiperItemStyle = () => {
+ const getSwiperItemStyle = (): React.CSSProperties => {
if (animation === 'fade') {
return {
opacity: currentIndex === index ? 1 : 0,
diff --git a/src/swiper/__tests__/swiper.test.tsx b/src/swiper/__tests__/swiper.test.tsx
index 1be5398bb6..40e77b8d2b 100644
--- a/src/swiper/__tests__/swiper.test.tsx
+++ b/src/swiper/__tests__/swiper.test.tsx
@@ -72,7 +72,7 @@ describe('Swiper 组件测试', () => {
expect(document.querySelector('.t-swiper__container')).not.toBeNull();
fireEvent.click(document.querySelector('.t-swiper__arrow-right'));
- expect(document.querySelector('.t-swiper__container').getAttribute('style')).toBe(
+ expect(document.querySelector('.t-swiper__container')?.getAttribute('style')).toBe(
'transform: translate3d(-200%, 0px, 0px); transition: transform 0.3s ease;',
);
fireEvent.click(document.querySelector('.t-swiper__arrow-left'));
diff --git a/src/switch/Switch.tsx b/src/switch/Switch.tsx
index a63d13395a..6dda377770 100644
--- a/src/switch/Switch.tsx
+++ b/src/switch/Switch.tsx
@@ -1,4 +1,4 @@
-import React, { forwardRef, useEffect, useState } from 'react';
+import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
import Loading from '../loading';
import useConfig from '../hooks/useConfig';
@@ -8,14 +8,16 @@ import { SwitchValue, TdSwitchProps } from './type';
import { switchDefaultProps } from './defaultProps';
import log from '../_common/js/log';
import parseTNode from '../_util/parseTNode';
+import useDefaultProps from '../hooks/useDefaultProps';
export type SwitchChangeEventHandler = (value: boolean, event: React.MouseEvent) => void;
export type SwitchClickEventHandler = SwitchChangeEventHandler;
export interface SwitchProps extends TdSwitchProps, StyledProps {}
-const Switch = forwardRef((props, ref) => {
+const Switch = React.forwardRef((originalProps, ref) => {
const { classPrefix } = useConfig();
+ const props = useDefaultProps>(originalProps, switchDefaultProps);
const { className, value, defaultValue, disabled, loading, size, label, customValue, onChange, ...restProps } = props;
const [activeValue = true, inactiveValue = false] = customValue || [];
@@ -23,24 +25,23 @@ const Switch = forwardRef((props, ref) => {
const initChecked = defaultValue === activeValue || value === activeValue;
const [innerChecked, setInnerChecked] = useState(initChecked);
- function renderContent(checked: boolean) {
+ const contentNode = React.useMemo(() => {
if (Array.isArray(label)) {
const [activeContent = '', inactiveContent = ''] = label;
- const content = checked ? activeContent : inactiveContent;
+ const content = innerChecked ? activeContent : inactiveContent;
return parseTNode(content, { value });
}
-
return parseTNode(label, { value });
- }
-
- function onInternalClick(e: React.MouseEvent) {
- if (disabled) return;
+ }, [label, innerChecked, value]);
+ const onInternalClick: React.MouseEventHandler = (e) => {
+ if (disabled) {
+ return;
+ }
!isControlled && setInnerChecked(!innerChecked);
-
const changedValue = !innerChecked ? activeValue : inactiveValue;
onChange?.(changedValue, { e });
- }
+ };
useEffect(() => {
if (Array.isArray(customValue) && !customValue.includes(value)) {
@@ -71,17 +72,14 @@ const Switch = forwardRef((props, ref) => {
ref={ref}
onClick={onInternalClick}
>
- {loading && }
- {renderContent(innerChecked)}
+ {loading && }
+ {contentNode}
);
});
Switch.displayName = 'Switch';
-Switch.defaultProps = switchDefaultProps;
export default Switch as (
- props: SwitchProps & {
- ref?: React.Ref;
- },
+ props: SwitchProps & React.RefAttributes,
) => React.ReactElement;
diff --git a/src/table/BaseTable.tsx b/src/table/BaseTable.tsx
index e3fa8fe547..59ce4e0de2 100644
--- a/src/table/BaseTable.tsx
+++ b/src/table/BaseTable.tsx
@@ -1,10 +1,19 @@
-import React, { useRef, useMemo, useImperativeHandle, forwardRef, useEffect, useState, WheelEvent } from 'react';
+import React, {
+ useRef,
+ useMemo,
+ useImperativeHandle,
+ forwardRef,
+ useEffect,
+ useState,
+ WheelEvent,
+ RefAttributes,
+} from 'react';
import pick from 'lodash/pick';
import classNames from 'classnames';
import TBody, { extendTableProps, TableBodyProps } from './TBody';
-import { Affix } from '../affix';
+import { Affix, AffixRef } from '../affix';
import { ROW_LISTENERS } from './TR';
-import THead from './THead';
+import THead, { TheadProps } from './THead';
import TFoot from './TFoot';
import useTableHeader from './hooks/useTableHeader';
import useColumnResize from './hooks/useColumnResize';
@@ -23,6 +32,7 @@ import { TableRowData } from './type';
import useVirtualScroll from '../hooks/useVirtualScroll';
import { getIEVersion } from '../_common/js/utils/helper';
import log from '../_common/js/log';
+import useDefaultProps from '../hooks/useDefaultProps';
export const BASE_TABLE_EVENTS = ['page-change', 'cell-click', 'scroll', 'scrollX', 'scrollY'];
export const BASE_TABLE_ALL_EVENTS = ROW_LISTENERS.map((t) => `row-${t}`).concat(BASE_TABLE_EVENTS);
@@ -31,7 +41,8 @@ export interface TableListeners {
[key: string]: Function;
}
-const BaseTable = forwardRef((props, ref) => {
+const BaseTable = forwardRef((originalProps, ref) => {
+ const props = useDefaultProps>(originalProps, baseTableDefaultProps);
const {
showHeader = true,
tableLayout,
@@ -62,10 +73,10 @@ const BaseTable = forwardRef((props, ref) => {
);
const { showElement } = useElementLazyRender(tableRef, lazyLoad);
- const paginationAffixRef = useRef();
- const horizontalScrollAffixRef = useRef();
- const headerTopAffixRef = useRef();
- const footerBottomAffixRef = useRef();
+ const paginationAffixRef = useRef();
+ const horizontalScrollAffixRef = useRef();
+ const headerTopAffixRef = useRef();
+ const footerBottomAffixRef = useRef();
// 1. 表头吸顶;2. 表尾吸底;3. 底部滚动条吸底;4. 分页器吸底
const {
@@ -284,7 +295,7 @@ const BaseTable = forwardRef((props, ref) => {
})}
);
- const headProps = {
+ const headProps: TheadProps = {
isFixedHeader,
rowAndColFixedPosition,
isMultipleHeader,
@@ -727,10 +738,6 @@ const BaseTable = forwardRef((props, ref) => {
BaseTable.displayName = 'BaseTable';
-BaseTable.defaultProps = baseTableDefaultProps;
-
export default BaseTable as (
- props: BaseTableProps & {
- ref?: React.Ref;
- },
+ props: BaseTableProps & RefAttributes,
) => React.ReactElement;
diff --git a/src/table/Cell.tsx b/src/table/Cell.tsx
index eec7f5e1c1..34fce64796 100644
--- a/src/table/Cell.tsx
+++ b/src/table/Cell.tsx
@@ -1,4 +1,4 @@
-import React, { MouseEvent, MutableRefObject } from 'react';
+import React, { MouseEvent, MutableRefObject, ReactNode } from 'react';
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
import get from 'lodash/get';
@@ -12,7 +12,7 @@ import { TooltipProps } from '../tooltip';
import { PaginationProps } from '../pagination';
export interface RenderEllipsisCellParams {
- cellNode: any;
+ cellNode: ReactNode;
tableElm?: HTMLDivElement;
columnLength: number;
classPrefix?: string;
diff --git a/src/table/EditableCell.tsx b/src/table/EditableCell.tsx
index 062dda0d28..db3d18b3eb 100644
--- a/src/table/EditableCell.tsx
+++ b/src/table/EditableCell.tsx
@@ -128,7 +128,7 @@ const EditableCell = (props: EditableCellProps) => {
}, [col]);
const validateEdit = (trigger: 'self' | 'parent', newVal: any) =>
- new Promise((resolve) => {
+ new Promise((resolve) => {
const params: PrimaryTableRowValidateContext = {
result: [
{
diff --git a/src/table/EnhancedTable.tsx b/src/table/EnhancedTable.tsx
index 135ecfedf4..0c2316ea8b 100644
--- a/src/table/EnhancedTable.tsx
+++ b/src/table/EnhancedTable.tsx
@@ -1,4 +1,4 @@
-import React, { forwardRef, useImperativeHandle, useRef } from 'react';
+import React, { RefAttributes, forwardRef, useImperativeHandle, useRef } from 'react';
import get from 'lodash/get';
import PrimaryTable from './PrimaryTable';
import { PrimaryTableCol, TableRowData, DragSortContext, TdPrimaryTableProps } from './type';
@@ -106,7 +106,5 @@ const EnhancedTable = forwardRef((props,
EnhancedTable.displayName = 'EnhancedTable';
export default EnhancedTable as (
- props: EnhancedTableProps & {
- ref?: React.Ref;
- },
+ props: EnhancedTableProps & RefAttributes,
) => React.ReactElement;
diff --git a/src/table/PrimaryTable.tsx b/src/table/PrimaryTable.tsx
index e619a6b4f3..a62b8f0348 100644
--- a/src/table/PrimaryTable.tsx
+++ b/src/table/PrimaryTable.tsx
@@ -1,4 +1,4 @@
-import React, { useRef, forwardRef, useImperativeHandle } from 'react';
+import React, { useRef, forwardRef, useImperativeHandle, ReactNode, RefAttributes } from 'react';
import get from 'lodash/get';
import classNames from 'classnames';
import BaseTable from './BaseTable';
@@ -20,12 +20,14 @@ import { StyledProps } from '../common';
import { useEditableRow } from './hooks/useEditableRow';
import { primaryTableDefaultProps } from './defaultProps';
import { CheckboxGroupValue } from '../checkbox';
+import useDefaultProps from '../hooks/useDefaultProps';
export { BASE_TABLE_ALL_EVENTS } from './BaseTable';
export interface TPrimaryTableProps extends PrimaryTableProps, StyledProps {}
-const PrimaryTable = forwardRef((props, ref) => {
+const PrimaryTable = forwardRef((originalProps, ref) => {
+ const props = useDefaultProps(originalProps, primaryTableDefaultProps);
const { columns, columnController, editableRowKeys, style, className } = props;
const primaryTableRef = useRef(null);
const innerPagination = useRef(props.pagination);
@@ -207,7 +209,12 @@ const PrimaryTable = forwardRef((props, ref
}
};
- function formatNode(api: string, renderInnerNode: Function, condition: boolean, extra?: { reverse?: boolean }) {
+ function formatNode(
+ api: string,
+ renderInnerNode: () => ReactNode,
+ condition: boolean,
+ extra?: { reverse?: boolean },
+ ) {
if (!condition) return props[api];
const innerNode = renderInnerNode();
const propsNode = props[api];
@@ -271,10 +278,6 @@ const PrimaryTable = forwardRef((props, ref
PrimaryTable.displayName = 'PrimaryTable';
-PrimaryTable.defaultProps = primaryTableDefaultProps;
-
export default PrimaryTable as (
- props: PrimaryTableProps & {
- ref?: React.Ref;
- },
+ props: PrimaryTableProps & RefAttributes,
) => React.ReactElement;
diff --git a/src/table/TBody.tsx b/src/table/TBody.tsx
index e744ed245d..01b00220a5 100644
--- a/src/table/TBody.tsx
+++ b/src/table/TBody.tsx
@@ -1,4 +1,4 @@
-import React, { MutableRefObject, useMemo } from 'react';
+import React, { CSSProperties, MutableRefObject, ReactNode, useMemo } from 'react';
import camelCase from 'lodash/camelCase';
import get from 'lodash/get';
import pick from 'lodash/pick';
@@ -123,7 +123,7 @@ export default function TBody(props: TableBodyProps) {
const getTRNodeList = () => {
if (isSkipSnapsMapNotFinish) return null;
- const trNodeList = [];
+ const trNodeList: ReactNode[] = [];
const properties = [
'classPrefix',
'ellipsisOverlayClassName',
@@ -182,13 +182,13 @@ export default function TBody(props: TableBodyProps) {
// 垫上隐藏的 tr 元素高度
const translate = `translateY(${virtualConfig.translateY}px)`;
- const posStyle = virtualConfig.isVirtualScroll
- ? {
+ const posStyle: CSSProperties = virtualConfig.isVirtualScroll
+ ? ({
transform: translate,
msTransform: translate,
MozTransform: translate,
WebkitTransform: translate,
- }
+ } as CSSProperties)
: undefined;
const list = (
diff --git a/src/table/TFoot.tsx b/src/table/TFoot.tsx
index b8ef9dcf5c..5356397ea0 100644
--- a/src/table/TFoot.tsx
+++ b/src/table/TFoot.tsx
@@ -27,7 +27,7 @@ export interface TFootProps {
export default function TFoot(props: TFootProps) {
const { footData, columns, rowKey, footerSummary } = props;
- const tfooterRef = useRef();
+ const tfooterRef = useRef();
const classnames = useClassName();
const { skipSpansMap } = useRowspanAndColspan(footData, columns, rowKey, props.rowspanAndColspanInFooter);
diff --git a/src/table/THead.tsx b/src/table/THead.tsx
index e501f20035..dae5c9bd2b 100644
--- a/src/table/THead.tsx
+++ b/src/table/THead.tsx
@@ -36,8 +36,15 @@ export interface TheadProps {
columnResizeParams?: {
resizeLineRef: MutableRefObject;
resizeLineStyle: CSSProperties;
- onColumnMouseover: (e: MouseEvent, col: BaseTableCol) => void;
- onColumnMousedown: (e: MouseEvent, col: BaseTableCol, index: number) => void;
+ onColumnMouseover: (
+ e: React.MouseEvent,
+ col: BaseTableCol,
+ ) => void;
+ onColumnMousedown: (
+ e: React.MouseEvent,
+ col: BaseTableCol,
+ index: number,
+ ) => void;
};
}
@@ -72,8 +79,8 @@ export default function THead(props: TheadProps) {
return map;
}, [props.thList]);
- const getTableNode = (thead: HTMLElement) => {
- let parent = thead;
+ const getTableNode = (thead: HTMLTableSectionElement) => {
+ let parent: HTMLElement = thead;
while (parent) {
parent = parent.parentNode as HTMLElement;
if (parent?.classList?.contains(`${props.classPrefix}-table`)) {
@@ -85,7 +92,7 @@ export default function THead(props: TheadProps) {
const renderThNodeList = (rowAndColFixedPosition: RowAndColFixedPosition, thWidthList: TheadProps['thWidthList']) => {
// thBorderMap: rowspan 会影响 tr > th 是否为第一列表头,从而影响边框
- const thBorderMap = new Map();
+ const thBorderMap = new Map, boolean>();
const thRowspanAndColspan = props.spansAndLeafNodes.rowspanAndColspanMap;
return props.thList.map((row, rowIndex) => {
const thRow = row.map((col: TableColumns[0], index: number) => {
@@ -130,19 +137,19 @@ export default function THead(props: TheadProps) {
const resizeColumnListener =
props.resizable || !canDragSort
? {
- onMouseDown: (e) => {
+ onMouseDown: (e: React.MouseEvent) => {
if (props.resizable) {
columnResizeParams?.onColumnMousedown?.(e, col, index);
}
if (!canDragSort) {
const timer = setTimeout(() => {
- const thList = theadRef.current.querySelectorAll('th');
+ const thList = theadRef.current.querySelectorAll('th');
thList[index]?.removeAttribute('draggable');
clearTimeout(timer);
}, 10);
}
},
- onMouseMove: (e) => {
+ onMouseMove: (e: React.MouseEvent) => {
props.resizable && columnResizeParams?.onColumnMouseover?.(e, col);
},
}
diff --git a/src/table/hooks/useColumnResize.tsx b/src/table/hooks/useColumnResize.tsx
index 0df3ddb807..fb7d14ebd5 100644
--- a/src/table/hooks/useColumnResize.tsx
+++ b/src/table/hooks/useColumnResize.tsx
@@ -5,7 +5,7 @@
* - 当表格内容没有超出时,即没有出现横向滚动条时,此时认为表格有足够的列宽呈现内容,修改宽度为相邻宽度调整
* - 当表格内容超出,出现横向滚动条时,会自动调整当前列宽和表格总列宽,不影响相邻列宽
*/
-import { useState, useRef, MutableRefObject, CSSProperties, useEffect } from 'react';
+import React, { useState, useRef, MutableRefObject, CSSProperties, useEffect } from 'react';
import isNumber from 'lodash/isNumber';
import { BaseTableCol, TableRowData, TdBaseTableProps } from '../type';
import { on, off } from '../../_util/dom';
@@ -108,7 +108,10 @@ export default function useColumnResize(params: {
// 表格列宽拖拽事件
// 只在表头显示拖拽图标
- const onColumnMouseover = (e: MouseEvent, col: BaseTableCol) => {
+ const onColumnMouseover = (
+ e: React.MouseEvent,
+ col: BaseTableCol,
+ ) => {
// calculate mouse cursor before drag start
if (!resizeLineRef.current || resizeLineParams.isDragging || !e.target) return;
const target = (e.target as HTMLElement).closest('th');
@@ -200,7 +203,11 @@ export default function useColumnResize(params: {
};
// 调整表格列宽
- const onColumnMousedown = (e: MouseEvent, col: BaseTableCol, index: number) => {
+ const onColumnMousedown = (
+ e: React.MouseEvent,
+ col: BaseTableCol,
+ index: number,
+ ) => {
if (!resizeLineParams.draggingCol) return;
const target = resizeLineParams.draggingCol;
const targetBoundRect = target.getBoundingClientRect();
@@ -217,7 +224,7 @@ export default function useColumnResize(params: {
// 初始化 resizeLine 标记线
if (resizeLineRef?.current) {
- const styles = { ...resizeLineStyle };
+ const styles: CSSProperties = { ...resizeLineStyle };
styles.display = 'block';
styles.height = `${tableBoundRect.bottom - targetBoundRect.top}px`;
styles.left = `${resizeLinePos}px`;
diff --git a/src/table/hooks/useFixed.ts b/src/table/hooks/useFixed.ts
index 53e97ec683..0b152dcf15 100644
--- a/src/table/hooks/useFixed.ts
+++ b/src/table/hooks/useFixed.ts
@@ -442,7 +442,6 @@ export default function useFixed(
finalColumns: BaseTableCol[] = [],
preFinalColumns: BaseTableCol[] = [],
) => {
- console.log(preFinalColumns);
const finalColKeys = finalColumns.map((t) => t.colKey);
const preColKeys = preFinalColumns.map((t) => t.colKey);
if (finalColKeys.length < preColKeys.length) {
diff --git a/src/table/utils.ts b/src/table/utils.ts
index 76d9c8d967..32fb09024d 100644
--- a/src/table/utils.ts
+++ b/src/table/utils.ts
@@ -85,7 +85,7 @@ export function formatClassNames(
for (let i = 0, len = classes.length; i < len; i++) {
const cls = classes[i];
if (isFunction(cls)) {
- arr.push(cls(params));
+ arr.push(cls(params as CellData));
} else {
arr.push(cls);
}
diff --git a/src/tabs/TabBar.tsx b/src/tabs/TabBar.tsx
index 55045eaed4..e240d5f14a 100644
--- a/src/tabs/TabBar.tsx
+++ b/src/tabs/TabBar.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState, CSSProperties, useCallback, useRef } from 'react';
+import React, { useEffect, useState, CSSProperties, useRef } from 'react';
import classNames from 'classnames';
import useConfig from '../hooks/useConfig';
import useMutationObserver from '../_util/useMutationObserver';
@@ -14,13 +14,12 @@ const TabBar: React.FC = (props) => {
const { classPrefix } = useConfig();
const [barStyle, setBarStyle] = useState({});
const tabsClassPrefix = `${classPrefix}-tabs`;
-
const currentActiveIdRef = useRef(activeId);
useEffect(() => {
currentActiveIdRef.current = activeId;
}, [activeId]);
- const computeStyle = useCallback(() => {
+ const computeStyle = React.useCallback(() => {
const isHorizontal = ['bottom', 'top'].includes(tabPosition);
const transformPosition = isHorizontal ? 'translateX' : 'translateY';
const itemProp = isHorizontal ? 'width' : 'height';
@@ -29,26 +28,20 @@ const TabBar: React.FC = (props) => {
let offset = 0;
if (containerRef.current) {
- const itemsRef = containerRef.current.querySelectorAll?.(`.${tabsClassPrefix}__nav-item`);
- if (itemsRef.length - 1 >= currentActiveIdRef.current) {
+ const itemsRef = containerRef.current?.querySelectorAll(`.${tabsClassPrefix}__nav-item`);
+ if (itemsRef.length - 1 >= (currentActiveIdRef.current as number)) {
itemsRef.forEach((item, itemIndex) => {
- if (itemIndex < currentActiveIdRef.current) {
+ if (itemIndex < (currentActiveIdRef.current as number)) {
offset += Number(getComputedStyle(item)[itemProp].replace('px', ''));
}
});
const computedItem = itemsRef[currentActiveIdRef.current];
if (!computedItem) {
- setBarStyle({
- transform: `${transformPosition}(${0}px)`,
- [barBorderProp]: 0,
- });
+ setBarStyle({ transform: `${transformPosition}(${0}px)`, [barBorderProp]: 0 });
return;
}
const itemPropValue = getComputedStyle(computedItem)[itemProp];
- setBarStyle({
- transform: `${transformPosition}(${offset}px)`,
- [barBorderProp]: itemPropValue || 0,
- });
+ setBarStyle({ transform: `${transformPosition}(${offset}px)`, [barBorderProp]: itemPropValue || 0 });
}
}
}, [currentActiveIdRef, containerRef, tabPosition, tabsClassPrefix]);
@@ -60,8 +53,8 @@ const TabBar: React.FC = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tabPosition, activeId, containerRef.current]);
- const handleMutationObserver = useCallback(
- (mutations) => {
+ const handleMutationObserver = React.useCallback(
+ (mutations: MutationRecord[]) => {
mutations.forEach((mutation) => {
if (mutation.type === 'characterData') {
computeStyle();
diff --git a/src/tabs/TabNav.tsx b/src/tabs/TabNav.tsx
index 23cdf8b28d..2b52d1fd07 100644
--- a/src/tabs/TabNav.tsx
+++ b/src/tabs/TabNav.tsx
@@ -14,6 +14,7 @@ import TabBar from './TabBar';
import tabBase from '../_common/js/tabs/base';
import useGlobalIcon from '../hooks/useGlobalIcon';
import type { DragSortInnerProps } from '../_util/useDragSorter';
+import parseTNode from '../_util/parseTNode';
const { moveActiveTabIntoView, calcScrollLeft, scrollToLeft, scrollToRight, calculateCanToLeft, calculateCanToRight } =
tabBase;
@@ -39,6 +40,7 @@ const TabNav: React.FC = (props) => {
onChange = noop,
activeValue,
children,
+ action,
getDragProps,
} = props;
@@ -272,7 +274,17 @@ const TabNav: React.FC = (props) => {
) : null}
- {props.action}
+ {action ? (
+
+ {parseTNode(action)}
+
+ ) : null}
= (props) => {
dragProps,
} = props;
- const { CloseIcon } = useGlobalIcon({
- CloseIcon: TdCloseIcon,
- });
+ const { CloseIcon } = useGlobalIcon({ CloseIcon: TdCloseIcon });
const isCard = theme === 'card';
diff --git a/src/tabs/TabPanel.tsx b/src/tabs/TabPanel.tsx
index 091d2e75c3..cdcb8141de 100644
--- a/src/tabs/TabPanel.tsx
+++ b/src/tabs/TabPanel.tsx
@@ -4,6 +4,7 @@ import { TdTabPanelProps } from './type';
import { StyledProps } from '../common';
import { useTabClass } from './useTabClass';
import { tabPanelDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TabPanelProps extends TdTabPanelProps, StyledProps {
children?: React.ReactNode;
@@ -11,9 +12,7 @@ export interface TabPanelProps extends TdTabPanelProps, StyledProps {
const TabPanel: React.FC
= (props) => {
const { tdTabPanelClassPrefix } = useTabClass();
-
- const { className, style } = props;
-
+ const { className, style } = useDefaultProps(props, tabPanelDefaultProps);
return (
{props.children || props.panel}
@@ -22,6 +21,5 @@ const TabPanel: React.FC
= (props) => {
};
TabPanel.displayName = 'TabPanel';
-TabPanel.defaultProps = tabPanelDefaultProps;
export default TabPanel;
diff --git a/src/tabs/Tabs.tsx b/src/tabs/Tabs.tsx
index 507c834be0..c11095e9bf 100644
--- a/src/tabs/Tabs.tsx
+++ b/src/tabs/Tabs.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect } from 'react';
import classNames from 'classnames';
import { TabValue, TdTabsProps } from './type';
import forwardRefWithStatics from '../_util/forwardRefWithStatics';
@@ -8,15 +8,27 @@ import TabPanel from './TabPanel';
import { StyledProps } from '../common';
import { tabsDefaultProps } from './defaultProps';
import useDragSorter from '../_util/useDragSorter';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TabsProps extends TdTabsProps, StyledProps {
children?: React.ReactNode;
}
const Tabs = forwardRefWithStatics(
- (props: TabsProps, ref) => {
- const { children, list, placement, onRemove, value: tabValue, onChange, className, style } = props;
- let { defaultValue } = props;
+ (originalProps: TabsProps, ref: React.Ref) => {
+ const props = useDefaultProps(originalProps, tabsDefaultProps);
+ const {
+ defaultValue,
+ children,
+ list,
+ placement,
+ value: tabValue,
+ dragSort,
+ className,
+ style,
+ onRemove,
+ onChange,
+ } = props;
// 样式工具引入
const { tdTabsClassPrefix, tdTabsClassGenerator, tdClassGenerator } = useTabClass();
@@ -24,21 +36,21 @@ const Tabs = forwardRefWithStatics(
const { getDragProps } = useDragSorter({
...props,
- sortOnDraggable: props.dragSort,
+ sortOnDraggable: dragSort,
onDragOverCheck: {
x: true,
targetClassNameRegExp: new RegExp(targetClassNameRegExpStr),
},
});
- const memoChildren = useMemo(() => {
+ const memoChildren = React.useMemo(() => {
if (!list || list.length === 0) {
return children;
}
- return list.map((panelProps) => );
+ return list.map((panelProps) => );
}, [children, list]);
- const itemList = React.Children.map(memoChildren, (child: any) => {
+ const itemList = React.Children.map(memoChildren, (child: React.ReactElement) => {
if (child && child.type === TabPanel) {
return child.props;
}
@@ -46,46 +58,66 @@ const Tabs = forwardRefWithStatics(
});
// 当未设置默认值时,默认选中第一个。
- if (defaultValue === undefined && Array.isArray(itemList) && itemList.length !== 0) {
- defaultValue = itemList[0].value;
- }
-
- const [value, setValue] = useState(defaultValue);
+ const [value, setValue] = React.useState(
+ defaultValue === undefined && Array.isArray(itemList) && itemList.length !== 0 ? itemList[0].value : defaultValue,
+ );
useEffect(() => {
- tabValue !== undefined && setValue(tabValue);
+ if (tabValue !== undefined) {
+ setValue(tabValue);
+ }
}, [tabValue]);
- const handleChange = (v) => {
- if (tabValue === undefined) {
- setValue(v);
- }
- onChange?.(v);
- };
+ const handleChange = React.useCallback(
+ (v: TabValue) => {
+ if (tabValue === undefined) {
+ setValue(v);
+ }
+ onChange?.(v);
+ },
+ [tabValue, onChange],
+ );
- const handleClickTab = (v) => {
- if (tabValue === undefined) {
- setValue(v);
- }
- };
+ const handleClickTab = React.useCallback(
+ (v: TabValue) => {
+ if (tabValue === undefined) {
+ setValue(v);
+ }
+ },
+ [tabValue],
+ );
- const renderHeader = () => (
-
-
-
+ const headerNode = React.useMemo(
+ () => (
+
+
+
+ ),
+ [
+ props,
+ getDragProps,
+ value,
+ onRemove,
+ itemList,
+ handleClickTab,
+ handleChange,
+ placement,
+ tdTabsClassGenerator,
+ tdClassGenerator,
+ ],
);
return (
- {placement !== 'bottom' ? renderHeader() : null}
+ {placement !== 'bottom' ? headerNode : null}
{React.Children.map(memoChildren, (child: any) => {
if (child && child.type === TabPanel) {
@@ -99,7 +131,7 @@ const Tabs = forwardRefWithStatics(
return null;
})}
- {placement === 'bottom' ? renderHeader() : null}
+ {placement === 'bottom' ? headerNode : null}
);
},
@@ -107,6 +139,5 @@ const Tabs = forwardRefWithStatics(
);
Tabs.displayName = 'Tabs';
-Tabs.defaultProps = tabsDefaultProps;
export default Tabs;
diff --git a/src/tabs/__tests__/tabs.test.tsx b/src/tabs/__tests__/tabs.test.tsx
index 0804841533..59c7ba35a2 100644
--- a/src/tabs/__tests__/tabs.test.tsx
+++ b/src/tabs/__tests__/tabs.test.tsx
@@ -340,4 +340,26 @@ describe('Tabs 组件测试', () => {
expect(onDragSort.mock.calls[0][0].target.value).toEqual('vue');
expect(container.querySelectorAll('.t-tabs__nav-item-text-wrapper').item(0).firstChild.nodeValue).toEqual('react');
});
+
+ test('Tabs action', () => {
+ const { container } = render(
+ ,
+ );
+
+ expect(container.querySelector('.t-tabs__nav-action')).not.toBeNull();
+ expect(container.querySelector('.t-tabs__nav-action')).toBeInTheDocument();
+ });
});
diff --git a/src/tag-input/TagInput.tsx b/src/tag-input/TagInput.tsx
index 900963af8b..24b1de9fa3 100644
--- a/src/tag-input/TagInput.tsx
+++ b/src/tag-input/TagInput.tsx
@@ -13,10 +13,12 @@ import useHover from './useHover';
import useControlled from '../hooks/useControlled';
import { StyledProps } from '../common';
import { tagInputDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TagInputProps extends TdTagInputProps, StyledProps {}
-const TagInput = forwardRef((props: TagInputProps, ref: React.RefObject) => {
+const TagInput = forwardRef((originalProps, ref) => {
+ const props = useDefaultProps(originalProps, tagInputDefaultProps);
const { classPrefix: prefix } = useConfig();
const { CloseCircleFilledIcon } = useGlobalIcon({
CloseCircleFilledIcon: TdCloseCircleFilledIcon,
@@ -183,6 +185,5 @@ const TagInput = forwardRef((props: TagInputProps, ref: React.RefObject) => {
+const CheckTag = forwardRef((originalProps, ref) => {
+ const props = useDefaultProps(originalProps, checkTagDefaultProps);
const {
value,
content,
@@ -101,6 +103,5 @@ const CheckTag = forwardRef((props: CheckTagProps, ref: React.Ref {
+const CheckTagGroup: React.FC = (originalProps) => {
+ const props = useDefaultProps(originalProps, checkTagGroupDefaultProps);
const { options, onChange } = props;
const { classPrefix } = useConfig();
const componentName = `${classPrefix}-check-tag-group`;
@@ -54,6 +56,5 @@ const CheckTagGroup = (props: CheckTagGroupProps) => {
};
CheckTagGroup.displayName = 'CheckTagGroup';
-CheckTagGroup.defaultProps = checkTagGroupDefaultProps;
export default CheckTagGroup;
diff --git a/src/tag/Tag.tsx b/src/tag/Tag.tsx
index 987f501907..d7aff2f979 100644
--- a/src/tag/Tag.tsx
+++ b/src/tag/Tag.tsx
@@ -1,12 +1,15 @@
-import React, { FocusEvent, forwardRef } from 'react';
+import React, { ForwardRefRenderFunction, FocusEvent, forwardRef, useMemo } from 'react';
import classNames from 'classnames';
import { CloseIcon as TdCloseIcon } from 'tdesign-icons-react';
+import tinycolor from 'tinycolor2';
+
import noop from '../_util/noop';
import useConfig from '../hooks/useConfig';
import useGlobalIcon from '../hooks/useGlobalIcon';
import { StyledProps } from '../common';
import { TdTagProps } from './type';
import { tagDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
/**
* Tag 组件支持的属性。
@@ -21,7 +24,8 @@ export interface TagProps extends TdTagProps, StyledProps {
onBlur?: (e: FocusEvent) => void;
}
-export function TagFunction(props: TagProps, ref: React.Ref) {
+export const TagFunction: ForwardRefRenderFunction = (originalProps, ref) => {
+ const props = useDefaultProps(originalProps, tagDefaultProps);
const {
theme,
size,
@@ -37,6 +41,7 @@ export function TagFunction(props: TagProps, ref: React.Ref) {
style,
disabled,
children,
+ color,
...otherTagProps
} = props;
@@ -83,6 +88,32 @@ export function TagFunction(props: TagProps, ref: React.Ref) {
})();
const titleAttribute = title ? { title } : undefined;
+ const getTagStyle = useMemo(() => {
+ if (!color) return style;
+ const luminance = tinycolor(color).getLuminance();
+
+ const calculatedStyle = style || {};
+
+ calculatedStyle.color = luminance > 0.5 ? 'black' : 'white';
+ if (variant === 'outline' || variant === 'light-outline') {
+ calculatedStyle.borderColor = color;
+ }
+
+ if (variant !== 'outline') {
+ const getLightestShade = () => {
+ const { r, g, b } = tinycolor(color).toRgb();
+ // alpha 0.1 is designed by @wen1kang
+ return `rgba(${r}, ${g}, ${b}, 0.1)`;
+ };
+
+ calculatedStyle.backgroundColor = variant === 'dark' ? color : getLightestShade();
+ }
+ if (variant !== 'dark') {
+ calculatedStyle.color = color;
+ }
+ return calculatedStyle;
+ }, [color, variant, style]);
+
const tag = (
) {
if (disabled) return;
onClick({ e });
}}
- style={maxWidth ? { maxWidth: typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth, ...style } : style}
+ style={
+ maxWidth ? { maxWidth: typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth, ...getTagStyle } : getTagStyle
+ }
{...otherTagProps}
>
<>
@@ -105,11 +138,10 @@ export function TagFunction(props: TagProps, ref: React.Ref
) {
);
return tag;
-}
+};
export const Tag = forwardRef(TagFunction);
Tag.displayName = 'Tag';
-Tag.defaultProps = tagDefaultProps;
export default Tag;
diff --git a/src/tag/_example/custom-color.jsx b/src/tag/_example/custom-color.jsx
new file mode 100644
index 0000000000..4d32f506b6
--- /dev/null
+++ b/src/tag/_example/custom-color.jsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react';
+import { Space, Tag, ColorPicker } from 'tdesign-react';
+
+export default function CustomColor() {
+ const [selfDefinedColor, changeSelfDefinedColor] = useState('#0052D9');
+ return (
+
+
+ changeSelfDefinedColor(v)} />
+
+
+
+ default
+
+
+ light
+
+
+ outline
+
+
+ light-outline
+
+
+
+ );
+}
diff --git a/src/tag/index.ts b/src/tag/index.ts
index 8403c65abf..b0719edfa1 100644
--- a/src/tag/index.ts
+++ b/src/tag/index.ts
@@ -2,8 +2,6 @@ import { TagFunction } from './Tag';
import _CheckTag from './CheckTag';
import _CheckTagGroup from './CheckTagGroup';
import forwardRefWithStatics from '../_util/forwardRefWithStatics';
-import { tagDefaultProps } from './defaultProps';
-
import './style/index.js';
export type { TagProps } from './Tag';
@@ -19,7 +17,6 @@ export const Tag = forwardRefWithStatics(TagFunction, {
});
Tag.displayName = 'Tag';
-Tag.defaultProps = tagDefaultProps;
export const CheckTag = _CheckTag;
export const CheckTagGroup = _CheckTagGroup;
diff --git a/src/tag/tag.en-US.md b/src/tag/tag.en-US.md
index 378dc44c58..9540a520bc 100644
--- a/src/tag/tag.en-US.md
+++ b/src/tag/tag.en-US.md
@@ -1,14 +1,16 @@
:: BASE_DOC ::
## API
+
### Tag Props
name | type | default | description | required
-- | -- | -- | -- | --
-className | String | - | 类名 | N
-style | Object | - | 样式,Typescript:`React.CSSProperties` | N
+className | String | - | className of component | N
+style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
children | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
closable | Boolean | false | \- | N
+color | String | - | self-defined tag color | N
content | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
disabled | Boolean | false | \- | N
icon | TElement | undefined | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
@@ -20,12 +22,13 @@ variant | String | dark | options: dark/light/outline/light-outline | N
onClick | Function | | Typescript:`(context: { e: MouseEvent }) => void`
| N
onClose | Function | | Typescript:`(context: { e: MouseEvent }) => void`
| N
+
### CheckTag Props
name | type | default | description | required
-- | -- | -- | -- | --
-className | String | - | 类名 | N
-style | Object | - | 样式,Typescript:`React.CSSProperties` | N
+className | String | - | className of component | N
+style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
checked | Boolean | - | \- | N
defaultChecked | Boolean | - | uncontrolled property | N
checkedProps | Object | - | used to set checked tag props。Typescript:`TdTagProps` | N
@@ -38,12 +41,13 @@ value | String / Number | - | tag unique key | N
onChange | Function | | Typescript:`(checked: boolean, context: CheckTagChangeContext) => void`
[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/tag/type.ts)。
`interface CheckTagChangeContext { e: MouseEvent \| KeyboardEvent; value: string \| number }`
| N
onClick | Function | | Typescript:`(context: { e: MouseEvent }) => void`
| N
+
### CheckTagGroup Props
name | type | default | description | required
-- | -- | -- | -- | --
-className | String | - | 类名 | N
-style | Object | - | 样式,Typescript:`React.CSSProperties` | N
+className | String | - | className of component | N
+style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
checkedProps | Object | - | used to set checked tag props。Typescript:`TdTagProps` | N
multiple | Boolean | false | allow to select multiple tags | N
options | Array | - | tag list。Typescript:`CheckTagGroupOption[]` `interface CheckTagGroupOption extends TdCheckTagProps { label: string \| TNode; value: string \| number }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/tag/type.ts) | N
diff --git a/src/tag/tag.md b/src/tag/tag.md
index ea477a8df0..2c065b96d7 100644
--- a/src/tag/tag.md
+++ b/src/tag/tag.md
@@ -1,14 +1,16 @@
:: BASE_DOC ::
## API
+
### Tag Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
children | TNode | - | 组件子元素,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
closable | Boolean | false | 标签是否可关闭 | N
+color | String | - | 自定义标签颜色 | N
content | TNode | - | 组件子元素。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
disabled | Boolean | false | 标签禁用态,失效标签不能触发事件。默认风格(theme=default)才有禁用态 | N
icon | TElement | undefined | 标签中的图标,可自定义图标呈现。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
@@ -20,9 +22,10 @@ variant | String | dark | 标签风格变体。可选项:dark/light/outline/li
onClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`
点击时触发 | N
onClose | Function | | TS 类型:`(context: { e: MouseEvent }) => void`
如果关闭按钮存在,点击关闭按钮时触发 | N
+
### CheckTag Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
@@ -38,9 +41,10 @@ value | String / Number | - | 标签唯一标识,一般用于标签组场景
onChange | Function | | TS 类型:`(checked: boolean, context: CheckTagChangeContext) => void`
状态切换时触发。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/tag/type.ts)。
`interface CheckTagChangeContext { e: MouseEvent \| KeyboardEvent; value: string \| number }`
| N
onClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`
点击标签时触发 | N
+
### CheckTagGroup Props
-名称 | 类型 | 默认值 | 说明 | 必传
+名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
diff --git a/src/tag/type.ts b/src/tag/type.ts
index 71729aee00..db009a7052 100644
--- a/src/tag/type.ts
+++ b/src/tag/type.ts
@@ -17,6 +17,11 @@ export interface TdTagProps {
* @default false
*/
closable?: boolean;
+ /**
+ * 自定义标签颜色
+ * @default ''
+ */
+ color?: string;
/**
* 组件子元素
*/
diff --git a/src/textarea/Textarea.tsx b/src/textarea/Textarea.tsx
index 1f1403ea41..4fcef82859 100644
--- a/src/textarea/Textarea.tsx
+++ b/src/textarea/Textarea.tsx
@@ -8,6 +8,7 @@ import useControlled from '../hooks/useControlled';
import { getCharacterLength, getUnicodeLength, limitUnicodeMaxLength } from '../_common/js/utils/helper';
import calcTextareaHeight from '../_common/js/utils/calcTextareaHeight';
import { textareaDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TextareaProps
extends Omit<
@@ -21,7 +22,8 @@ export interface TextareaRefInterface extends React.RefObject {
textareaElement: HTMLTextAreaElement;
}
-const Textarea = forwardRef((props: TextareaProps, ref: TextareaRefInterface) => {
+const Textarea = forwardRef((originalProps: TextareaProps, ref: TextareaRefInterface) => {
+ const props = useDefaultProps(originalProps, textareaDefaultProps);
const {
disabled,
maxlength,
@@ -201,6 +203,5 @@ const Textarea = forwardRef((props: TextareaProps, ref: TextareaRefInterface) =>
});
Textarea.displayName = 'Textarea';
-Textarea.defaultProps = textareaDefaultProps;
export default Textarea;
diff --git a/src/time-picker/TimePicker.tsx b/src/time-picker/TimePicker.tsx
index d6af49b0c8..db2ca6d9b0 100644
--- a/src/time-picker/TimePicker.tsx
+++ b/src/time-picker/TimePicker.tsx
@@ -21,6 +21,7 @@ import { timePickerDefaultProps } from './defaultProps';
import type { StyledProps } from '../common';
import type { TdTimePickerProps } from './type';
+import useDefaultProps from '../hooks/useDefaultProps';
// https://github.com/iamkun/dayjs/issues/1552
dayjs.extend(customParseFormat);
@@ -28,7 +29,8 @@ dayjs.extend(customParseFormat);
export interface TimePickerProps extends TdTimePickerProps, StyledProps {}
const TimePicker = forwardRefWithStatics(
- (props: TimePickerProps, ref: Ref) => {
+ (originalProps: TimePickerProps, ref: Ref) => {
+ const props = useDefaultProps(originalProps, timePickerDefaultProps);
const TEXT_CONFIG = useTimePickerTextConfig();
const {
allowInput,
@@ -147,6 +149,5 @@ const TimePicker = forwardRefWithStatics(
);
TimePicker.displayName = 'TimePicker';
-TimePicker.defaultProps = timePickerDefaultProps;
export default TimePicker;
diff --git a/src/time-picker/TimeRangePicker.tsx b/src/time-picker/TimeRangePicker.tsx
index 28844b4594..3c49318a63 100644
--- a/src/time-picker/TimeRangePicker.tsx
+++ b/src/time-picker/TimeRangePicker.tsx
@@ -16,12 +16,14 @@ import { TIME_PICKER_EMPTY } from '../_common/js/time-picker/const';
import { TdTimeRangePickerProps, TimeRangeValue, TimeRangePickerPartial } from './type';
import { StyledProps } from '../common';
import { timeRangePickerDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TimeRangePickerProps extends TdTimeRangePickerProps, StyledProps {}
const defaultArrVal = [undefined, undefined];
-const TimeRangePicker: FC = (props) => {
+const TimeRangePicker: FC = (originalProps) => {
+ const props = useDefaultProps(originalProps, timeRangePickerDefaultProps);
const TEXT_CONFIG = useTimePickerTextConfig();
const {
@@ -172,6 +174,5 @@ const TimeRangePicker: FC = (props) => {
};
TimeRangePicker.displayName = 'TimeRangePicker';
-TimeRangePicker.defaultProps = timeRangePickerDefaultProps;
export default TimeRangePicker;
diff --git a/src/timeline/useAlign.ts b/src/timeline/useAlign.ts
index 446e89f362..5074858314 100644
--- a/src/timeline/useAlign.ts
+++ b/src/timeline/useAlign.ts
@@ -6,7 +6,7 @@ const DefaultAlign = {
horizontal: ['top', 'bottom'],
};
-export const useAlign = (align, layout = 'vertical') =>
+export const useAlign = (align: string, layout = 'vertical') =>
useMemo(() => {
let renderAlign = layout === 'vertical' ? 'left' : 'top';
if (layout === 'vertical' && align) {
diff --git a/src/tooltip/Tooltip.tsx b/src/tooltip/Tooltip.tsx
index 6cfd516adf..176537519b 100644
--- a/src/tooltip/Tooltip.tsx
+++ b/src/tooltip/Tooltip.tsx
@@ -1,13 +1,15 @@
import React, { forwardRef, useState, useEffect, useRef, useImperativeHandle } from 'react';
import classNames from 'classnames';
-import Popup, { PopupVisibleChangeContext } from '../popup';
+import Popup, { PopupRef, PopupVisibleChangeContext } from '../popup';
import useConfig from '../hooks/useConfig';
import { TdTooltipProps } from './type';
import { tooltipDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
export type TooltipProps = TdTooltipProps;
-const Tooltip = forwardRef((props: TdTooltipProps, ref) => {
+const Tooltip = forwardRef, TdTooltipProps>((originalProps, ref) => {
+ const props = useDefaultProps(originalProps, tooltipDefaultProps);
const {
theme,
showArrow,
@@ -22,7 +24,7 @@ const Tooltip = forwardRef((props: TdTooltipProps, ref) => {
const { classPrefix } = useConfig();
const [timeUp, setTimeUp] = useState(false);
- const popupRef = useRef(null);
+ const popupRef = useRef(null);
const timerRef = useRef(null);
const toolTipClass = classNames(
`${classPrefix}-tooltip`,
@@ -51,7 +53,7 @@ const Tooltip = forwardRef((props: TdTooltipProps, ref) => {
}, [duration, timeUp]);
useImperativeHandle(ref, () => ({
- ...((popupRef.current || {}) as any),
+ ...(popupRef.current || {}),
}));
return (
@@ -70,6 +72,5 @@ const Tooltip = forwardRef((props: TdTooltipProps, ref) => {
});
Tooltip.displayName = 'Tooltip';
-Tooltip.defaultProps = tooltipDefaultProps;
export default Tooltip;
diff --git a/src/tooltip/TooltipLite.tsx b/src/tooltip/TooltipLite.tsx
index e25fe9277e..1f39adde79 100644
--- a/src/tooltip/TooltipLite.tsx
+++ b/src/tooltip/TooltipLite.tsx
@@ -10,6 +10,7 @@ import getPosition from '../_common/js/utils/getPosition';
import { TdTooltipLiteProps } from './type';
import { tooltipLiteDefaultProps } from './defaultProps';
import { getTransitionParams } from '../popup/utils/transition';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TooltipLiteProps extends TdTooltipLiteProps, StyledProps {
children?: ReactNode;
@@ -17,7 +18,8 @@ export interface TooltipLiteProps extends TdTooltipLiteProps, StyledProps {
const DEFAULT_TRANSITION_TIMEOUT = 180;
-const TooltipLite: React.FC = (props) => {
+const TooltipLite: React.FC = (originalProps) => {
+ const props = useDefaultProps(originalProps, tooltipLiteDefaultProps);
const { style, className, placement, showArrow, theme, children, triggerElement, content, showShadow } = props;
const triggerRef = useRef(null);
const contentRef = useRef(null);
@@ -108,6 +110,5 @@ const TooltipLite: React.FC = (props) => {
};
TooltipLite.displayName = 'Tooltiplite';
-TooltipLite.defaultProps = tooltipLiteDefaultProps;
export default React.memo(TooltipLite);
diff --git a/src/transfer/Transfer.tsx b/src/transfer/Transfer.tsx
index 41f2e551ca..edab3f18ae 100644
--- a/src/transfer/Transfer.tsx
+++ b/src/transfer/Transfer.tsx
@@ -13,6 +13,7 @@ import { filterCheckedTreeNodes, getTargetNodes, getDefaultValue, getJSX, getLea
import { TNode, StyledProps } from '../common';
import { useLocaleReceiver } from '../locale/LocalReceiver';
import { transferDefaultProps } from './defaultProps';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface TransferProps extends TdTransferProps, StyledProps {
content?: Array;
@@ -28,7 +29,8 @@ interface CheckedInterface {
target: Array;
}
-const Transfer: React.FunctionComponent = (props) => {
+const Transfer: React.FunctionComponent = (originalProps) => {
+ const props = useDefaultProps(originalProps, transferDefaultProps);
const {
data,
search,
@@ -279,6 +281,5 @@ const Transfer: React.FunctionComponent = (props) => {
};
Transfer.displayName = 'Transfer';
-Transfer.defaultProps = transferDefaultProps;
export default Transfer;
diff --git a/src/tree-select/TreeSelect.tsx b/src/tree-select/TreeSelect.tsx
index 017d8556b1..6c806746bb 100644
--- a/src/tree-select/TreeSelect.tsx
+++ b/src/tree-select/TreeSelect.tsx
@@ -16,6 +16,9 @@ import { useTreeSelectPassThroughProps } from './useTreeSelectPassthoughProps';
import { useTreeSelectLocale } from './useTreeSelectLocale';
import { treeSelectDefaultProps } from './defaultProps';
import parseTNode from '../_util/parseTNode';
+import useDefaultProps from '../hooks/useDefaultProps';
+import { PopupRef } from '../popup';
+import { InputRef } from '../input';
export interface TreeSelectProps
extends TdTreeSelectProps,
@@ -29,7 +32,10 @@ export interface NodeOptions {
const useMergeFn = (...fns: Array<(...args: T) => void>) =>
usePersistFn((...args: T) => fns.forEach((fn) => fn?.(...args)));
-const TreeSelect = forwardRef((props: TreeSelectProps, ref) => {
+type TreeSelectRefType = Partial & PopupRef & InputRef>;
+
+const TreeSelect = forwardRef((originalProps, ref) => {
+ const props = useDefaultProps>(originalProps, treeSelectDefaultProps);
/* ---------------------------------config---------------------------------------- */
// 国际化文本初始化
@@ -73,7 +79,7 @@ const TreeSelect = forwardRef((props: TreeSelectProps, ref) => {
const [filterInput, setFilterInput] = useControlled(props, 'inputValue', onInputChange);
const treeRef = useRef>();
- const selectInputRef = useRef();
+ const selectInputRef = useRef>();
const tKeys = useMemo(
() => ({
@@ -351,6 +357,5 @@ const TreeSelect = forwardRef((props: TreeSelectProps, ref) => {
});
TreeSelect.displayName = 'TreeSelect';
-TreeSelect.defaultProps = treeSelectDefaultProps;
export default TreeSelect;
diff --git a/src/tree/Tree.tsx b/src/tree/Tree.tsx
index 7041193ff1..69fc0d9073 100644
--- a/src/tree/Tree.tsx
+++ b/src/tree/Tree.tsx
@@ -28,11 +28,21 @@ import useTreeVirtualScroll from './hooks/useTreeVirtualScroll';
import type { TreeNodeState, TreeNodeValue, TypeTreeNodeData, TypeTreeNodeModel } from '../_common/js/tree-v1/types';
import type { TreeInstanceFunctions, TdTreeProps } from './type';
+import useDefaultProps from '../hooks/useDefaultProps';
export type TreeProps = TdTreeProps & StyledProps;
-const Tree = forwardRef((props: TreeProps, ref: React.Ref) => {
+const Tree = forwardRef, TreeProps>((originalProps, ref) => {
const { treeClassNames, transitionNames, transitionClassNames, transitionDuration, locale } = useTreeConfig();
+ const props = useDefaultProps(originalProps, {
+ data: [],
+ expandLevel: 0,
+ icon: true,
+ line: false,
+ transition: true,
+ lazy: true,
+ valueMode: 'onlyLeaf',
+ });
// 可见节点集合
const [visibleNodes, setVisibleNodes] = useState([]);
@@ -365,14 +375,4 @@ const Tree = forwardRef((props: TreeProps, ref: React.Ref
Tree.displayName = 'Tree';
-Tree.defaultProps = {
- data: [],
- expandLevel: 0,
- icon: true,
- line: false,
- transition: true,
- lazy: true,
- valueMode: 'onlyLeaf',
-};
-
export default Tree;
diff --git a/src/tree/TreeItem.tsx b/src/tree/TreeItem.tsx
index 476a5fc3f9..ded073df88 100644
--- a/src/tree/TreeItem.tsx
+++ b/src/tree/TreeItem.tsx
@@ -8,6 +8,7 @@ import React, {
DragEvent,
isValidElement,
useEffect,
+ useState,
} from 'react';
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
@@ -25,6 +26,7 @@ import useConfig from '../hooks/useConfig';
import type { TdTreeProps } from './type';
import type { TreeItemProps } from './interface';
+import type { TypeTreeNodeData } from '../_common/js/tree-v1/types';
/**
* 树节点组件
@@ -210,11 +212,23 @@ const TreeItem = forwardRef(
const [labelDom, setRefCurrent] = useDomRefCallback();
useRipple(labelDom);
+ // setData需要强制刷新组件来更新数据
+ const [, updateRender] = useState({});
+
const renderLabel = () => {
const emptyView = locale('empty');
let labelText: string | ReactNode = '';
if (label instanceof Function) {
- labelText = label(node.getModel()) || emptyView;
+ const { setData: nodeSetData, ...rest } = node.getModel();
+ labelText =
+ label({
+ ...rest,
+ // 拦截setData render tree-item
+ setData: (value: TypeTreeNodeData) => {
+ nodeSetData(value);
+ updateRender({});
+ },
+ }) || emptyView;
} else {
labelText = node.label || emptyView;
}
diff --git a/src/tree/__tests__/tree.test.tsx b/src/tree/__tests__/tree.test.tsx
index 2dea5c0542..fc749f67c6 100644
--- a/src/tree/__tests__/tree.test.tsx
+++ b/src/tree/__tests__/tree.test.tsx
@@ -320,4 +320,71 @@ describe('Tree test', () => {
await mockDelay(300);
expect(container.querySelectorAll('.t-loading').length).toBe(1);
});
+
+ test('custom label', async () => {
+ const data = [
+ {
+ label: '第1一段',
+ value: 1,
+ children: [
+ {
+ label: '第二段',
+ value: '1-1',
+ },
+ ],
+ },
+ {
+ label: '第二段',
+ value: 2,
+ },
+ ];
+ const { container } = await renderTreeWithProps({
+ data,
+ label: ({ data, setData }) =>
+ data.isEditing ? (
+ {
+ console.log('blur', { ...data, isEditing: false });
+ setData({ ...data, label: e.target.value, isEditing: false });
+ }}
+ onKeyDown={(e) => {
+ console.log('keydown', e.key);
+ if (e.key === 'Enter') {
+ setData({ ...data, label: e.currentTarget.value, isEditing: false });
+ console.log('Enter setData({ ...data, name: e.target.value, isEditing: false })');
+ }
+ if (e.key === 'Escape') {
+ setData({ ...data, isEditing: false });
+ console.log('ESC setData({ ...data, isEditing: false })');
+ }
+ }}
+ />
+ ) : (
+ {
+ e.stopPropagation();
+ setData({ ...data, isEditing: true });
+ }}
+ >
+ {data.label}
+
+ ),
+ });
+ await mockDelay(300);
+ expect(container.querySelector('.tree-item-span')).not.toBeNull();
+ fireEvent.dblClick(container.querySelector('.tree-item-span'));
+ await mockDelay(300);
+ expect(container.querySelector('.tree-item-input')).not.toBeNull();
+ fireEvent.change(container.querySelector('.tree-item-input'), { target: { value: '123' } });
+ expect(container.querySelector('.tree-item-input').value).toBe('123');
+ container.querySelector('.tree-item-input').focus();
+ container.querySelector('.tree-item-input').blur();
+ await mockDelay(300);
+ expect(container.querySelector('.tree-item-input')).toBeNull();
+ expect(container.querySelector('.tree-item-span')).not.toBeNull();
+ expect(container.querySelector('.tree-item-span').textContent).toBe('123');
+ });
});
diff --git a/src/upload/upload.tsx b/src/upload/upload.tsx
index 1439fc4eeb..8f07c80285 100644
--- a/src/upload/upload.tsx
+++ b/src/upload/upload.tsx
@@ -13,9 +13,11 @@ import { UploadDragEvents } from './hooks/useDrag';
import CustomFile from './themes/CustomFile';
import { UploadFile } from './type';
import parseTNode from '../_util/parseTNode';
+import useDefaultProps from '../hooks/useDefaultProps';
// const Upload = forwardRef((props: UploadProps, ref) => {
-function TdUpload(props: UploadProps, ref: ForwardedRef) {
+function TdUpload(originalProps: UploadProps, ref: ForwardedRef) {
+ const props = useDefaultProps>(originalProps, uploadDefaultProps);
const { theme } = props;
const {
locale,
@@ -216,6 +218,5 @@ export type UploadOuterForwardRef = {
const Upload = forwardRef(TdUpload) as UploadOuterForwardRef;
Upload.displayName = 'Upload';
-Upload.defaultProps = uploadDefaultProps;
export default Upload;
diff --git a/src/watermark/Watermark.tsx b/src/watermark/Watermark.tsx
index 54e50cfdd1..be36f04e4f 100644
--- a/src/watermark/Watermark.tsx
+++ b/src/watermark/Watermark.tsx
@@ -8,31 +8,35 @@ import injectStyle from '../_common/js/utils/injectStyle';
import useConfig from '../hooks/useConfig';
import useMutationObserver from '../_util/useMutationObserver';
import { TdWatermarkProps } from './type';
-import { watermarkDefaultProps as defaultProps } from './defaultProps';
+import { watermarkDefaultProps } from './defaultProps';
import { getStyleStr } from './utils';
+import useDefaultProps from '../hooks/useDefaultProps';
export interface WatermarkProps extends TdWatermarkProps, StyledProps {}
-const Watermark: React.FC = ({
- alpha = defaultProps.alpha,
- x = 200,
- y = 210,
- width = 120,
- height = 60,
- rotate: tempRotate = defaultProps.rotate,
- zIndex = 10,
- lineSpace = defaultProps.lineSpace,
- isRepeat = defaultProps.isRepeat,
- removable = defaultProps.removable,
- movable = defaultProps.movable,
- moveInterval = defaultProps.moveInterval,
- offset = [],
- content,
- children,
- watermarkContent,
- className,
- style = {},
-}) => {
+const Watermark: React.FC = (originalProps) => {
+ const props = useDefaultProps(originalProps, watermarkDefaultProps);
+ const {
+ alpha,
+ x = 200,
+ y = 210,
+ width = 120,
+ height = 60,
+ rotate: tempRotate,
+ zIndex = 10,
+ lineSpace,
+ isRepeat,
+ removable,
+ movable,
+ moveInterval,
+ offset = [],
+ content,
+ children,
+ watermarkContent,
+ className,
+ style = {},
+ } = props;
+
const { classPrefix } = useConfig();
let gapX = x;
diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap
index c17242e172..6d21b14474 100644
--- a/test/snap/__snapshots__/csr.test.jsx.snap
+++ b/test/snap/__snapshots__/csr.test.jsx.snap
@@ -6109,7 +6109,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/base.jsx 1`] = `
- 图片加载中
+ loading
@@ -6200,7 +6200,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/base.jsx 1`] = `
- 图片加载中
+ loading
@@ -6329,7 +6329,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group.jsx 1`] = `
- 图片加载中
+ loading
@@ -6405,7 +6405,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group.jsx 1`] = `
- 图片加载中
+ loading
@@ -6489,7 +6489,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group.jsx 1`] = `
- 图片加载中
+ loading
@@ -6565,7 +6565,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group.jsx 1`] = `
- 图片加载中
+ loading
@@ -6706,7 +6706,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-cascading.jsx 1`
- 图片加载中
+ loading
@@ -6782,7 +6782,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-cascading.jsx 1`
- 图片加载中
+ loading
@@ -6866,7 +6866,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-cascading.jsx 1`
- 图片加载中
+ loading
@@ -6942,7 +6942,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-cascading.jsx 1`
- 图片加载中
+ loading
@@ -7083,7 +7083,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-max.jsx 1`] = `
- 图片加载中
+ loading
@@ -7152,7 +7152,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-max.jsx 1`] = `
- 图片加载中
+ loading
@@ -7232,7 +7232,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-max.jsx 1`] = `
- 图片加载中
+ loading
@@ -7309,7 +7309,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-max.jsx 1`] = `
- 图片加载中
+ loading
@@ -7378,7 +7378,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-max.jsx 1`] = `
- 图片加载中
+ loading
@@ -7458,7 +7458,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/group-max.jsx 1`] = `
- 图片加载中
+ loading
@@ -7857,7 +7857,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -7905,7 +7905,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -7953,7 +7953,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -8002,7 +8002,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -8198,7 +8198,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -8246,7 +8246,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -8294,7 +8294,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -8343,7 +8343,7 @@ exports[`csr snapshot test > csr test src/avatar/_example/size.jsx 1`] = `
- 图片加载中
+ loading
@@ -41311,7 +41311,7 @@ exports[`csr snapshot test > csr test src/card/_example/footer-actions.jsx 1`] =
- 图片加载中
+ loading
@@ -41668,7 +41668,7 @@ exports[`csr snapshot test > csr test src/card/_example/footer-actions.jsx 1`] =
- 图片加载中
+ loading
@@ -42797,7 +42797,7 @@ exports[`csr snapshot test > csr test src/card/_example/header-footer-actions.js
- 图片加载中
+ loading
@@ -43002,7 +43002,7 @@ exports[`csr snapshot test > csr test src/card/_example/header-footer-actions.js
- 图片加载中
+ loading
@@ -69283,7 +69283,7 @@ exports[`csr snapshot test > csr test src/config-provider/_example/others.jsx 1`
- 图片加载中
+ loading
@@ -69905,7 +69905,7 @@ exports[`csr snapshot test > csr test src/config-provider/_example/others.jsx 1`
- 图片加载中
+ loading
@@ -85042,13 +85042,6 @@ exports[`csr snapshot test > csr test src/descriptions/_example/base.jsx 1`] = `
class="t-space t-space-vertical"
style="gap: 16px;"
>
-
-
- 推荐:数据写法
-
-
@@ -85120,84 +85113,6 @@ exports[`csr snapshot test > csr test src/descriptions/_example/base.jsx 1`] = `
-
-
- JSX写法
-
-
-
-
-
-
-
-
-
- Name
- |
-
- TDesign
- |
-
- Telephone Number
- |
-
- 139****0609
- |
-
-
-
- Area
- |
-
- China Tencent Headquarters
- |
-
- Address
- |
-
- test
- |
-
-
-
-
-
+
+
+
+
-
-
-
-
-
- Name
- |
-
- TDesign
- |
-
-
-
- Telephone Number
- |
-
- 139****0609
- |
-
-
-
- Area
- |
-
- China Tencent Headquarters
- |
-
-
-
- Address
- |
-
- Shenzhen Penguin Island D1 4A Mail Center
- |
-
-
-
+ Shipping address
+
+
+
+
+ Name
+ |
+
+ TDesign
+ |
+
+ Telephone Number
+ |
+
+ 139****0609
+ |
+
+
+
+ Area
+ |
+
+ China Tencent Headquarters
+ |
+
+ Address
+ |
+
+
+
+
+
+
+ City
+ :
+ |
+
+ Shenzhen
+ |
+
+ Detail
+ :
+ |
+
+ Penguin Island D1 4A Mail Center
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ light-outline
+
+
+
+
+
+
+
+