diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md
index f96db93e9f..021c86cd6a 100644
--- a/CHANGELOG.en-US.md
+++ b/CHANGELOG.en-US.md
@@ -30,7 +30,7 @@ timeline: true
- 🛠 Table changes `filterDropdownVisible` to `filterDropdownOpen`. [#37026](https://github.com/ant-design/ant-design/pull/37026) [@yykoypj](https://github.com/yykoypj)
- 🛠 Slider add `tooltip` prop for all props related with Tooltip. [#37000](https://github.com/ant-design/ant-design/pull/37000) [@yykoypj](https://github.com/yykoypj)
- 🛠 Tooltip Popover and Popconfirm change `visible` to `open`. [#37241](https://github.com/ant-design/ant-design/pull/37241) [@yykoypj](https://github.com/yykoypj)
- - 🛠 Remove `visible` prop of Tag. [#36934](https://github.com/ant-design/ant-design/pull/36934) [@yykoypj](https://github.com/yykoypj)
+ - 🛠 Deprecate `visible` prop of Tag. [#36934](https://github.com/ant-design/ant-design/pull/36934) [@yykoypj](https://github.com/yykoypj)
- 🛠 Deprecate `dropdownClassName` prop of all components and change to `popupClassName`. [#36880](https://github.com/ant-design/ant-design/pull/36880) [@heiyu4585](https://github.com/heiyu4585)
- 🛠 Tabs support `items` props and origin jsx usage will be depreacted. [#36889](https://github.com/ant-design/ant-design/pull/36889)
- 🐞 Fix that some css variables are not consistent with less variables.
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index cf6b448e21..b08e8c4384 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -30,7 +30,7 @@ timeline: true
- 🛠 Table 组件 `columns` 中的 `filterDropdownVisible` 改为 `filterDropdownOpen`。[#37026](https://github.com/ant-design/ant-design/pull/37026) [@yykoypj](https://github.com/yykoypj)
- 🛠 Tooltip, Popover 和 Popconfirm 中的 `visible` 改为 `open`。[#37241](https://github.com/ant-design/ant-design/pull/37241) [@yykoypj](https://github.com/yykoypj)
- 🛠 Slider 的 `tooltip` 相关属性合并到 `tooltip` 属性中。[#37000](https://github.com/ant-design/ant-design/pull/37000) [@yykoypj](https://github.com/yykoypj)
- - 🛠 移除 Tag 组件的 `visible` 属性。[#36934](https://github.com/ant-design/ant-design/pull/36934) [@yykoypj](https://github.com/yykoypj)
+ - 🛠 废弃 Tag 组件的 `visible` 属性。[#36934](https://github.com/ant-design/ant-design/pull/36934) [@yykoypj](https://github.com/yykoypj)
- 🛠 废弃所有组件的 `dropdownClassName`,统一为 `popupClassName`。[#36880](https://github.com/ant-design/ant-design/pull/36880) [@heiyu4585](https://github.com/heiyu4585)
- 🛠 Tabs 支持 `items` 属性,并且废弃原 jsx 语法糖用法。[#36889](https://github.com/ant-design/ant-design/pull/36889)
- 🐞 修复 css 变量与 less 变量不一致的问题。
diff --git a/components/affix/__tests__/Affix.test.tsx b/components/affix/__tests__/Affix.test.tsx
index bd37d46298..47e4ee4794 100644
--- a/components/affix/__tests__/Affix.test.tsx
+++ b/components/affix/__tests__/Affix.test.tsx
@@ -141,7 +141,7 @@ describe('Affix Render', () => {
describe('updatePosition when target changed', () => {
it('function change', async () => {
document.body.innerHTML = '
';
- const container = document.querySelector('#id') as HTMLDivElement;
+ const container = document.getElementById('mounter');
const getTarget = () => container;
let affixInstance: InternalAffixClass;
const { rerender } = render(
diff --git a/components/back-top/index.tsx b/components/back-top/index.tsx
index 2bf17a1890..0ef6b3513d 100644
--- a/components/back-top/index.tsx
+++ b/components/back-top/index.tsx
@@ -25,6 +25,33 @@ export interface BackTopProps {
visible?: boolean; // Only for test. Don't use it.
}
+interface ChildrenProps {
+ prefixCls: string;
+ rootPrefixCls: string;
+ children?: React.ReactNode;
+ visible?: boolean; // Only for test. Don't use it.
+}
+
+const BackTopContent: React.FC = props => {
+ const { prefixCls, rootPrefixCls, children, visible } = props;
+ const defaultElement = (
+
+ );
+ return (
+
+ {({ className: motionClassName }) =>
+ cloneElement(children || defaultElement, ({ className }) => ({
+ className: classNames(motionClassName, className),
+ }))
+ }
+
+ );
+};
+
const BackTop: React.FC = props => {
const [visible, setVisible] = useMergedState(false, {
value: props.visible,
@@ -51,9 +78,7 @@ const BackTop: React.FC = props => {
scrollEvent.current = addEventListener(container, 'scroll', (e: React.UIEvent) => {
handleScroll(e);
});
- handleScroll({
- target: container,
- });
+ handleScroll({ target: container });
};
React.useEffect(() => {
@@ -62,7 +87,7 @@ const BackTop: React.FC = props => {
if (scrollEvent.current) {
scrollEvent.current.remove();
}
- (handleScroll as any).cancel();
+ handleScroll.cancel();
};
}, [props.target]);
@@ -77,32 +102,6 @@ const BackTop: React.FC = props => {
}
};
- const renderChildren = ({
- prefixCls,
- rootPrefixCls,
- }: {
- prefixCls: string;
- rootPrefixCls: string;
- }) => {
- const { children } = props;
- const defaultElement = (
-
- );
- return (
-
- {({ className: motionClassName }) =>
- cloneElement(children || defaultElement, ({ className }) => ({
- className: classNames(motionClassName, className),
- }))
- }
-
- );
- };
-
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { prefixCls: customizePrefixCls, className = '' } = props;
const prefixCls = getPrefixCls('back-top', customizePrefixCls);
@@ -130,7 +129,9 @@ const BackTop: React.FC = props => {
return wrapSSR(
- {renderChildren({ prefixCls, rootPrefixCls })}
+
+ {props.children}
+
,
);
};
diff --git a/components/drawer/index.en-US.md b/components/drawer/index.en-US.md
index d0768fdc36..749794f16c 100644
--- a/components/drawer/index.en-US.md
+++ b/components/drawer/index.en-US.md
@@ -48,7 +48,7 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr
| style | Style of Drawer panel. Use `bodyStyle` if want to config body only | CSSProperties | - | |
| size | presetted size of drawer, default `378px` and large `736px` | 'default' \| 'large' | 'default' | 4.17.0 |
| title | The title for Drawer | ReactNode | - | |
-
-<<<<<<< HEAD | open | Whether the Drawer dialog is visible or not | boolean | false | | ======= | open | Whether the Drawer dialog is visible or not | boolean | false | 4.23.0 |
-
-> > > > > > > feature | width | Width of the Drawer dialog | string \| number | 378 | | | zIndex | The `z-index` of the Drawer | number | 1000 | | | onClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button | function(e) | - | |
+| open | Whether the Drawer dialog is visible or not | boolean | false | |
+| width | Width of the Drawer dialog | string \| number | 378 | |
+| zIndex | The `z-index` of the Drawer | number | 1000 | |
+| onClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button | function(e) | - | |
diff --git a/components/drawer/index.zh-CN.md b/components/drawer/index.zh-CN.md
index 0f23af0b72..975fd06a70 100644
--- a/components/drawer/index.zh-CN.md
+++ b/components/drawer/index.zh-CN.md
@@ -47,7 +47,7 @@ cover: https://img.alicdn.com/imgextra/i4/O1CN019djdZP1OHwXSRGCOW_!!600000000168
| size | 预设抽屉宽度(或高度),default `378px` 和 large `736px` | 'default' \| 'large' | 'default' | 4.17.0 |
| style | 设计 Drawer 容器样式,如果你只需要设置内容部分请使用 `bodyStyle` | CSSProperties | - | |
| title | 标题 | ReactNode | - | |
-
-<<<<<<< HEAD | open | Drawer 是否可见 | boolean | - | | ======= | open | Drawer 是否可见 | boolean | - | 4.23.0 |
-
-> > > > > > > feature | width | 宽度 | string \| number | 378 | | | zIndex | 设置 Drawer 的 `z-index` | number | 1000 | | | onClose | 点击遮罩层或左上角叉或取消按钮的回调 | function(e) | - | |
+| open | Drawer 是否可见 | boolean | - |
+| width | 宽度 | string \| number | 378 | |
+| zIndex | 设置 Drawer 的 `z-index` | number | 1000 | |
+| onClose | 点击遮罩层或左上角叉或取消按钮的回调 | function(e) | - | |
diff --git a/components/dropdown/index.en-US.md b/components/dropdown/index.en-US.md
index c714cc1ed1..c5c3038dd5 100644
--- a/components/dropdown/index.en-US.md
+++ b/components/dropdown/index.en-US.md
@@ -27,8 +27,8 @@ When there are more than a few options to choose from, you can wrap them in a `D
| overlayStyle | The style of the dropdown root element | CSSProperties | - | |
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
-| open | Whether the dropdown menu is currently open | boolean | - | 4.23.0 |
-| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item | (open: boolean) => void | - | 4.23.0 |
+| open | Whether the dropdown menu is currently open. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | - | 4.23.0 |
+| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item. Use `onVisibleChange` under 4.23.0 ([why?](/docs/react/faq#why-open)) | (open: boolean) => void | - | 4.23.0 |
You should use [Menu](/components/menu/) as `overlay`. The menu items and dividers are also available by using `Menu.Item` and `Menu.Divider`.
diff --git a/components/dropdown/index.zh-CN.md b/components/dropdown/index.zh-CN.md
index f5592a9a4e..c33e18ef04 100644
--- a/components/dropdown/index.zh-CN.md
+++ b/components/dropdown/index.zh-CN.md
@@ -31,8 +31,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
| overlayStyle | 下拉根元素的样式 | CSSProperties | - | |
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
| trigger | 触发下拉的行为, 移动端不支持 hover | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
-| open | 菜单是否显示 | boolean | - | 4.23.0 |
-| onOpenChange | 菜单显示状态改变时调用,参数为 `visible`。点击菜单按钮导致的消失不会触发 | (open: boolean) => void | - | 4.23.0 |
+| open | 菜单是否显示,小于 4.23.0 使用 `visible`([为什么?](/docs/react/faq#why-open)) | boolean | - | 4.23.0 |
+| onOpenChange | 菜单显示状态改变时调用,点击菜单按钮导致的消失不会触发。小于 4.23.0 使用 `onVisibleChange`([为什么?](/docs/react/faq#why-open)) | (open: boolean) => void | - | 4.23.0 |
`overlay` 菜单使用 [Menu](/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。
@@ -56,4 +56,4 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
| type | 按钮类型,和 [Button](/components/button/#API) 一致 | string | `default` | |
| open | 菜单是否显示 | boolean | - | 4.23.0 |
| onClick | 点击左侧按钮的回调,和 [Button](/components/button/#API) 一致 | (event) => void | - | |
-| onOpenChange | 菜单显示状态改变时调用,参数为 `visible` | (open: boolean) => void | - | 4.23.0 |
+| onOpenChange | 菜单显示状态改变时调用 | (open: boolean) => void | - | 4.23.0 |
diff --git a/components/input-number/demo/out-of-range.md b/components/input-number/demo/out-of-range.md
index 51c4189f1d..6fe7ba455e 100644
--- a/components/input-number/demo/out-of-range.md
+++ b/components/input-number/demo/out-of-range.md
@@ -18,7 +18,7 @@ import { Button, InputNumber, Space } from 'antd';
import React, { useState } from 'react';
const App: React.FC = () => {
- const [value, setValue] = useState('99');
+ const [value, setValue] = useState('99');
return (
diff --git a/components/input-number/index.tsx b/components/input-number/index.tsx
index a9ae290bad..b490d185c6 100644
--- a/components/input-number/index.tsx
+++ b/components/input-number/index.tsx
@@ -3,6 +3,7 @@ import UpOutlined from '@ant-design/icons/UpOutlined';
import classNames from 'classnames';
import type { InputNumberProps as RcInputNumberProps } from 'rc-input-number';
import RcInputNumber from 'rc-input-number';
+import type { ValueType } from 'rc-input-number/lib/utils/MiniDecimal';
import * as React from 'react';
import { useContext } from 'react';
import { ConfigContext } from '../config-provider';
@@ -15,8 +16,6 @@ import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import useStyle from './style';
-type ValueType = string | number;
-
export interface InputNumberProps
extends Omit, 'prefix' | 'size' | 'controls'> {
prefixCls?: string;
diff --git a/components/result/__tests__/index.test.tsx b/components/result/__tests__/index.test.tsx
index 8ea775d70d..44d74cc33e 100644
--- a/components/result/__tests__/index.test.tsx
+++ b/components/result/__tests__/index.test.tsx
@@ -10,7 +10,7 @@ describe('Result', () => {
rtlTest(Result);
it('🙂 successPercent should decide the progress status when it exists', () => {
- const { container: wrapper } = render(
+ const { container } = render(
{
]}
/>,
);
- expect(wrapper.querySelectorAll('.anticon-check-circle')).toHaveLength(1);
+ expect(container.querySelectorAll('.anticon-check-circle')).toHaveLength(1);
});
it('🙂 different status, different class', () => {
- const { container: wrapper, rerender } = render();
- expect(wrapper.querySelectorAll('.ant-result-warning')).toHaveLength(1);
+ const { container, rerender } = render();
+ expect(container.querySelectorAll('.ant-result-warning')).toHaveLength(1);
rerender();
- expect(wrapper.querySelectorAll('.ant-result-error')).toHaveLength(1);
+ expect(container.querySelectorAll('.ant-result-error')).toHaveLength(1);
rerender();
- expect(wrapper.querySelectorAll('.ant-result-500')).toHaveLength(1);
+ expect(container.querySelectorAll('.ant-result-500')).toHaveLength(1);
});
it('🙂 When status = 404, the icon is an image', () => {
- const { container: wrapper } = render();
- expect(wrapper.querySelectorAll('.ant-result-404 .ant-result-image')).toHaveLength(1);
+ const { container } = render();
+ expect(container.querySelectorAll('.ant-result-404 .ant-result-image')).toHaveLength(1);
});
it('🙂 When extra is undefined, the extra dom is undefined', () => {
- const { container: wrapper } = render();
- expect(wrapper.querySelectorAll('.ant-result-extra')).toHaveLength(0);
+ const { container } = render();
+ expect(container.querySelectorAll('.ant-result-extra')).toHaveLength(0);
});
it('🙂 result should support className', () => {
- const { container: wrapper } = render(
- ,
- );
- expect(wrapper.querySelectorAll('.ant-result.my-result')).toHaveLength(1);
+ const { container } = render();
+ expect(container.querySelectorAll('.ant-result.my-result')).toHaveLength(1);
});
it('should warning when pass a string as icon props', () => {
diff --git a/components/table/demo/ajax.md b/components/table/demo/ajax.md
index 6e07e56920..f12484e0a5 100644
--- a/components/table/demo/ajax.md
+++ b/components/table/demo/ajax.md
@@ -44,12 +44,11 @@ interface DataType {
};
}
-interface Params {
+interface TableParams {
pagination?: TablePaginationConfig;
- sorter?: SorterResult | SorterResult[];
- total?: number;
sortField?: string;
sortOrder?: string;
+ filters?: Record;
}
const columns: ColumnsType = [
@@ -75,7 +74,7 @@ const columns: ColumnsType = [
},
];
-const getRandomuserParams = (params: Params) => ({
+const getRandomuserParams = (params: TableParams) => ({
results: params.pagination?.pageSize,
page: params.pagination?.current,
...params,
@@ -84,41 +83,45 @@ const getRandomuserParams = (params: Params) => ({
const App: React.FC = () => {
const [data, setData] = useState();
const [loading, setLoading] = useState(false);
- const [pagination, setPagination] = useState({
- current: 1,
- pageSize: 10,
+ const [tableParams, setTableParams] = useState({
+ pagination: {
+ current: 1,
+ pageSize: 10,
+ },
});
- const fetchData = (params: Params = {}) => {
+ const fetchData = () => {
setLoading(true);
- fetch(`https://randomuser.me/api?${qs.stringify(getRandomuserParams(params))}`)
+ fetch(`https://randomuser.me/api?${qs.stringify(getRandomuserParams(tableParams))}`)
.then(res => res.json())
.then(({ results }) => {
setData(results);
setLoading(false);
- setPagination({
- ...params.pagination,
- total: 200,
- // 200 is mock data, you should read it from server
- // total: data.totalCount,
+ setTableParams({
+ ...tableParams,
+ pagination: {
+ ...tableParams.pagination,
+ total: 200,
+ // 200 is mock data, you should read it from server
+ // total: data.totalCount,
+ },
});
});
};
useEffect(() => {
- fetchData({ pagination });
- }, []);
+ fetchData();
+ }, [JSON.stringify(tableParams)]);
const handleTableChange = (
- newPagination: TablePaginationConfig,
+ pagination: TablePaginationConfig,
filters: Record,
sorter: SorterResult,
) => {
- fetchData({
- sortField: sorter.field as string,
- sortOrder: sorter.order as string,
- pagination: newPagination,
- ...filters,
+ setTableParams({
+ pagination,
+ filters,
+ ...sorter,
});
};
@@ -127,7 +130,7 @@ const App: React.FC = () => {
columns={columns}
rowKey={record => record.login.uuid}
dataSource={data}
- pagination={pagination}
+ pagination={tableParams.pagination}
loading={loading}
onChange={handleTableChange}
/>
diff --git a/components/tag/__tests__/__snapshots__/index.test.js.snap b/components/tag/__tests__/__snapshots__/index.test.tsx.snap
similarity index 100%
rename from components/tag/__tests__/__snapshots__/index.test.js.snap
rename to components/tag/__tests__/__snapshots__/index.test.tsx.snap
diff --git a/components/tag/__tests__/index.test.js b/components/tag/__tests__/index.test.js
deleted file mode 100644
index c012a6fe57..0000000000
--- a/components/tag/__tests__/index.test.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import { mount } from 'enzyme';
-import React from 'react';
-import Tag from '..';
-import mountTest from '../../../tests/shared/mountTest';
-import { render, act } from '../../../tests/utils';
-import rtlTest from '../../../tests/shared/rtlTest';
-import { resetWarned } from '../../_util/warning';
-
-describe('Tag', () => {
- mountTest(Tag);
- mountTest(Tag.CheckableTag);
- rtlTest(Tag);
- rtlTest(Tag.CheckableTag);
-
- beforeAll(() => {
- jest.useFakeTimers();
- });
-
- afterAll(() => {
- jest.useRealTimers();
- });
-
- it('should be closable', () => {
- const onClose = jest.fn();
- const wrapper = mount();
- expect(wrapper.find('.anticon-close').length).toBe(1);
- expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
- wrapper.find('.anticon-close').simulate('click');
- expect(onClose).toHaveBeenCalled();
- act(() => {
- jest.runAllTimers();
- });
- wrapper.update();
- expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(0);
- });
-
- it('should not be closed when prevent default', () => {
- const onClose = e => {
- e.preventDefault();
- };
- const wrapper = mount();
- expect(wrapper.find('.anticon-close').length).toBe(1);
- expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
- wrapper.find('.anticon-close').simulate('click');
- act(() => {
- jest.runAllTimers();
- });
- expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
- });
-
- it('should trigger onClick', () => {
- const onClick = jest.fn();
- const wrapper = mount();
- wrapper.find('.ant-tag').simulate('click');
- expect(onClick).toHaveBeenCalledWith(
- expect.objectContaining({
- type: 'click',
- preventDefault: expect.any(Function),
- }),
- );
- });
-
- it('should trigger onClick on CheckableTag', () => {
- const onClick = jest.fn();
- const wrapper = mount();
- wrapper.find('.ant-tag').simulate('click');
- expect(onClick).toHaveBeenCalledWith(
- expect.objectContaining({
- type: 'click',
- preventDefault: expect.any(Function),
- }),
- );
- });
-
- // https://github.com/ant-design/ant-design/issues/20344
- it('should not trigger onClick when click close icon', () => {
- const onClose = jest.fn();
- const onClick = jest.fn();
- const wrapper = mount();
- wrapper.find('.anticon-close').simulate('click');
- expect(onClose).toHaveBeenCalled();
- expect(onClick).not.toHaveBeenCalled();
- });
-
- it('deprecated warning', () => {
- resetWarned();
- const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
-
- const { container } = render();
- expect(errSpy).toHaveBeenCalledWith(
- 'Warning: [antd: Tag] `visible` is deprecated, please use `visible && ` instead.',
- );
- expect(container.querySelector('.ant-tag-hidden')).toBeTruthy();
-
- errSpy.mockRestore();
- });
-
- describe('CheckableTag', () => {
- it('support onChange', () => {
- const onChange = jest.fn();
- const wrapper = mount();
- wrapper.find('.ant-tag').simulate('click');
- expect(onChange).toHaveBeenCalledWith(true);
- });
- });
-});
diff --git a/components/tag/__tests__/index.test.tsx b/components/tag/__tests__/index.test.tsx
new file mode 100644
index 0000000000..f40b6bad08
--- /dev/null
+++ b/components/tag/__tests__/index.test.tsx
@@ -0,0 +1,151 @@
+import React from 'react';
+import { Simulate } from 'react-dom/test-utils';
+
+import Tag from '..';
+import { resetWarned } from '../../_util/warning';
+
+import mountTest from '../../../tests/shared/mountTest';
+import rtlTest from '../../../tests/shared/rtlTest';
+import { act, render, fireEvent } from '../../../tests/utils';
+
+describe('Tag', () => {
+ mountTest(Tag);
+ mountTest(Tag.CheckableTag);
+ rtlTest(Tag);
+ rtlTest(Tag.CheckableTag);
+
+ beforeAll(() => {
+ jest.useFakeTimers();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ it('should be closable', () => {
+ const onClose = jest.fn();
+ const { container } = render();
+ expect(container.querySelectorAll('.anticon-close').length).toBe(1);
+ expect(container.querySelectorAll('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
+ fireEvent.click(container.querySelectorAll('.anticon-close')[0]);
+ expect(onClose).toHaveBeenCalled();
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(container.querySelectorAll('.ant-tag:not(.ant-tag-hidden)').length).toBe(0);
+ });
+
+ it('should not be closed when prevent default', () => {
+ const onClose = (e: React.MouseEvent) => {
+ e.preventDefault();
+ };
+ const { container } = render();
+ expect(container.querySelectorAll('.anticon-close').length).toBe(1);
+ expect(container.querySelectorAll('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
+ fireEvent.click(container.querySelectorAll('.anticon-close')[0]);
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(container.querySelectorAll('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
+ });
+
+ it('should trigger onClick', () => {
+ const onClick = jest.fn();
+ const { container } = render();
+ const target = container.querySelectorAll('.ant-tag')[0];
+ Simulate.click(target);
+ expect(onClick).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'click',
+ target,
+ preventDefault: expect.any(Function),
+ nativeEvent: {
+ type: 'click',
+ target,
+ },
+ }),
+ );
+ });
+
+ it('should trigger onClick on CheckableTag', () => {
+ const onClick = jest.fn();
+ const { container } = render();
+ const target = container.querySelectorAll('.ant-tag')[0];
+ Simulate.click(target);
+ expect(onClick).toHaveBeenCalledWith(
+ expect.objectContaining({
+ type: 'click',
+ target,
+ preventDefault: expect.any(Function),
+ nativeEvent: {
+ type: 'click',
+ target,
+ },
+ }),
+ );
+ });
+
+ // https://github.com/ant-design/ant-design/issues/20344
+ it('should not trigger onClick when click close icon', () => {
+ const onClose = jest.fn();
+ const onClick = jest.fn();
+ const { container } = render();
+ fireEvent.click(container.querySelectorAll('.anticon-close')[0]);
+ expect(onClose).toHaveBeenCalled();
+ expect(onClick).not.toHaveBeenCalled();
+ });
+
+ it('deprecated warning', () => {
+ resetWarned();
+ const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
+
+ const { container } = render();
+ expect(errSpy).toHaveBeenCalledWith(
+ 'Warning: [antd: Tag] `visible` is deprecated, please use `visible && ` instead.',
+ );
+ expect(container.querySelector('.ant-tag-hidden')).toBeTruthy();
+
+ errSpy.mockRestore();
+ });
+
+ describe('visibility', () => {
+ it('can be controlled by visible with visible as initial value', () => {
+ const { asFragment, rerender } = render();
+ expect(asFragment().firstChild).toMatchSnapshot();
+ rerender();
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(asFragment().firstChild).toMatchSnapshot();
+ rerender();
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(asFragment().firstChild).toMatchSnapshot();
+ });
+
+ it('can be controlled by visible with hidden as initial value', () => {
+ const { asFragment, rerender } = render();
+ expect(asFragment().firstChild).toMatchSnapshot();
+ rerender();
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(asFragment().firstChild).toMatchSnapshot();
+ rerender();
+ act(() => {
+ jest.runAllTimers();
+ });
+ expect(asFragment().firstChild).toMatchSnapshot();
+ });
+ });
+
+ describe('CheckableTag', () => {
+ it('support onChange', () => {
+ const onChange = jest.fn();
+ const { container } = render();
+ fireEvent.click(container.querySelectorAll('.ant-tag')[0]);
+ expect(onChange).toHaveBeenCalledWith(true);
+ });
+ });
+});
diff --git a/components/tooltip/index.en-US.md b/components/tooltip/index.en-US.md
index 4bf7f598fe..b7d32e9947 100644
--- a/components/tooltip/index.en-US.md
+++ b/components/tooltip/index.en-US.md
@@ -38,7 +38,7 @@ The following APIs are shared by Tooltip, Popconfirm, Popover.
| overlayInnerStyle | Style of the tooltip inner content | object | - | |
| placement | The position of the tooltip relative to the target, which can be one of `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` | |
| trigger | Tooltip trigger mode. Could be multiple by passing an array | `hover` \| `focus` \| `click` \| `contextMenu` \| Array<string> | `hover` | |
-| open | Whether the floating tooltip card is open or not | 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 | - | |
| onOpenChange | Callback executed when visibility of the tooltip card is changed | (open) => void | - | 4.23.0 |
diff --git a/components/tooltip/index.zh-CN.md b/components/tooltip/index.zh-CN.md
index 8186d842e0..b252015b8b 100644
--- a/components/tooltip/index.zh-CN.md
+++ b/components/tooltip/index.zh-CN.md
@@ -40,7 +40,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
| overlayInnerStyle | 卡片内容区域的样式对象 | object | - | |
| placement | 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` | |
| trigger | 触发行为,可选 `hover` \| `focus` \| `click` \| `contextMenu`,可使用数组设置多个触发行为 | string \| string\[] | `hover` | |
-| 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 | - | |
| onOpenChange | 显示隐藏的回调 | (open) => void | - | 4.23.0 |
diff --git a/docs/react/faq.en-US.md b/docs/react/faq.en-US.md
index 9050b74bcf..f0b823cb21 100644
--- a/docs/react/faq.en-US.md
+++ b/docs/react/faq.en-US.md
@@ -108,7 +108,7 @@ import { Table } from 'antd';
type Props any> = Parameters[0];
-type TableProps = Props>;
+type TableProps = Props>;
type DataSource = TableProps['dataSource'];
```
@@ -169,6 +169,12 @@ ConfigProvider.config({
You should only access the API by official doc with ref. Directly access internal `props` or `state` is not recommended which will make your code strong coupling with current version. Any refactor will break your code like refactor with [Hooks](https://reactjs.org/docs/hooks-intro.html) version, delete or rename internal `props` or `state`, adjust internal node constructor, etc.
+
+
+## Why we need align pop component with `open` prop?
+
+For historical reasons, the display names of the pop components are not uniform, and both `open` and `visible` are used. This makes the memory cost that non-tsx users encounter when developing. It also leads to ambiguity about what name to choose when adding a feature. So we want to unify the attribute name, you can still use the original `visible` and it will still be backward compatible, but we will remove this attribute from the documentation as of v5.
+
## How to spell Ant Design correctly?
- ✅ **Ant Design**: Capitalized with space, for the design language.
diff --git a/docs/react/faq.zh-CN.md b/docs/react/faq.zh-CN.md
index 0f8d0c75ac..6ef3c0c226 100644
--- a/docs/react/faq.zh-CN.md
+++ b/docs/react/faq.zh-CN.md
@@ -122,7 +122,7 @@ import { Table } from 'antd';
type Props any> = Parameters[0];
-type TableProps = Props>;
+type TableProps = Props>;
type DataSource = TableProps['dataSource'];
```
@@ -189,6 +189,12 @@ ConfigProvider.config({
你通过 ref 获得引用时只应该使用文档提供的方法。直接读取组件内部的 `props` 和 `state` 不是一个好的设计,这会使你的代码与组件版本强耦合。任何重构都可能会使你的代码无法工作,其中重构包括且不仅限于改造成 [Hooks](https://reactjs.org/docs/hooks-intro.html) 版本、移除 / 更名内部 `props` 与 `state`、调整内部 React 节点结构等等。
+
+
+## 弹层类组件为什么要统一至 `open` 属性?
+
+因为历史原因,弹层类组件展示命名并不统一,出现了 `open` 与 `visible` 都在使用的情况。这使得非 tsx 用户在开发时遭遇的记忆成本。同样导致新增 feature 时选择何种命名的模棱两可。因而我们希望统一该属性命名,你仍然可以使用原本的 `visible` 它仍然会向下兼容,但是从 v5 起我们将从文档中移除该属性。
+
## 如何正确的拼写 Ant Design?
- ✅ **Ant Design**:用空格分隔的首字母大写单词,指代设计语言。
diff --git a/site/theme/template/Content/MainContent.jsx b/site/theme/template/Content/MainContent.jsx
index 28855ea47c..290592aea9 100644
--- a/site/theme/template/Content/MainContent.jsx
+++ b/site/theme/template/Content/MainContent.jsx
@@ -195,6 +195,14 @@ class MainContent extends Component {
handleLoad = () => {
if (window.location.hash) {
updateActiveToc(window.location.hash.replace(/^#/, ''));
+
+ // 有时候不滚动,强制触发一次滚动逻辑
+ setTimeout(() => {
+ const target = document.querySelector(window.location.hash);
+ if (target) {
+ target.scrollIntoView();
+ }
+ }, 100);
}
this.bindScroller();
};