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(); };