Merge branch 'master' into next-merge-master

This commit is contained in:
MadCcc 2022-09-22 11:47:05 +08:00
commit eb9179464b
37 changed files with 344 additions and 266 deletions

View File

@ -15,6 +15,17 @@ timeline: true
--- ---
## 4.23.2
`2022-09-17`
- 🐞 Fix Card console warning when using `tabList` prop. [#37537](https://github.com/ant-design/ant-design/pull/37537) [@edc-hui](https://github.com/edc-hui)
- 🐞 Fix Table `filterSearch` is not executed when `filterMode="tree"`. [#37587](https://github.com/ant-design/ant-design/pull/37587) [@edc-hui](https://github.com/edc-hui)
- 🐞 Fix Tooltip should not replace children `className` when it's not a string type. [#37598](https://github.com/ant-design/ant-design/pull/37598)
- 🐞 Fix Tree that TreeNodes not aligned when draggable and some node is disabled. [#37534](https://github.com/ant-design/ant-design/pull/37534)
- TypeScript
- 🤖 Replace deprecated `React.ReactChild` type. [#37551](https://github.com/ant-design/ant-design/pull/37551) [@bowen-wu](https://github.com/bowen-wu)
## 4.23.1 ## 4.23.1
`2022-09-09` `2022-09-09`
@ -202,7 +213,7 @@ timeline: true
- 💄 Fix nested Table margin style. [#36209](https://github.com/ant-design/ant-design/pull/36209) - 💄 Fix nested Table margin style. [#36209](https://github.com/ant-design/ant-design/pull/36209)
- 🐞 Fix Table filter dropdown with multiple subMenu may not closed. [#36132](https://github.com/ant-design/ant-design/pull/36132) - 🐞 Fix Table filter dropdown with multiple subMenu may not closed. [#36132](https://github.com/ant-design/ant-design/pull/36132)
- 🐞 Table reset the last selection key when deselect or bulk operations. [#34705](https://github.com/ant-design/ant-design/pull/34705) [@Dunqing](https://github.com/Dunqing) - 🐞 Table reset the last selection key when deselect or bulk operations. [#34705](https://github.com/ant-design/ant-design/pull/34705) [@Dunqing](https://github.com/Dunqing)
- 🐞 Fix Popover arrow color with custom `color` prop. [#36241](https://github.com/ant-design/ant-design/pull/36241) [@MadCcc](https://github.com/MadCcc) - 🐞 Fix Popover arrow color with custom `color` prop. [#36241](https://github.com/ant-design/ant-design/pull/36241)
- 🐞 Fix Upload `listType="picture-card"` select button not being hidden when children is empty. [#36196](https://github.com/ant-design/ant-design/pull/36196) - 🐞 Fix Upload `listType="picture-card"` select button not being hidden when children is empty. [#36196](https://github.com/ant-design/ant-design/pull/36196)
- 🐞 Fix Menu deprecated warning when `item={undefined}`. [#36190](https://github.com/ant-design/ant-design/pull/36190) - 🐞 Fix Menu deprecated warning when `item={undefined}`. [#36190](https://github.com/ant-design/ant-design/pull/36190)
- 💄 Fix Button `loading` icon margin style lost. [#36168](https://github.com/ant-design/ant-design/pull/36168) - 💄 Fix Button `loading` icon margin style lost. [#36168](https://github.com/ant-design/ant-design/pull/36168)
@ -425,7 +436,7 @@ timeline: true
- Less - Less
- 💄 Replace less html selector with related variable. [#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod) - 💄 Replace less html selector with related variable. [#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod)
- 💄 Modify less `danger` value from the function to variable. [#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi) - 💄 Modify less `danger` value from the function to variable. [#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi)
- 🐞 Arrow border radius variable use fixed value. [#35086](https://github.com/ant-design/ant-design/pull/35086) [@MadCcc](https://github.com/MadCcc) - 🐞 Arrow border radius variable use fixed value. [#35086](https://github.com/ant-design/ant-design/pull/35086)
- TypeScript - TypeScript
- 🤖 Fixed `Upload` component `UploadChangeParam<T>` internal `fileList` not using generics. [#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer) - 🤖 Fixed `Upload` component `UploadChangeParam<T>` internal `fileList` not using generics. [#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer)
- 🤖 Update TypeScript definition for `@types/react@18` compatible. [#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian) - 🤖 Update TypeScript definition for `@types/react@18` compatible. [#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian)
@ -580,7 +591,7 @@ timeline: true
- 💄 Improve Menu `:focus-visible` style. [#34008](https://github.com/ant-design/ant-design/pull/34008) - 💄 Improve Menu `:focus-visible` style. [#34008](https://github.com/ant-design/ant-design/pull/34008)
- 💄 Fix Pagination and Rate style problem in Safari. [#34002](https://github.com/ant-design/ant-design/pull/34002) - 💄 Fix Pagination and Rate style problem in Safari. [#34002](https://github.com/ant-design/ant-design/pull/34002)
- 💄 Fix Row and Col component styles when using prefixCls. [#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web) - 💄 Fix Row and Col component styles when using prefixCls. [#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web)
- 🐞 Fix Timeline icons with custom color not working. [#33951](https://github.com/ant-design/ant-design/pull/33951) [@MadCcc](https://github.com/MadCcc) - 🐞 Fix Timeline icons with custom color not working. [#33951](https://github.com/ant-design/ant-design/pull/33951)
- TypeScript - TypeScript
- 🤖 Optimize Cascader `onChange` definition with `multiple` prop. [#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay) - 🤖 Optimize Cascader `onChange` definition with `multiple` prop. [#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay)
@ -663,7 +674,7 @@ timeline: true
`2021-12-29` `2021-12-29`
- 🐞 Fix Popconfirm throws `Can't perform a React state update on an unmounted component.` warning in some async case. [#33432](https://github.com/ant-design/ant-design/pull/33432) [@MadCcc](https://github.com/MadCcc) - 🐞 Fix Popconfirm throws `Can't perform a React state update on an unmounted component.` warning in some async case. [#33432](https://github.com/ant-design/ant-design/pull/33432)
- 🐞 Fix Input with `suffix` will crash when `value` is number type. [#33462](https://github.com/ant-design/ant-design/pull/33462) - 🐞 Fix Input with `suffix` will crash when `value` is number type. [#33462](https://github.com/ant-design/ant-design/pull/33462)
- 🐞 Fix Divider with text dashed border color error. [#33452](https://github.com/ant-design/ant-design/pull/33452) [@chen-jingjie](https://github.com/chen-jingjie) - 🐞 Fix Divider with text dashed border color error. [#33452](https://github.com/ant-design/ant-design/pull/33452) [@chen-jingjie](https://github.com/chen-jingjie)
- 🐞 Fix Dropdown.Button not support `destroyPopupOnHide`. [#33442](https://github.com/ant-design/ant-design/pull/33442) [@LongHaoo](https://github.com/LongHaoo) - 🐞 Fix Dropdown.Button not support `destroyPopupOnHide`. [#33442](https://github.com/ant-design/ant-design/pull/33442) [@LongHaoo](https://github.com/LongHaoo)

View File

@ -15,6 +15,17 @@ timeline: true
--- ---
## 4.23.2
`2022-09-17`
- 🐞 修复 Card 传入 `tabList` 属性时控制台出现废弃警告的问题。[#37537](https://github.com/ant-design/ant-design/pull/37537) [@edc-hui](https://github.com/edc-hui)
- 🐞 修复 Table `filterMode="tree"``filterSearch` 函数未执行的问题。[#37587](https://github.com/ant-design/ant-design/pull/37587) [@edc-hui](https://github.com/edc-hui)
- 🐞 修复 Tooltip 的子元素 `className` 非 string 类型时会被覆盖的问题。[#37598](https://github.com/ant-design/ant-design/pull/37598)
- 🐞 修复 Tree 组件 TreeNode 在可拖拽并且禁用状态下不对齐的问题。[#37534](https://github.com/ant-design/ant-design/pull/37534)
- TypeScript
- 🤖 替换已废弃的 `React.ReactChild` 定义。[#37551](https://github.com/ant-design/ant-design/pull/37551) [@bowen-wu](https://github.com/bowen-wu)
## 4.23.1 ## 4.23.1
`2022-09-09` `2022-09-09`
@ -203,7 +214,7 @@ timeline: true
- 🐞 Table 取消选择或批量操作时重置上一次选择的 key。[#34705](https://github.com/ant-design/ant-design/pull/34705) [@Dunqing](https://github.com/Dunqing) - 🐞 Table 取消选择或批量操作时重置上一次选择的 key。[#34705](https://github.com/ant-design/ant-design/pull/34705) [@Dunqing](https://github.com/Dunqing)
- 🐞 修复 Table 过滤列表在某些场景下多级展开无法关闭的问题。[#36132](https://github.com/ant-design/ant-design/pull/36132) - 🐞 修复 Table 过滤列表在某些场景下多级展开无法关闭的问题。[#36132](https://github.com/ant-design/ant-design/pull/36132)
- 🐞 修复 Upload `listType="picture-card"` 当 children 为空时上传文件按钮没有隐藏的问题。[#36196](https://github.com/ant-design/ant-design/pull/36196) - 🐞 修复 Upload `listType="picture-card"` 当 children 为空时上传文件按钮没有隐藏的问题。[#36196](https://github.com/ant-design/ant-design/pull/36196)
- 🐞 修复 Popover 自定义 `color` 时箭头颜色问题。[#36241](https://github.com/ant-design/ant-design/pull/36241) [@MadCcc](https://github.com/MadCcc) - 🐞 修复 Popover 自定义 `color` 时箭头颜色问题。[#36241](https://github.com/ant-design/ant-design/pull/36241)
- 🐞 修复 Menu `item={undefined}` 时会有废弃警告的问题。[#36190](https://github.com/ant-design/ant-design/pull/36190) - 🐞 修复 Menu `item={undefined}` 时会有废弃警告的问题。[#36190](https://github.com/ant-design/ant-design/pull/36190)
- 💄 修复 Button `loading` 图标的间距丢失的问题。[#36168](https://github.com/ant-design/ant-design/pull/36168) - 💄 修复 Button `loading` 图标的间距丢失的问题。[#36168](https://github.com/ant-design/ant-design/pull/36168)
- 🐞 修复 Dropdown 中 Menu 分组下的 Item 点击不会关闭的问题。[#36148](https://github.com/ant-design/ant-design/pull/36148) - 🐞 修复 Dropdown 中 Menu 分组下的 Item 点击不会关闭的问题。[#36148](https://github.com/ant-design/ant-design/pull/36148)
@ -421,12 +432,12 @@ timeline: true
- 🐞 修复 Title、Text、Paragraph 组件不支持 `ref` 的问题。[#34847](https://github.com/ant-design/ant-design/pull/34847) [@MQuy](https://github.com/MQuy) - 🐞 修复 Title、Text、Paragraph 组件不支持 `ref` 的问题。[#34847](https://github.com/ant-design/ant-design/pull/34847) [@MQuy](https://github.com/MQuy)
- Input - Input
- 💄 Input.Group 对子组件屏蔽 Form.Item 的样式。[#34764](https://github.com/ant-design/ant-design/pull/34764) - 💄 Input.Group 对子组件屏蔽 Form.Item 的样式。[#34764](https://github.com/ant-design/ant-design/pull/34764)
- 💄 调整 Form 下 TextArea 的样式。[#34714](https://github.com/ant-design/ant-design/pull/34714) [@MadCcc](https://github.com/MadCcc) - 💄 调整 Form 下 TextArea 的样式。[#34714](https://github.com/ant-design/ant-design/pull/34714)
- ⌨️ 修复 Checkbox 缺少 `aria-checked` 属性导致屏幕阅读器识别错误的问题。[#34862](https://github.com/ant-design/ant-design/pull/34862) [@SpaNb4](https://github.com/SpaNb4) - ⌨️ 修复 Checkbox 缺少 `aria-checked` 属性导致屏幕阅读器识别错误的问题。[#34862](https://github.com/ant-design/ant-design/pull/34862) [@SpaNb4](https://github.com/SpaNb4)
- Less - Less
- 💄 替换 less 中的 html 选择器为对应变量。[#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod) - 💄 替换 less 中的 html 选择器为对应变量。[#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod)
- 💄 修改 less 中 `danger` 值从函数改为变量。[#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi) - 💄 修改 less 中 `danger` 值从函数改为变量。[#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi)
- 🐞 箭头圆角使用固定值 2px 变量。[#35086](https://github.com/ant-design/ant-design/pull/35086) [@MadCcc](https://github.com/MadCcc) - 🐞 箭头圆角使用固定值 2px 变量。[#35086](https://github.com/ant-design/ant-design/pull/35086)
- TypeScript - TypeScript
- 🤖 修正 Upload 组件中 `UploadChangeParam<T>` 内部 `fileList` 不使用泛型问题。[#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer) - 🤖 修正 Upload 组件中 `UploadChangeParam<T>` 内部 `fileList` 不使用泛型问题。[#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer)
- 🤖 更新 TypeScript 定义以兼容 `@types/react@18`。[#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian) - 🤖 更新 TypeScript 定义以兼容 `@types/react@18`。[#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian)
@ -582,7 +593,7 @@ timeline: true
- 💄 优化 Menu `:focus-visible` 的样式。[#34008](https://github.com/ant-design/ant-design/pull/34008) - 💄 优化 Menu `:focus-visible` 的样式。[#34008](https://github.com/ant-design/ant-design/pull/34008)
- 💄 修复 Pagination 和 Rate 在 Safari 下部分样式丢失的问题,比如分页按钮禁用样式失效。[#34002](https://github.com/ant-design/ant-design/pull/34002) - 💄 修复 Pagination 和 Rate 在 Safari 下部分样式丢失的问题,比如分页按钮禁用样式失效。[#34002](https://github.com/ant-design/ant-design/pull/34002)
- 💄 修复 Row 与 Col 在配置 `prefixCls` 的样式问题。[#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web) - 💄 修复 Row 与 Col 在配置 `prefixCls` 的样式问题。[#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web)
- 🐞 修复 Timeline 的自定义图标颜色无效的问题。[#33951](https://github.com/ant-design/ant-design/pull/33951) [@MadCcc](https://github.com/MadCcc) - 🐞 修复 Timeline 的自定义图标颜色无效的问题。[#33951](https://github.com/ant-design/ant-design/pull/33951)
- TypeScript - TypeScript
- 🤖 优化 Cascader `multiple` 属性对应的 `onChange` 类型推断。[#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay) - 🤖 优化 Cascader `multiple` 属性对应的 `onChange` 类型推断。[#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay)
@ -665,7 +676,7 @@ timeline: true
`2021-12-29` `2021-12-29`
- 🐞 修复 Popconfirm 在某些情况下会出现 `Can't perform a React state update on an unmounted component.` 的错误。[#33432](https://github.com/ant-design/ant-design/pull/33432) [@MadCcc](https://github.com/MadCcc) - 🐞 修复 Popconfirm 在某些情况下会出现 `Can't perform a React state update on an unmounted component.` 的错误。[#33432](https://github.com/ant-design/ant-design/pull/33432)
- 🐞 修复 Input 配置 `suffix``value` 为数字类型会崩溃的问题。[#33462](https://github.com/ant-design/ant-design/pull/33462) - 🐞 修复 Input 配置 `suffix``value` 为数字类型会崩溃的问题。[#33462](https://github.com/ant-design/ant-design/pull/33462)
- 🐞 修复 Divider with text dashed 的边框颜色错误问题。[#33452](https://github.com/ant-design/ant-design/pull/33452) [@chen-jingjie](https://github.com/chen-jingjie) - 🐞 修复 Divider with text dashed 的边框颜色错误问题。[#33452](https://github.com/ant-design/ant-design/pull/33452) [@chen-jingjie](https://github.com/chen-jingjie)
- 🐞 修复 Dropdown.Button 不支持 `destroyPopupOnHide` 的问题。[#33442](https://github.com/ant-design/ant-design/pull/33442) [@LongHaoo](https://github.com/LongHaoo) - 🐞 修复 Dropdown.Button 不支持 `destroyPopupOnHide` 的问题。[#33442](https://github.com/ant-design/ant-design/pull/33442) [@LongHaoo](https://github.com/LongHaoo)

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import { render, sleep, fireEvent } from '../../../tests/utils'; import { render, sleep, fireEvent, act } from '../../../tests/utils';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import Wave from '../wave'; import Wave from '../wave';
@ -270,4 +270,31 @@ describe('Wave component', () => {
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: red;'); expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: red;');
unmount(); unmount();
}); });
it('Wave style should append to validate element', () => {
jest.useFakeTimers();
const { container } = render(
<Wave>
<div className="bamboo" style={{ borderColor: 'red' }} />
</Wave>,
);
// Mock shadow container
const fakeDoc = document.createElement('div');
fakeDoc.append('text');
fakeDoc.appendChild(document.createElement('span'));
expect(fakeDoc.childNodes).toHaveLength(2);
(container.querySelector('.bamboo') as any).getRootNode = () => fakeDoc;
// Click should not throw
fireEvent.click(container.querySelector('.bamboo')!);
act(() => {
jest.runAllTimers();
});
expect(fakeDoc.querySelector('style')).toBeTruthy();
jest.useRealTimers();
});
}); });

View File

@ -18,6 +18,16 @@ function isHidden(element: HTMLElement) {
return !element || element.offsetParent === null || element.hidden; return !element || element.offsetParent === null || element.hidden;
} }
function getValidateContainer(nodeRoot: Node): Element {
if (nodeRoot instanceof Document) {
return nodeRoot.body;
}
return Array.from(nodeRoot.childNodes).find(
ele => ele?.nodeType === Node.ELEMENT_NODE,
) as Element;
}
function isNotGrey(color: string) { function isNotGrey(color: string) {
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/); const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/);
@ -119,8 +129,7 @@ class InternalWave extends React.Component<WaveProps> {
extraNode.style.borderColor = waveColor; extraNode.style.borderColor = waveColor;
const nodeRoot = node.getRootNode?.() || node.ownerDocument; const nodeRoot = node.getRootNode?.() || node.ownerDocument;
const nodeBody: Element = const nodeBody = getValidateContainer(nodeRoot) ?? nodeRoot;
nodeRoot instanceof Document ? nodeRoot.body : (nodeRoot.firstChild as Element) ?? nodeRoot;
styleForPseudo = updateCSS( styleForPseudo = updateCSS(
` `

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Alert action of Alert custom action 1`] = ` exports[`Alert custom action 1`] = `
<div <div
class="ant-alert ant-alert-success" class="ant-alert ant-alert-success"
data-show="true" data-show="true"
@ -74,29 +74,6 @@ exports[`Alert action of Alert custom action 1`] = `
</div> </div>
`; `;
exports[`Alert could accept none react element icon 1`] = `
<div
class="ant-alert ant-alert-success"
data-show="true"
role="alert"
>
<span
class="ant-alert-icon"
>
icon
</span>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
>
Success Tips
</div>
</div>
</div>
`;
exports[`Alert rtl render component should be rendered correctly in RTL direction 1`] = ` exports[`Alert rtl render component should be rendered correctly in RTL direction 1`] = `
<div <div
class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-rtl" class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-rtl"
@ -108,30 +85,3 @@ exports[`Alert rtl render component should be rendered correctly in RTL directio
/> />
</div> </div>
`; `;
exports[`Alert support closeIcon 1`] = `
<div
class="ant-alert ant-alert-warning ant-alert-no-icon"
data-show="true"
role="alert"
>
<div
class="ant-alert-content"
>
<div
class="ant-alert-message"
>
Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text
</div>
</div>
<button
class="ant-alert-close-icon"
tabindex="0"
type="button"
>
<span>
close
</span>
</button>
</div>
`;

View File

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import userEvent from '@testing-library/user-event';
import Alert from '..'; import Alert from '..';
import accessibilityTest from '../../../tests/shared/accessibilityTest'; import accessibilityTest from '../../../tests/shared/accessibilityTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, sleep, act } from '../../../tests/utils'; import { render, act, screen } from '../../../tests/utils';
import Button from '../../button'; import Button from '../../button';
import Popconfirm from '../../popconfirm'; import Popconfirm from '../../popconfirm';
import Tooltip from '../../tooltip'; import Tooltip from '../../tooltip';
@ -21,9 +22,9 @@ describe('Alert', () => {
jest.useRealTimers(); jest.useRealTimers();
}); });
it('could be closed', () => { it('should show close button and could be closed', async () => {
const onClose = jest.fn(); const onClose = jest.fn();
const { container } = render( render(
<Alert <Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text" message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning" type="warning"
@ -32,125 +33,102 @@ describe('Alert', () => {
/>, />,
); );
jest.useFakeTimers(); await userEvent.click(screen.getByRole('button', { name: /close/i }));
fireEvent.click(container.querySelector('.ant-alert-close-icon')!);
act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });
expect(onClose).toHaveBeenCalled();
jest.useRealTimers(); expect(onClose).toHaveBeenCalledTimes(1);
}); });
describe('action of Alert', () => { it('custom action', () => {
it('custom action', () => {
const { container } = render(
<Alert
message="Success Tips"
type="success"
showIcon
action={
<Button size="small" type="text">
UNDO
</Button>
}
closable
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
});
it('support closeIcon', () => {
const { container } = render( const { container } = render(
<Alert <Alert
message="Success Tips"
type="success"
showIcon
action={
<Button size="small" type="text">
UNDO
</Button>
}
closable closable
closeIcon={<span>close</span>}
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
/>, />,
); );
expect(container.firstChild).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
describe('data and aria props', () => { it('should sets data attributes on alert when pass attributes to props', () => {
it('sets data attributes on input', () => { render(
const { container } = render(<Alert data-test="test-id" data-id="12345" message={null} />); <Alert data-test="test-id" data-id="12345" aria-describedby="some-label" message={null} />,
const input = container.querySelector('.ant-alert')!; );
expect(input.getAttribute('data-test')).toBe('test-id'); const alert = screen.getByRole('alert');
expect(input.getAttribute('data-id')).toBe('12345'); expect(alert).toHaveAttribute('data-test', 'test-id');
}); expect(alert).toHaveAttribute('data-id', '12345');
expect(alert).toHaveAttribute('aria-describedby', 'some-label');
it('sets aria attributes on input', () => {
const { container } = render(<Alert aria-describedby="some-label" message={null} />);
const input = container.querySelector('.ant-alert')!;
expect(input.getAttribute('aria-describedby')).toBe('some-label');
});
it('sets role attribute on input', () => {
const { container } = render(<Alert role="status" message={null} />);
const input = container.querySelector('.ant-alert')!;
expect(input.getAttribute('role')).toBe('status');
});
}); });
it('ErrorBoundary', () => { it('sets role attribute on input', () => {
render(<Alert role="status" message={null} />);
expect(screen.getByRole('status')).toBeInTheDocument();
});
it('should show error as ErrorBoundary when children have error', () => {
jest.spyOn(console, 'error').mockImplementation(() => undefined); jest.spyOn(console, 'error').mockImplementation(() => undefined);
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledTimes(0); expect(console.error).toHaveBeenCalledTimes(0);
// @ts-expect-error // @ts-expect-error
// eslint-disable-next-line react/jsx-no-undef // eslint-disable-next-line react/jsx-no-undef
const ThrowError = () => <NotExisted />; const ThrowError = () => <NotExisted />;
const { container } = render( render(
<ErrorBoundary> <ErrorBoundary>
<ThrowError /> <ThrowError />
</ErrorBoundary>, </ErrorBoundary>,
); );
// eslint-disable-next-line jest/no-standalone-expect
expect(container.textContent).toContain('ReferenceError: NotExisted is not defined'); expect(screen.getByRole('alert')).toHaveTextContent(
'ReferenceError: NotExisted is not defined',
);
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
(console.error as any).mockRestore(); (console.error as any).mockRestore();
}); });
it('could be used with Tooltip', async () => { it('could be used with Tooltip', async () => {
const ref = React.createRef<any>(); render(
jest.useRealTimers(); <Tooltip title="xxx" mouseEnterDelay={0}>
const { container } = render(
<Tooltip title="xxx" mouseEnterDelay={0} ref={ref}>
<Alert <Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text" message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning" type="warning"
/> />
</Tooltip>, </Tooltip>,
); );
// wrapper.find('.ant-alert').simulate('mouseenter');
fireEvent.mouseEnter(container.querySelector('.ant-alert')!); await userEvent.hover(screen.getByRole('alert'));
await sleep(0);
expect(ref.current.getPopupDomNode()).toBeTruthy(); expect(screen.getByRole('tooltip')).toBeInTheDocument();
jest.useFakeTimers();
}); });
it('could be used with Popconfirm', async () => { it('could be used with Popconfirm', async () => {
const ref = React.createRef<any>(); render(
jest.useRealTimers(); <Popconfirm title="xxx">
const { container } = render(
<Popconfirm ref={ref} title="xxx">
<Alert <Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text" message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning" type="warning"
/> />
</Popconfirm>, </Popconfirm>,
); );
fireEvent.click(container.querySelector('.ant-alert')!); await userEvent.click(screen.getByRole('alert'));
await sleep(0);
expect(ref.current.getPopupDomNode()).toBeTruthy(); expect(screen.getByRole('tooltip')).toBeInTheDocument();
jest.useFakeTimers();
}); });
it('could accept none react element icon', () => { it('could accept none react element icon', () => {
const { container } = render( render(<Alert message="Success Tips" type="success" showIcon icon="icon" />);
<Alert message="Success Tips" type="success" showIcon icon="icon" />,
); expect(screen.getByRole('alert')).toHaveTextContent(/success tips/i);
expect(container.firstChild).toMatchSnapshot(); expect(screen.getByRole('alert')).toHaveTextContent(/icon/i);
}); });
it('should not render message div when no message', () => { it('should not render message div when no message', () => {

View File

@ -150,6 +150,8 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
} }
componentDidUpdate() { componentDidUpdate() {
const { getCurrentAnchor } = this.props;
const { activeLink } = this.state;
if (this.scrollEvent) { if (this.scrollEvent) {
const currentContainer = this.getContainer(); const currentContainer = this.getContainer();
if (this.scrollContainer !== currentContainer) { if (this.scrollContainer !== currentContainer) {
@ -159,6 +161,9 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
this.handleScroll(); this.handleScroll();
} }
} }
if (typeof getCurrentAnchor === 'function') {
this.setCurrentActiveLink(getCurrentAnchor(activeLink || ''), false);
}
this.updateInk(); this.updateInk();
} }
@ -227,7 +232,7 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
this.inkNode = node; this.inkNode = node;
}; };
setCurrentActiveLink = (link: string) => { setCurrentActiveLink = (link: string, triggerChange = true) => {
const { activeLink } = this.state; const { activeLink } = this.state;
const { onChange, getCurrentAnchor } = this.props; const { onChange, getCurrentAnchor } = this.props;
if (activeLink === link) { if (activeLink === link) {
@ -237,7 +242,9 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
this.setState({ this.setState({
activeLink: typeof getCurrentAnchor === 'function' ? getCurrentAnchor(link) : link, activeLink: typeof getCurrentAnchor === 'function' ? getCurrentAnchor(link) : link,
}); });
onChange?.(link); if (triggerChange) {
onChange?.(link);
}
}; };
handleScroll = () => { handleScroll = () => {

View File

@ -736,5 +736,21 @@ describe('Anchor Render', () => {
fireEvent.click(container.querySelector(`a[href="#${hash2}"]`)!); fireEvent.click(container.querySelector(`a[href="#${hash2}"]`)!);
expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash2}`); expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash2}`);
}); });
// https://github.com/ant-design/ant-design/issues/37627
it('should update anchorLink when component is rerender', async () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const Demo: React.FC<{ current: string }> = ({ current }) => (
<Anchor getCurrentAnchor={() => `#${current}`}>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>
);
const { container, rerender } = render(<Demo current={hash1} />);
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash1);
rerender(<Demo current={hash2} />);
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash2);
});
}); });
}); });

View File

@ -121,11 +121,12 @@ const Card = React.forwardRef((props: CardProps, ref: React.Ref<HTMLDivElement>)
{...extraProps} {...extraProps}
className={`${prefixCls}-head-tabs`} className={`${prefixCls}-head-tabs`}
onChange={onTabChange} onChange={onTabChange}
> items={tabList.map(item => ({
{tabList.map(item => ( label: item.tab,
<Tabs.TabPane tab={item.tab} disabled={item.disabled} key={item.key} /> key: item.key,
))} disabled: item.disabled ?? false,
</Tabs> }))}
/>
) : null; ) : null;
if (title || extra || tabs) { if (title || extra || tabs) {
head = ( head = (

View File

@ -767,6 +767,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-tabtest" aria-controls="rc-tabs-test-panel-tabtest"
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tabtest" id="rc-tabs-test-tab-tabtest"
@ -781,6 +782,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-tabtest" aria-controls="rc-tabs-test-panel-tabtest"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tabtest" id="rc-tabs-test-tab-tabtest"
@ -907,6 +909,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-article" aria-controls="rc-tabs-test-panel-article"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-article" id="rc-tabs-test-tab-article"
@ -921,6 +924,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-app" aria-controls="rc-tabs-test-panel-app"
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-app" id="rc-tabs-test-tab-app"
@ -935,6 +939,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-project" aria-controls="rc-tabs-test-panel-project"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-project" id="rc-tabs-test-tab-project"

View File

@ -767,6 +767,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-tabtest" aria-controls="rc-tabs-test-panel-tabtest"
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tabtest" id="rc-tabs-test-tab-tabtest"
@ -781,6 +782,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-tabtest" aria-controls="rc-tabs-test-panel-tabtest"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-tabtest" id="rc-tabs-test-tab-tabtest"
@ -888,6 +890,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-article" aria-controls="rc-tabs-test-panel-article"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-article" id="rc-tabs-test-tab-article"
@ -902,6 +905,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-app" aria-controls="rc-tabs-test-panel-app"
aria-disabled="false"
aria-selected="true" aria-selected="true"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-app" id="rc-tabs-test-tab-app"
@ -916,6 +920,7 @@ Array [
> >
<div <div
aria-controls="rc-tabs-test-panel-project" aria-controls="rc-tabs-test-panel-project"
aria-disabled="false"
aria-selected="false" aria-selected="false"
class="ant-tabs-tab-btn" class="ant-tabs-tab-btn"
id="rc-tabs-test-tab-project" id="rc-tabs-test-tab-project"

View File

@ -6,8 +6,6 @@ import Button from '../../button/index';
import Card from '../index'; import Card from '../index';
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
console.log('fireEvent');
describe('Card', () => { describe('Card', () => {
mountTest(Card); mountTest(Card);
rtlTest(Card); rtlTest(Card);

View File

@ -12,6 +12,7 @@ describe('Icon', () => {
it('should throw Error', () => { it('should throw Error', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Icon />); render(<Icon />);
expect(errSpy).toHaveBeenCalled(); expect(errSpy).toHaveBeenCalledWith('Warning: [antd: Icon] Empty Icon');
errSpy.mockRestore();
}); });
}); });

View File

@ -31,6 +31,8 @@ const localeValues: Locale = {
searchPlaceholder: 'Procurar...', searchPlaceholder: 'Procurar...',
itemUnit: 'item', itemUnit: 'item',
itemsUnit: 'itens', itemsUnit: 'itens',
selectAll: 'Seleccionar Tudo',
selectInvert: 'Inverter a página actual',
}, },
Upload: { Upload: {
uploading: 'A carregar...', uploading: 'A carregar...',

View File

@ -132,6 +132,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
close, close,
zIndex, zIndex,
afterClose, afterClose,
visible,
open, open,
keyboard, keyboard,
centered, centered,
@ -149,6 +150,14 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
focusTriggerAfterClose, focusTriggerAfterClose,
} = props; } = props;
if (process.env.NODE_ENV !== 'production') {
warning(
visible === undefined,
'Modal',
`\`visible\` is deprecated, please use \`open\` instead.`,
);
}
const confirmPrefixCls = `${prefixCls}-confirm`; const confirmPrefixCls = `${prefixCls}-confirm`;
const width = props.width || 416; const width = props.width || 416;

View File

@ -44,14 +44,7 @@ export default function confirm(config: ModalFuncProps) {
reactUnmount(container); reactUnmount(container);
} }
function render({ function render({ okText, cancelText, prefixCls: customizePrefixCls, ...props }: any) {
okText,
cancelText,
prefixCls: customizePrefixCls,
open,
visible,
...props
}: any) {
/** /**
* https://github.com/ant-design/ant-design/issues/23623 * https://github.com/ant-design/ant-design/issues/23623
* *
@ -68,7 +61,6 @@ export default function confirm(config: ModalFuncProps) {
reactRender( reactRender(
<ConfirmDialog <ConfirmDialog
{...props} {...props}
open={open ?? visible}
prefixCls={prefixCls} prefixCls={prefixCls}
rootPrefixCls={rootPrefixCls} rootPrefixCls={rootPrefixCls}
iconPrefixCls={iconPrefixCls} iconPrefixCls={iconPrefixCls}
@ -93,6 +85,12 @@ export default function confirm(config: ModalFuncProps) {
destroy.apply(this, args); destroy.apply(this, args);
}, },
}; };
// Legacy support
if (currentConfig.visible) {
delete currentConfig.visible;
}
render(currentConfig); render(currentConfig);
} }

View File

@ -311,7 +311,7 @@ function InternalTable<RecordType extends object = any>(
); );
}; };
const [transformFilterColumns, filterStates, getFilters] = useFilter<RecordType>({ const [transformFilterColumns, filterStates, filters] = useFilter<RecordType>({
prefixCls, prefixCls,
locale: tableLocale, locale: tableLocale,
dropdownPrefixCls, dropdownPrefixCls,
@ -321,16 +321,23 @@ function InternalTable<RecordType extends object = any>(
}); });
const mergedData = getFilterData(sortedData, filterStates); const mergedData = getFilterData(sortedData, filterStates);
changeEventInfo.filters = getFilters(); changeEventInfo.filters = filters;
changeEventInfo.filterStates = filterStates; changeEventInfo.filterStates = filterStates;
// ============================ Column ============================ // ============================ Column ============================
const columnTitleProps = React.useMemo( const columnTitleProps = React.useMemo(() => {
() => ({ const mergedFilters: Record<string, FilterValue> = {};
Object.keys(filters).forEach(filterKey => {
if (filters[filterKey] !== null) {
mergedFilters[filterKey] = filters[filterKey]!;
}
});
return {
...sorterTitleProps, ...sorterTitleProps,
}), filters: mergedFilters,
[sorterTitleProps], };
); }, [sorterTitleProps, filters]);
const [transformTitleColumns] = useTitleColumns(columnTitleProps); const [transformTitleColumns] = useTitleColumns(columnTitleProps);
// ========================== Pagination ========================== // ========================== Pagination ==========================

View File

@ -13,6 +13,7 @@ import type { SelectProps } from '../../select';
import type { ColumnGroupType, ColumnType, TableProps } from '..'; import type { ColumnGroupType, ColumnType, TableProps } from '..';
import type { ColumnFilterItem, FilterDropdownProps, FilterValue } from '../interface'; import type { ColumnFilterItem, FilterDropdownProps, FilterValue } from '../interface';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';
import type { TreeColumnFilterItem } from '../hooks/useFilter/FilterDropdown';
// https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73 // https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } }; const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
@ -1950,6 +1951,35 @@ describe('Table.filter', () => {
expect(container.querySelectorAll('li.ant-dropdown-menu-item').length).toBe(2); expect(container.querySelectorAll('li.ant-dropdown-menu-item').length).toBe(2);
}); });
it('should supports filterSearch has type of function when filterMode is tree', () => {
jest.spyOn(console, 'error').mockImplementation(() => undefined);
const { container } = render(
createTable({
columns: [
{
...column,
filterMode: 'tree',
filters: [
{ text: '节点一', value: 'node1' },
{ text: '节点二', value: 'node2' },
{ text: '节点三', value: 'node3' },
],
filterSearch: (input: any, record: TreeColumnFilterItem) =>
(record.title as string).includes(input),
},
],
}),
);
fireEvent.click(container.querySelector('span.ant-dropdown-trigger')!, nativeEvent);
act(() => {
jest.runAllTimers();
});
expect(container.querySelectorAll('.ant-table-filter-dropdown-tree').length).toBe(1);
expect(container.querySelectorAll('.ant-input').length).toBe(1);
fireEvent.change(container.querySelector('.ant-input')!, { target: { value: '节点二' } });
expect(container.querySelectorAll('.ant-tree-treenode.filter-node').length).toBe(1);
});
it('supports check all items', () => { it('supports check all items', () => {
jest.spyOn(console, 'error').mockImplementation(() => undefined); jest.spyOn(console, 'error').mockImplementation(() => undefined);
const { container } = render( const { container } = render(
@ -2457,4 +2487,26 @@ describe('Table.filter', () => {
?.disabled, ?.disabled,
).toBeTruthy(); ).toBeTruthy();
}); });
it('title render function support `filter`', () => {
const title = jest.fn(() => 'RenderTitle');
const { container } = render(
createTable({
columns: [
{
...column,
title,
filteredValue: ['boy'],
},
],
}),
);
expect(container.querySelector('.ant-table-column-title')?.textContent).toEqual('RenderTitle');
expect(title).toHaveBeenCalledWith(
expect.objectContaining({
filters: { name: ['boy'] },
}),
);
});
}); });

View File

@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import type { ColumnProps } from '..'; import type { ColumnProps } from '..';
import type { TreeColumnFilterItem } from '../hooks/useFilter/FilterDropdown';
import Table from '../Table'; import Table from '../Table';
const { Column, ColumnGroup } = Table; const { Column, ColumnGroup } = Table;
@ -48,6 +49,8 @@ describe('Table.typescript types', () => {
{ {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
filterSearch: (input: any, record: TreeColumnFilterItem) =>
(record.title as string).includes(input),
}, },
]; ];

View File

@ -61,7 +61,7 @@ function renderFilterItems({
filteredKeys: Key[]; filteredKeys: Key[];
filterMultiple: boolean; filterMultiple: boolean;
searchValue: string; searchValue: string;
filterSearch: FilterSearchType; filterSearch: FilterSearchType<ColumnFilterItem>;
}): Required<MenuProps>['items'] { }): Required<MenuProps>['items'] {
return filters.map((filter, index) => { return filters.map((filter, index) => {
const key = String(filter.value); const key = String(filter.value);
@ -103,6 +103,8 @@ function renderFilterItems({
}); });
} }
export type TreeColumnFilterItem = ColumnFilterItem & FilterTreeDataNode;
export interface FilterDropdownProps<RecordType> { export interface FilterDropdownProps<RecordType> {
tablePrefixCls: string; tablePrefixCls: string;
prefixCls: string; prefixCls: string;
@ -111,7 +113,7 @@ export interface FilterDropdownProps<RecordType> {
filterState?: FilterState<RecordType>; filterState?: FilterState<RecordType>;
filterMultiple: boolean; filterMultiple: boolean;
filterMode?: 'menu' | 'tree'; filterMode?: 'menu' | 'tree';
filterSearch?: FilterSearchType; filterSearch?: FilterSearchType<ColumnFilterItem | TreeColumnFilterItem>;
columnKey: Key; columnKey: Key;
children: React.ReactNode; children: React.ReactNode;
triggerFilter: (filterState: FilterState<RecordType>) => void; triggerFilter: (filterState: FilterState<RecordType>) => void;
@ -312,6 +314,12 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
} }
return item; return item;
}); });
const getFilterData = (node: FilterTreeDataNode): TreeColumnFilterItem => ({
...node,
text: node.title,
value: node.key,
children: node.children?.map(item => getFilterData(item)) || [],
});
let dropdownContent: React.ReactNode; let dropdownContent: React.ReactNode;
@ -348,7 +356,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
if (filterMode === 'tree') { if (filterMode === 'tree') {
return ( return (
<> <>
<FilterSearch <FilterSearch<TreeColumnFilterItem>
filterSearch={filterSearch} filterSearch={filterSearch}
value={searchValue} value={searchValue}
onChange={onSearch} onChange={onSearch}
@ -385,7 +393,12 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
defaultExpandAll defaultExpandAll
filterTreeNode={ filterTreeNode={
searchValue.trim() searchValue.trim()
? node => searchValueMatched(searchValue, node.title) ? node => {
if (typeof filterSearch === 'function') {
return filterSearch(searchValue, getFilterData(node));
}
return searchValueMatched(searchValue, node.title);
}
: undefined : undefined
} }
/> />

View File

@ -3,21 +3,21 @@ import * as React from 'react';
import Input from '../../../input'; import Input from '../../../input';
import type { FilterSearchType, TableLocale } from '../../interface'; import type { FilterSearchType, TableLocale } from '../../interface';
interface FilterSearchProps { interface FilterSearchProps<RecordType = any> {
value: string; value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
filterSearch: FilterSearchType; filterSearch: FilterSearchType<RecordType>;
tablePrefixCls: string; tablePrefixCls: string;
locale: TableLocale; locale: TableLocale;
} }
const FilterSearch: React.FC<FilterSearchProps> = ({ function FilterSearch<RecordType>({
value, value,
onChange, onChange,
filterSearch, filterSearch,
tablePrefixCls, tablePrefixCls,
locale, locale,
}) => { }: FilterSearchProps<RecordType>) {
if (!filterSearch) { if (!filterSearch) {
return null; return null;
} }
@ -34,6 +34,6 @@ const FilterSearch: React.FC<FilterSearchProps> = ({
/> />
</div> </div>
); );
}; }
export default FilterSearch; export default FilterSearch;

View File

@ -203,7 +203,7 @@ function useFilter<RecordType>({
}: FilterConfig<RecordType>): [ }: FilterConfig<RecordType>): [
TransformColumns<RecordType>, TransformColumns<RecordType>,
FilterState<RecordType>[], FilterState<RecordType>[],
() => Record<string, FilterValue | null>, Record<string, FilterValue | null>,
] { ] {
const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>( const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>(
collectFilterStates(mergedColumns, true), collectFilterStates(mergedColumns, true),
@ -235,10 +235,7 @@ function useFilter<RecordType>({
return collectedStates; return collectedStates;
}, [mergedColumns, filterStates]); }, [mergedColumns, filterStates]);
const getFilters = React.useCallback( const filters = React.useMemo(() => generateFilterInfo(mergedFilterStates), [mergedFilterStates]);
() => generateFilterInfo(mergedFilterStates),
[mergedFilterStates],
);
const triggerFilter = (filterState: FilterState<RecordType>) => { const triggerFilter = (filterState: FilterState<RecordType>) => {
const newFilterStates = mergedFilterStates.filter(({ key }) => key !== filterState.key); const newFilterStates = mergedFilterStates.filter(({ key }) => key !== filterState.key);
@ -258,7 +255,7 @@ function useFilter<RecordType>({
tableLocale, tableLocale,
); );
return [transformColumns, mergedFilterStates, getFilters]; return [transformColumns, mergedFilterStates, filters];
} }
export default useFilter; export default useFilter;

View File

@ -63,7 +63,7 @@ export interface ColumnTitleProps<RecordType> {
sortColumn?: ColumnType<RecordType>; sortColumn?: ColumnType<RecordType>;
sortColumns?: { column: ColumnType<RecordType>; order: SortOrder }[]; sortColumns?: { column: ColumnType<RecordType>; order: SortOrder }[];
filters?: Record<string, string[]>; filters?: Record<string, FilterValue>;
} }
export type ColumnTitle<RecordType> = export type ColumnTitle<RecordType> =
@ -72,7 +72,9 @@ export type ColumnTitle<RecordType> =
export type FilterValue = (Key | boolean)[]; export type FilterValue = (Key | boolean)[];
export type FilterKey = Key[] | null; export type FilterKey = Key[] | null;
export type FilterSearchType = boolean | ((input: string, record: {}) => boolean); export type FilterSearchType<RecordType = Record<string, any>> =
| boolean
| ((input: string, record: RecordType) => boolean);
export interface FilterConfirmProps { export interface FilterConfirmProps {
closeDropdown: boolean; closeDropdown: boolean;
} }
@ -112,7 +114,7 @@ export interface ColumnType<RecordType> extends Omit<RcColumnType<RecordType>, '
defaultFilteredValue?: FilterValue | null; defaultFilteredValue?: FilterValue | null;
filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode); filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode);
filterMode?: 'menu' | 'tree'; filterMode?: 'menu' | 'tree';
filterSearch?: FilterSearchType; filterSearch?: FilterSearchType<ColumnFilterItem>;
onFilter?: (value: string | number | boolean, record: RecordType) => boolean; onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
filterDropdownOpen?: boolean; filterDropdownOpen?: boolean;
onFilterDropdownOpenChange?: (visible: boolean) => void; onFilterDropdownOpenChange?: (visible: boolean) => void;

View File

@ -14,10 +14,12 @@ describe('Tabs.Animated', () => {
it('boolean: true', () => { it('boolean: true', () => {
const { result } = renderHook(() => useAnimateConfig('test', true)); const { result } = renderHook(() => useAnimateConfig('test', true));
expect(result.current).toEqual({ expect(result.current).toEqual(
inkBar: true, expect.objectContaining({
tabPane: false, inkBar: true,
}); tabPane: true,
}),
);
}); });
it('config', () => { it('config', () => {

View File

@ -26,7 +26,7 @@ export default function useAnimateConfig(
} else if (animated === true) { } else if (animated === true) {
mergedAnimated = { mergedAnimated = {
inkBar: true, inkBar: true,
tabPane: false, tabPane: true,
}; };
} else { } else {
mergedAnimated = { mergedAnimated = {

View File

@ -845,6 +845,7 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
}, },
[`${componentCls}-tabpane`]: { [`${componentCls}-tabpane`]: {
outline: 'none',
'&-hidden': { '&-hidden': {
display: 'none', display: 'none',
}, },

View File

@ -538,4 +538,16 @@ describe('Tooltip', () => {
jest.useRealTimers(); jest.useRealTimers();
errSpy.mockRestore(); errSpy.mockRestore();
}); });
it('not inject className when children className is not string type', () => {
const HOC = ({ className }: { className: Function }) => <span className={className()} />;
const { container } = render(
<Tooltip open>
<HOC className={() => 'bamboo'} />
</Tooltip>,
);
expect(container.querySelector('.bamboo')).toBeTruthy();
expect(container.querySelector('.ant-tooltip')).toBeTruthy();
});
}); });

View File

@ -30,7 +30,7 @@ The following APIs are shared by Tooltip, Popconfirm, Popover.
| color | The background color | string | - | 4.3.0 | | color | The background color | string | - | 4.3.0 |
| defaultOpen | Whether the floating tooltip card is open by default | boolean | false | 4.23.0 | | defaultOpen | Whether the floating tooltip card is open by default | boolean | false | 4.23.0 |
| destroyTooltipOnHide | Whether destroy tooltip when hidden, parent container of tooltip will be destroyed when `keepParent` is false | boolean \| { keepParent?: boolean } | false | | | destroyTooltipOnHide | Whether destroy tooltip when hidden, parent container of tooltip will be destroyed when `keepParent` is false | boolean \| { keepParent?: boolean } | false | |
| getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body` | function(triggerNode) | () => document.body | | | getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body` | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
| mouseEnterDelay | Delay in seconds, before tooltip is shown on mouse enter | number | 0.1 | | | mouseEnterDelay | Delay in seconds, before tooltip is shown on mouse enter | number | 0.1 | |
| mouseLeaveDelay | Delay in seconds, before tooltip is hidden on mouse leave | number | 0.1 | | | mouseLeaveDelay | Delay in seconds, before tooltip is hidden on mouse leave | number | 0.1 | |
| overlayClassName | Class name of the tooltip card | string | - | | | overlayClassName | Class name of the tooltip card | string | - | |
@ -40,7 +40,7 @@ The following APIs are shared by Tooltip, Popconfirm, Popover.
| trigger | Tooltip trigger mode. Could be multiple by passing an array | `hover` \| `focus` \| `click` \| `contextMenu` \| Array&lt;string> | `hover` | | | trigger | Tooltip trigger mode. Could be multiple by passing an array | `hover` \| `focus` \| `click` \| `contextMenu` \| Array&lt;string> | `hover` | |
| open | Whether the floating tooltip card is open or not. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | false | 4.23.0 | | open | Whether the floating tooltip card is open or not. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | false | 4.23.0 |
| zIndex | Config `z-index` of Tooltip | number | - | | | zIndex | Config `z-index` of Tooltip | number | - | |
| onOpenChange | Callback executed when visibility of the tooltip card is changed | (open) => void | - | 4.23.0 | | onOpenChange | Callback executed when visibility of the tooltip card is changed | (open: boolean) => void | - | 4.23.0 |
## Note ## Note

View File

@ -276,9 +276,12 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
prefixCls, prefixCls,
); );
const childProps = child.props; const childProps = child.props;
const childCls = classNames(childProps.className, { const childCls =
[openClassName || `${prefixCls}-open`]: true, !childProps.className || typeof childProps.className === 'string'
}); ? classNames(childProps.className, {
[openClassName || `${prefixCls}-open`]: true,
})
: childProps.className;
// Style // Style
const [wrapSSR, hashId] = useStyle(prefixCls, !injectFromPopover); const [wrapSSR, hashId] = useStyle(prefixCls, !injectFromPopover);

View File

@ -32,7 +32,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
| color | 背景颜色 | string | - | 4.3.0 | | color | 背景颜色 | string | - | 4.3.0 |
| defaultOpen | 默认是否显隐 | boolean | false | 4.23.0 | | defaultOpen | 默认是否显隐 | boolean | false | 4.23.0 |
| destroyTooltipOnHide | 关闭后是否销毁 Tooltip`keepParent``false` 时销毁父容器 | boolean \| { keepParent?: boolean } | false | | | destroyTooltipOnHide | 关闭后是否销毁 Tooltip`keepParent``false` 时销毁父容器 | boolean \| { keepParent?: boolean } | false | |
| getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | function(triggerNode) | () => document.body | | | getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
| mouseEnterDelay | 鼠标移入后延时多少才显示 Tooltip单位秒 | number | 0.1 | | | mouseEnterDelay | 鼠标移入后延时多少才显示 Tooltip单位秒 | number | 0.1 | |
| mouseLeaveDelay | 鼠标移出后延时多少才隐藏 Tooltip单位秒 | number | 0.1 | | | mouseLeaveDelay | 鼠标移出后延时多少才隐藏 Tooltip单位秒 | number | 0.1 | |
| overlayClassName | 卡片类名 | string | - | | | overlayClassName | 卡片类名 | string | - | |
@ -42,7 +42,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
| trigger | 触发行为,可选 `hover` \| `focus` \| `click` \| `contextMenu`,可使用数组设置多个触发行为 | string \| string\[] | `hover` | | | trigger | 触发行为,可选 `hover` \| `focus` \| `click` \| `contextMenu`,可使用数组设置多个触发行为 | string \| string\[] | `hover` | |
| open | 用于手动控制浮层显隐,小于 4.23.0 使用 `visible`[为什么?](/docs/react/faq#why-open) | boolean | false | 4.23.0 | | open | 用于手动控制浮层显隐,小于 4.23.0 使用 `visible`[为什么?](/docs/react/faq#why-open) | boolean | false | 4.23.0 |
| zIndex | 设置 Tooltip 的 `z-index` | number | - | | | zIndex | 设置 Tooltip 的 `z-index` | number | - | |
| onOpenChange | 显示隐藏的回调 | (open) => void | - | 4.23.0 | | onOpenChange | 显示隐藏的回调 | (open: boolean) => void | - | 4.23.0 |
## 注意 ## 注意

View File

@ -24,7 +24,7 @@ Basic text writing, including headings, body text, lists, and more.
| delete | Deleted line style | boolean | false | | | delete | Deleted line style | boolean | false | |
| disabled | Disabled content | boolean | false | | | disabled | Disabled content | boolean | false | |
| editable | If editable. Can control edit state when is object | boolean \| [editable](#editable) | false | [editable](#editable) | | editable | If editable. Can control edit state when is object | boolean \| [editable](#editable) | false | [editable](#editable) |
| ellipsis | Display ellipsis when text overflowscan't configure expandable、rows and onExpand by using object | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) | | ellipsis | Display ellipsis when text overflowscan't configure expandable、rows and onExpand by using object. Diff with Typography.Paragraph, Text do not have 100% width style which means it will fix width on the first ellipsis. If you want to have responsive ellipsis, please set width manually | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |
| keyboard | Keyboard style | boolean | false | 4.3.0 | | keyboard | Keyboard style | boolean | false | 4.3.0 |
| mark | Marked style | boolean | false | | | mark | Marked style | boolean | false | |
| onClick | Set the handler to handle click event | (event) => void | - | | | onClick | Set the handler to handle click event | (event) => void | - | |

View File

@ -25,7 +25,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
| delete | 添加删除线样式 | boolean | false | | | delete | 添加删除线样式 | boolean | false | |
| disabled | 禁用文本 | boolean | false | | | disabled | 禁用文本 | boolean | false | |
| editable | 是否可编辑,为对象时可对编辑进行控制 | boolean \| [editable](#editable) | false | [editable](#editable) | | editable | 是否可编辑,为对象时可对编辑进行控制 | boolean \| [editable](#editable) | false | [editable](#editable) |
| ellipsis | 自动溢出省略为对象时不能设置省略行数、是否可展开、onExpand 展开事件 | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) | | ellipsis | 自动溢出省略为对象时不能设置省略行数、是否可展开、onExpand 展开事件。不同于 Typography.ParagraphText 组件自身不带 100% 宽度样式,因而默认情况下初次缩略后宽度便不再变化。如果需要自适应宽度,请手工配置宽度样式 | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |
| keyboard | 添加键盘样式 | boolean | false | 4.3.0 | | keyboard | 添加键盘样式 | boolean | false | 4.3.0 |
| mark | 添加标记样式 | boolean | false | | | mark | 添加标记样式 | boolean | false | |
| onClick | 点击 Text 时的回调 | (event) => void | - | | | onClick | 点击 Text 时的回调 | (event) => void | - | |

View File

@ -83,6 +83,10 @@ toc: false
- https://mastergo-local-default.oss-cn-beijing.aliyuncs.com/ant-design-mastergo.svg - https://mastergo-local-default.oss-cn-beijing.aliyuncs.com/ant-design-mastergo.svg
- 可在「MasterGo」在线免费使用的全套组件和模板 - 可在「MasterGo」在线免费使用的全套组件和模板
- https://mastergo.com/community/?utm_source=antdesign&utm_medium=link&utm_campaign=resource&cata_name=AntDesign - https://mastergo.com/community/?utm_source=antdesign&utm_medium=link&utm_campaign=resource&cata_name=AntDesign
- Raycast 拓展
- https://gw.alipayobjects.com/zos/basement_prod/5edc7f4d-3302-4710-963b-7b6c77ea8d06.svg
- mac 用户可使用 Raycast 快速打开 Ant Design 组件
- https://www.raycast.com/crazyair/antd-open-browser
## 文章 ## 文章

View File

@ -111,6 +111,7 @@
"@ant-design/react-slick": "~0.29.1", "@ant-design/react-slick": "~0.29.1",
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@ctrl/tinycolor": "^3.4.0", "@ctrl/tinycolor": "^3.4.0",
"@testing-library/user-event": "^14.4.2",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"copy-to-clipboard": "^3.2.0", "copy-to-clipboard": "^3.2.0",
"dayjs": "^1.11.1", "dayjs": "^1.11.1",
@ -125,7 +126,7 @@
"rc-field-form": "~1.27.0", "rc-field-form": "~1.27.0",
"rc-image": "~5.7.0", "rc-image": "~5.7.0",
"rc-input": "~0.1.2", "rc-input": "~0.1.2",
"rc-input-number": "~7.3.5", "rc-input-number": "~7.3.9",
"rc-mentions": "~1.9.1", "rc-mentions": "~1.9.1",
"rc-menu": "~9.6.3", "rc-menu": "~9.6.3",
"rc-motion": "^2.6.1", "rc-motion": "^2.6.1",
@ -136,7 +137,7 @@
"rc-rate": "~2.9.0", "rc-rate": "~2.9.0",
"rc-resize-observer": "^1.2.0", "rc-resize-observer": "^1.2.0",
"rc-segmented": "~2.1.0", "rc-segmented": "~2.1.0",
"rc-select": "~14.1.1", "rc-select": "~14.1.13",
"rc-slider": "~10.0.0", "rc-slider": "~10.0.0",
"rc-steps": "~4.1.0", "rc-steps": "~4.1.0",
"rc-switch": "~4.0.0", "rc-switch": "~4.0.0",
@ -230,7 +231,7 @@
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"immer": "^9.0.1", "immer": "^9.0.1",
"immutability-helper": "^3.0.0", "immutability-helper": "^3.0.0",
"inquirer": "^8.0.0", "inquirer": "^9.1.2",
"intersection-observer": "^0.12.0", "intersection-observer": "^0.12.0",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"jest": "^28.0.3", "jest": "^28.0.3",

View File

@ -36,6 +36,7 @@ const MAINTAINERS = [
'fireairforce', 'fireairforce',
'kerm1it', 'kerm1it',
'madccc', 'madccc',
'MadCcc',
].map(author => author.toLowerCase()); ].map(author => author.toLowerCase());
const cwd = process.cwd(); const cwd = process.cwd();

View File

@ -4,14 +4,7 @@ import { FormattedMessage, injectIntl } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import { Row, Col, Affix, Tooltip } from 'antd'; import { Row, Col, Affix, Tooltip } from 'antd';
import { getChildren } from 'jsonml.js/lib/utils'; import { getChildren } from 'jsonml.js/lib/utils';
import { import { CodeFilled, CodeOutlined, BugFilled, BugOutlined } from '@ant-design/icons';
CodeFilled,
CodeOutlined,
BugFilled,
BugOutlined,
ExperimentOutlined,
ExperimentFilled,
} from '@ant-design/icons';
import Demo from './Demo'; import Demo from './Demo';
import EditButton from './EditButton'; import EditButton from './EditButton';
import { ping, getMetaDescription } from '../utils'; import { ping, getMetaDescription } from '../utils';
@ -25,7 +18,6 @@ class ComponentDoc extends React.Component {
expandAll: false, expandAll: false,
visibleAll: process.env.NODE_ENV !== 'production', visibleAll: process.env.NODE_ENV !== 'production',
showRiddleButton: false, showRiddleButton: false,
react17Demo: false,
}; };
componentDidMount() { componentDidMount() {
@ -108,7 +100,7 @@ class ComponentDoc extends React.Component {
} = this.props; } = this.props;
const { content, meta } = doc; const { content, meta } = doc;
const demoValues = Object.keys(demos).map(key => demos[key]); const demoValues = Object.keys(demos).map(key => demos[key]);
const { expandAll, visibleAll, showRiddleButton, react17Demo } = this.state; const { expandAll, visibleAll, showRiddleButton } = this.state;
const isSingleCol = meta.cols === 1; const isSingleCol = meta.cols === 1;
const leftChildren = []; const leftChildren = [];
const rightChildren = []; const rightChildren = [];
@ -131,7 +123,6 @@ class ComponentDoc extends React.Component {
location={location} location={location}
theme={theme} theme={theme}
setIframeTheme={setIframeTheme} setIframeTheme={setIframeTheme}
react18={react17Demo}
/> />
); );
if (index % 2 === 0 || isSingleCol) { if (index % 2 === 0 || isSingleCol) {
@ -220,27 +211,6 @@ class ComponentDoc extends React.Component {
<BugOutlined className={expandTriggerClass} onClick={this.handleVisibleToggle} /> <BugOutlined className={expandTriggerClass} onClick={this.handleVisibleToggle} />
)} )}
</Tooltip> </Tooltip>
<Tooltip
title={
<FormattedMessage
id={`app.component.examples.${
react17Demo ? 'openDemoWithReact18' : 'openDemoNotReact18'
}`}
/>
}
>
{react17Demo ? (
<ExperimentFilled
className={expandTriggerClass}
onClick={this.handleDemoVersionToggle}
/>
) : (
<ExperimentOutlined
className={expandTriggerClass}
onClick={this.handleDemoVersionToggle}
/>
)}
</Tooltip>
</span> </span>
</h2> </h2>
</section> </section>

View File

@ -48,14 +48,13 @@ class Demo extends React.Component {
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
const { codeExpand, copied, copyTooltipOpen } = this.state; const { codeExpand, copied, copyTooltipOpen } = this.state;
const { expand, theme, showRiddleButton, react18 } = this.props; const { expand, theme, showRiddleButton } = this.props;
return ( return (
(codeExpand || expand) !== (nextState.codeExpand || nextProps.expand) || (codeExpand || expand) !== (nextState.codeExpand || nextProps.expand) ||
copied !== nextState.copied || copied !== nextState.copied ||
copyTooltipOpen !== nextState.copyTooltipOpen || copyTooltipOpen !== nextState.copyTooltipOpen ||
nextProps.theme !== theme || nextProps.theme !== theme ||
nextProps.showRiddleButton !== showRiddleButton || nextProps.showRiddleButton !== showRiddleButton
nextProps.react18 !== react18
); );
} }
@ -137,7 +136,6 @@ class Demo extends React.Component {
intl: { locale }, intl: { locale },
theme, theme,
showRiddleButton, showRiddleButton,
react18,
} = props; } = props;
const { copied, copyTooltipOpen } = state; const { copied, copyTooltipOpen } = state;
if (!this.liveDemo) { if (!this.liveDemo) {
@ -206,13 +204,13 @@ class Demo extends React.Component {
); );
dependencies['@ant-design/icons'] = 'latest'; dependencies['@ant-design/icons'] = 'latest';
dependencies.react = react18 ? '^18.0.0' : '^17.0.0'; dependencies.react = '^18.0.0';
dependencies['react-dom'] = react18 ? '^18.0.0' : '^17.0.0'; dependencies['react-dom'] = '^18.0.0';
const codepenPrefillConfig = { const codepenPrefillConfig = {
title: `${localizedTitle} - antd@${dependencies.antd}`, title: `${localizedTitle} - antd@${dependencies.antd}`,
html, html,
js: `${react18 ? 'const { createRoot } = ReactDOM;\n' : ''}${sourceCode js: `${'const { createRoot } = ReactDOM;\n'}${sourceCode
.replace(/import\s+(?:React,\s+)?{(\s+[^}]*\s+)}\s+from\s+'react'/, `const { $1 } = React;`) .replace(/import\s+(?:React,\s+)?{(\s+[^}]*\s+)}\s+from\s+'react'/, `const { $1 } = React;`)
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'antd';/, 'const { $1 } = antd;') .replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'antd';/, 'const { $1 } = antd;')
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'@ant-design\/icons';/, 'const { $1 } = icons;') .replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'@ant-design\/icons';/, 'const { $1 } = icons;')
@ -224,20 +222,17 @@ class Demo extends React.Component {
'const { $1 } = ReactRouterDOM;', 'const { $1 } = ReactRouterDOM;',
) )
.replace(/([A-Za-z]*)\s+as\s+([A-Za-z]*)/, '$1:$2') .replace(/([A-Za-z]*)\s+as\s+([A-Za-z]*)/, '$1:$2')
.replace(/export default/, 'const ComponentDemo =')}\n\n${ .replace(
react18 /export default/,
? 'createRoot(mountNode).render(<ComponentDemo />)' 'const ComponentDemo =',
: 'ReactDOM.render(<ComponentDemo />, mountNode)' )}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
};\n`,
css: prefillStyle, css: prefillStyle,
editors: '001', editors: '001',
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
css_external: `https://unpkg.com/antd@${antdReproduceVersion}/dist/antd.css`, css_external: `https://unpkg.com/antd@${antdReproduceVersion}/dist/antd.css`,
js_external: [ js_external: [
react18 ? 'react@18/umd/react.development.js' : 'react@16.x/umd/react.development.js', 'react@18/umd/react.development.js',
react18 'react-dom@18/umd/react-dom.development.js',
? 'react-dom@18/umd/react-dom.development.js'
: 'react-dom@16.x/umd/react-dom.development.js',
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
`antd@${antdReproduceVersion}/dist/antd-with-locales.js`, `antd@${antdReproduceVersion}/dist/antd-with-locales.js`,
`@ant-design/icons/dist/index.umd.js`, `@ant-design/icons/dist/index.umd.js`,
@ -253,15 +248,10 @@ class Demo extends React.Component {
title: `${localizedTitle} - antd@${dependencies.antd}`, title: `${localizedTitle} - antd@${dependencies.antd}`,
js: `${ js: `${
/import React(\D*)from 'react';/.test(sourceCode) ? '' : `import React from 'react';\n` /import React(\D*)from 'react';/.test(sourceCode) ? '' : `import React from 'react';\n`
}${ }import { createRoot } from 'react-dom/client';\n${sourceCode.replace(
react18 /export default/,
? `import { createRoot } from 'react-dom/client';\n` 'const ComponentDemo =',
: `import ReactDOM from 'react-dom';\n` )}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
}${sourceCode.replace(/export default/, 'const ComponentDemo =')}\n\n${
react18
? 'createRoot(mountNode).render(<ComponentDemo />)'
: 'ReactDOM.render(<ComponentDemo />, mountNode)'
};\n`,
css: prefillStyle, css: prefillStyle,
json: JSON.stringify( json: JSON.stringify(
{ {
@ -296,20 +286,12 @@ ${parsedSourceCode}
.replace('</style>', '') .replace('</style>', '')
.replace('<style>', ''); .replace('<style>', '');
const indexJsContent = react18 const indexJsContent = `
? `
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import Demo from './demo'; import Demo from './demo';
createRoot(document.getElementById('container')).render(<Demo />); createRoot(document.getElementById('container')).render(<Demo />);
`
: `
import React from 'react';
import ReactDOM from 'react-dom';
import Demo from './demo';
ReactDOM.render(<Demo />, document.getElementById('container'));
`; `;
const codesandboxPackage = { const codesandboxPackage = {
@ -317,8 +299,8 @@ ReactDOM.render(<Demo />, document.getElementById('container'));
main: 'index.js', main: 'index.js',
dependencies: { dependencies: {
...dependencies, ...dependencies,
react: react18 ? '^18.0.0' : '^16.14.0', react: '^18.0.0',
'react-dom': react18 ? '^18.0.0' : '^16.14.0', 'react-dom': '^18.0.0',
'react-scripts': '^4.0.0', 'react-scripts': '^4.0.0',
}, },
devDependencies: { devDependencies: {