diff --git a/components/alert/index.tsx b/components/alert/index.tsx index e8496276a1..8a021fdaa4 100644 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -9,8 +9,8 @@ import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled'; import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined'; import classNames from 'classnames'; import CSSMotion from 'rc-motion'; +import type { ReactElement } from 'react'; import * as React from 'react'; - import { ConfigContext } from '../config-provider'; import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import { replaceElement } from '../_util/reactNode'; @@ -65,6 +65,43 @@ const iconMapOutlined = { warning: ExclamationCircleOutlined, }; +interface IconNodeProps { + type: AlertProps['type']; + icon: AlertProps['icon']; + prefixCls: AlertProps['prefixCls']; + description: AlertProps['description']; +} + +const IconNode: React.FC = props => { + const { description, icon, prefixCls, type } = props; + const iconType = (description ? iconMapOutlined : iconMapFilled)[type!] || null; + if (icon) { + return replaceElement(icon, {icon}, () => ({ + className: classNames(`${prefixCls}-icon`, { + [(icon as ReactElement).props.className]: (icon as ReactElement).props.className, + }), + })) as ReactElement; + } + return React.createElement(iconType, { className: `${prefixCls}-icon` }); +}; + +interface CloseIconProps { + isClosable: boolean; + prefixCls: AlertProps['prefixCls']; + closeText: AlertProps['closeText']; + closeIcon: AlertProps['closeIcon']; + handleClose: AlertProps['onClose']; +} + +const CloseIcon: React.FC = props => { + const { isClosable, closeText, prefixCls, closeIcon, handleClose } = props; + return isClosable ? ( + + ) : null; +}; + interface AlertInterface extends React.FC { ErrorBoundary: typeof ErrorBoundary; } @@ -112,32 +149,6 @@ const Alert: AlertInterface = ({ const isClosable = closeText ? true : closable; const type = getType(); - const renderIconNode = () => { - const { icon } = props; - // use outline icon in alert with description - const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null; - if (icon) { - return replaceElement(icon, {icon}, () => ({ - className: classNames(`${prefixCls}-icon`, { - [(icon as any).props.className]: (icon as any).props.className, - }), - })); - } - return React.createElement(iconType, { className: `${prefixCls}-icon` }); - }; - - const renderCloseIcon = () => - isClosable ? ( - - ) : null; - // banner 模式默认有 Icon const isShowIcon = banner && showIcon === undefined ? true : showIcon; @@ -179,13 +190,26 @@ const Alert: AlertInterface = ({ role="alert" {...dataOrAriaProps} > - {isShowIcon ? renderIconNode() : null} + {isShowIcon ? ( + + ) : null}
{message ?
{message}
: null} {description ?
{description}
: null}
{action ?
{action}
: null} - {renderCloseIcon()} + )} , diff --git a/components/menu/__tests__/index.test.js b/components/menu/__tests__/index.test.js index 09f2c6e5b5..c8d9dea851 100644 --- a/components/menu/__tests__/index.test.js +++ b/components/menu/__tests__/index.test.js @@ -816,6 +816,7 @@ describe('Menu', () => { ); expect(onOpen).not.toHaveBeenCalled(); expect(onClose).not.toHaveBeenCalled(); + errorSpy.mockRestore(); }); // https://github.com/ant-design/ant-design/issues/18825 @@ -1002,4 +1003,13 @@ describe('Menu', () => { expect(wrapper.render()).toMatchSnapshot(); }); + + it('should not warning deprecated message when items={undefined}', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined); + mount(); + expect(errorSpy).not.toHaveBeenCalledWith( + expect.stringContaining('`children` will be removed in next major version'), + ); + errorSpy.mockRestore(); + }); }); diff --git a/components/menu/index.tsx b/components/menu/index.tsx index 1912ed90b4..dc87536a69 100644 --- a/components/menu/index.tsx +++ b/components/menu/index.tsx @@ -91,7 +91,7 @@ const InternalMenu = forwardRef((props, ref) => { ); warning( - !!items && !children, + 'items' in props && !children, 'Menu', '`children` will be removed in next major version. Please use `items` instead.', ); diff --git a/components/modal/demo/hooks.md b/components/modal/demo/hooks.md index ed054ccaad..64f20acfe2 100644 --- a/components/modal/demo/hooks.md +++ b/components/modal/demo/hooks.md @@ -66,7 +66,7 @@ const App: React.FC = () => { Error - {/* `contextHolder` should always under the context you want to access */} + {/* `contextHolder` should always be placed under the context you want to access */} {contextHolder} {/* Can not access this context since `contextHolder` is not in it */} diff --git a/components/pagination/__tests__/index.test.js b/components/pagination/__tests__/index.test.js index 7aa32e9e1c..ba693a2974 100644 --- a/components/pagination/__tests__/index.test.js +++ b/components/pagination/__tests__/index.test.js @@ -1,8 +1,8 @@ -import { mount } from 'enzyme'; import React from 'react'; import Pagination from '..'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; +import { fireEvent, render } from '../../../tests/utils'; import ConfigProvider from '../../config-provider'; import Select from '../../select'; @@ -20,13 +20,15 @@ describe('Pagination', () => { } return originalElement; }; - const wrapper = mount(); - expect(wrapper.find('button').at(0).props().disabled).toBe(true); + const { container } = render( + , + ); + expect(container.querySelector('button').disabled).toBe(true); }); it('should autometically be small when size is not specified', async () => { - const wrapper = mount(); - expect(wrapper.find('ul').at(0).hasClass('ant-pagination-mini')).toBe(true); + const { container } = render(); + expect(container.querySelector('ul').className.includes('ant-pagination-mini')).toBe(true); }); // https://github.com/ant-design/ant-design/issues/24913 @@ -34,7 +36,7 @@ describe('Pagination', () => { it('should onChange called when pageSize change', () => { const onChange = jest.fn(); const onShowSizeChange = jest.fn(); - const wrapper = mount( + const { container } = render( { onShowSizeChange={onShowSizeChange} />, ); - wrapper.find('.ant-select-selector').simulate('mousedown'); - expect(wrapper.find('.ant-select-item-option').length).toBe(4); - wrapper.find('.ant-select-item-option').at(1).simulate('click'); + + fireEvent.mouseDown(container.querySelector('.ant-select-selector')); + + expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4); + fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]); expect(onChange).toHaveBeenCalledWith(1, 20); }); @@ -55,30 +59,30 @@ describe('Pagination', () => { CustomSelect.Option = Select.Option; - const wrapper = mount( + const { container } = render( , ); - expect(wrapper.find('.custom-select').length).toBeTruthy(); + expect(container.querySelectorAll('.custom-select').length).toBeTruthy(); }); describe('ConfigProvider', () => { it('should be rendered correctly in RTL', () => { - const wrapper = mount( + const { asFragment } = render( , ); - expect(wrapper.render()).toMatchSnapshot(); + expect(asFragment().firstChild).toMatchSnapshot(); }); it('should be rendered correctly when componentSize is large', () => { - const wrapper = mount( + const { container, asFragment } = render( , ); - expect(wrapper.render()).toMatchSnapshot(); - expect(wrapper.find('.ant-select-lg').length).toBe(0); + expect(asFragment().firstChild).toMatchSnapshot(); + expect(container.querySelectorAll('.ant-select-lg').length).toBe(0); }); }); }); diff --git a/components/table/__tests__/Table.rowSelection.test.js b/components/table/__tests__/Table.rowSelection.test.js index 7877ffd26d..8de09d90cd 100644 --- a/components/table/__tests__/Table.rowSelection.test.js +++ b/components/table/__tests__/Table.rowSelection.test.js @@ -292,6 +292,79 @@ describe('Table.rowSelection', () => { ]); }); + it('reset last select key after performing select and bulk operations', async () => { + jest.useFakeTimers(); + const onChange = jest.fn(); + + const { container, baseElement } = render( + createTable({ + checkbox: true, + rowSelection: { + selections: [Table.SELECTION_NONE], + onChange: keys => onChange(keys), + }, + }), + ); + + const last = () => { + const elements = container.querySelectorAll('td input'); + return elements[elements.length - 1]; + }; + + const first = () => { + const elements = container.querySelectorAll('td input'); + return elements[0]; + }; + + const allElement = () => container.querySelector('th input'); + + // Multiple select normal + fireEvent.click(last()); + expect(onChange).toHaveBeenLastCalledWith([3]); + fireEvent.click(first(), { + shiftKey: true, + }); + expect(onChange).toHaveBeenLastCalledWith([3, 0, 1, 2]); + fireEvent.click(allElement()); + console.log(baseElement.innerHTML); + expect(onChange).toHaveBeenLastCalledWith([]); + + // Reset last select key when select all + fireEvent.click(last()); + expect(onChange).toHaveBeenLastCalledWith([3]); + fireEvent.click(allElement()); + fireEvent.click(allElement()); + expect(onChange).toHaveBeenLastCalledWith([]); + fireEvent.click(first(), { + shiftKey: true, + }); + expect(onChange).toHaveBeenLastCalledWith([0]); + + // Reset last select key when deselect + fireEvent.click(last()); + expect(onChange).toHaveBeenLastCalledWith([0, 3]); + fireEvent.click(first()); + expect(onChange).toHaveBeenLastCalledWith([3]); + fireEvent.click(first(), { + shiftKey: true, + }); + expect(onChange).toHaveBeenLastCalledWith([3, 0]); + + // Reset last select key when bulk operations + fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')); + act(() => { + jest.runAllTimers(); + }); + fireEvent.click(baseElement.querySelector('li.ant-dropdown-menu-item')); + expect(onChange).toHaveBeenLastCalledWith([]); + fireEvent.click(first(), { + shiftKey: true, + }); + expect(onChange).toHaveBeenLastCalledWith([0]); + + jest.useRealTimers(); + }); + it('fires selectAll event', () => { const order = []; const handleSelectAll = jest.fn().mockImplementation(() => { diff --git a/components/table/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/table/__tests__/__snapshots__/demo-extend.test.ts.snap index a372de62e8..8eaffde55c 100644 --- a/components/table/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/table/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -15798,7 +15798,7 @@ Array [ ,
+Array [
- - - - - - - - - - - - - - - - - - + - - - - - - - - - + Name + + + + + + + + + + - - + + + + + + + + + - Screem - - + + - iOS - - + + + + + + + + + - 10.3.4.5654 - - - - - - - - - - - - - - - - - -
+
- Name - - Platform - - Version - - Upgraded - - Creator - - Date - - Action -
- - - - Screem - - iOS - - 10.3.4.5654 - - 500 - - Jack - - 2014-12-24 23:12:00 - - - Publish - -
+ Platform + + Version + + Upgraded + + Creator + + Date + + Action +
- + + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Date + + Name + + Status + + Upgrade Status + + Action +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
- 500 - - Jack - - 2014-12-24 23:12:00 - - - Publish - -
- - Screem - - iOS - - 10.3.4.5654 - - 500 - - Jack - - 2014-12-24 23:12:00 - - - Publish - -
+ +
+
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
-
    -
  • - -
  • -
  • - - 1 - -
  • -
  • - -
  • -
-
-
+ , +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + Platform + + Version + + Upgraded + + Creator + + Date + + Action +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Date + + Name + + Status + + Upgrade Status + + Action +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
+
+
+
, +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + Platform + + Version + + Upgraded + + Creator + + Date + + Action +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Date + + Name + + Status + + Upgrade Status + + Action +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + +
+ + +
+ + More + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
+
+
+
, +] `; exports[`renders ./components/table/demo/order-column.md extend context correctly 1`] = ` diff --git a/components/table/__tests__/__snapshots__/demo.test.js.snap b/components/table/__tests__/__snapshots__/demo.test.js.snap index ba7053d7c0..bac3bdd3df 100644 --- a/components/table/__tests__/__snapshots__/demo.test.js.snap +++ b/components/table/__tests__/__snapshots__/demo.test.js.snap @@ -12020,7 +12020,7 @@ Array [ ,
+Array [
- - - - - - - - - - - - - - - - - - + - - - - - - - - - + Name + + + + + + + + + + - - + + + + + + + + + - Screem - - + + - iOS - - + + + + + + + + + - 10.3.4.5654 - - - - - - - - - - - - - - - - - -
+
- Name - - Platform - - Version - - Upgraded - - Creator - - Date - - Action -
- - - - Screem - - iOS - - 10.3.4.5654 - - 500 - - Jack - - 2014-12-24 23:12:00 - - - Publish - -
+ Platform + + Version + + Upgraded + + Creator + + Date + + Action +
- + + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Date + + Name + + Status + + Upgrade Status + + Action +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+
+
+
+
+
+
+
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
- 500 - - Jack - - 2014-12-24 23:12:00 - - - Publish - -
- - Screem - - iOS - - 10.3.4.5654 - - 500 - - Jack - - 2014-12-24 23:12:00 - - - Publish - -
+ +
+
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
-
    -
  • - -
  • -
  • - - 1 - -
  • -
  • - -
  • -
-
-
+ , +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + Platform + + Version + + Upgraded + + Creator + + Date + + Action +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Date + + Name + + Status + + Upgrade Status + + Action +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+
+
+
+
+
+
+
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
+
+
+
, +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + Platform + + Version + + Upgraded + + Creator + + Date + + Action +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Date + + Name + + Status + + Upgrade Status + + Action +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+ 2014-12-24 23:12:00 + + This is production name + + + + + + + Finished + + + Upgraded: 56 + + +
+
+
+
+
+
+
+
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+ + Screem + + iOS + + 10.3.4.5654 + + 500 + + Jack + + 2014-12-24 23:12:00 + + + Publish + +
+
+
+
+
    +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
+
+
+
, +] `; exports[`renders ./components/table/demo/order-column.md correctly 1`] = ` diff --git a/components/table/demo/nest-table-border-debug.md b/components/table/demo/nest-table-border-debug.md index 329f05339f..8c2298803f 100644 --- a/components/table/demo/nest-table-border-debug.md +++ b/components/table/demo/nest-table-border-debug.md @@ -16,8 +16,8 @@ To see if bordered style applied to other tables. ```tsx import { DownOutlined } from '@ant-design/icons'; +import type { TableColumnsType } from 'antd'; import { Badge, Dropdown, Form, Menu, Space, Switch, Table } from 'antd'; -import type { ColumnsType } from 'antd/lib/table'; import React, { useState } from 'react'; interface DataType { @@ -93,7 +93,7 @@ const App: React.FC = () => { return ; }; - const columns: ColumnsType = [ + const columns: TableColumnsType = [ { title: 'Name', dataIndex: 'name', key: 'name' }, { title: 'Platform', dataIndex: 'platform', key: 'platform' }, { title: 'Version', dataIndex: 'version', key: 'version' }, @@ -135,7 +135,6 @@ const App: React.FC = () => {
'cool'} footer={() => 'cool'} - className="components-table-demo-nested" columns={columns} expandable={{ expandedRowRender: createExpandedRowRender(childTableBordered) }} dataSource={data} diff --git a/components/table/demo/nested-table.md b/components/table/demo/nested-table.md index 529032de3b..718eb2508e 100644 --- a/components/table/demo/nested-table.md +++ b/components/table/demo/nested-table.md @@ -15,8 +15,8 @@ Showing more detailed info of every row. ```tsx import { DownOutlined } from '@ant-design/icons'; +import type { TableColumnsType } from 'antd'; import { Badge, Dropdown, Menu, Space, Table } from 'antd'; -import type { ColumnsType } from 'antd/lib/table'; import React from 'react'; interface DataType { @@ -47,7 +47,7 @@ const menu = ( const App: React.FC = () => { const expandedRowRender = () => { - const columns: ColumnsType = [ + const columns: TableColumnsType = [ { title: 'Date', dataIndex: 'date', key: 'date' }, { title: 'Name', dataIndex: 'name', key: 'name' }, { @@ -82,7 +82,7 @@ const App: React.FC = () => { const data = []; for (let i = 0; i < 3; ++i) { data.push({ - key: i, + key: i.toString(), date: '2014-12-24 23:12:00', name: 'This is production name', upgradeNum: 'Upgraded: 56', @@ -91,7 +91,7 @@ const App: React.FC = () => { return
; }; - const columns: ColumnsType = [ + const columns: TableColumnsType = [ { title: 'Name', dataIndex: 'name', key: 'name' }, { title: 'Platform', dataIndex: 'platform', key: 'platform' }, { title: 'Version', dataIndex: 'version', key: 'version' }, @@ -104,7 +104,7 @@ const App: React.FC = () => { const data: DataType[] = []; for (let i = 0; i < 3; ++i) { data.push({ - key: i, + key: i.toString(), name: 'Screem', platform: 'iOS', version: '10.3.4.5654', @@ -115,12 +115,25 @@ const App: React.FC = () => { } return ( -
+ <> +
+
+
+ ); }; diff --git a/components/table/hooks/useSelection.tsx b/components/table/hooks/useSelection.tsx index bbba5baff7..7bf4e7480b 100644 --- a/components/table/hooks/useSelection.tsx +++ b/components/table/hooks/useSelection.tsx @@ -271,75 +271,83 @@ export default function useSelection( const selectionList: INTERNAL_SELECTION_ITEM[] = selections === true ? [SELECTION_ALL, SELECTION_INVERT, SELECTION_NONE] : selections; - return selectionList.map((selection: INTERNAL_SELECTION_ITEM) => { - if (selection === SELECTION_ALL) { - return { - key: 'all', - text: tableLocale.selectionAll, - onSelect() { - setSelectedKeys( - data - .map((record, index) => getRowKey(record, index)) - .filter(key => { - const checkProps = checkboxPropsMap.get(key); - return !checkProps?.disabled || derivedSelectedKeySet.has(key); - }), - 'all', - ); - }, - }; - } - if (selection === SELECTION_INVERT) { - return { - key: 'invert', - text: tableLocale.selectInvert, - onSelect() { - const keySet = new Set(derivedSelectedKeySet); - pageData.forEach((record, index) => { - const key = getRowKey(record, index); - const checkProps = checkboxPropsMap.get(key); - - if (!checkProps?.disabled) { - if (keySet.has(key)) { - keySet.delete(key); - } else { - keySet.add(key); - } - } - }); - - const keys = Array.from(keySet); - if (onSelectInvert) { - warning( - false, - 'Table', - '`onSelectInvert` will be removed in future. Please use `onChange` instead.', + return selectionList + .map((selection: INTERNAL_SELECTION_ITEM) => { + if (selection === SELECTION_ALL) { + return { + key: 'all', + text: tableLocale.selectionAll, + onSelect() { + setSelectedKeys( + data + .map((record, index) => getRowKey(record, index)) + .filter(key => { + const checkProps = checkboxPropsMap.get(key); + return !checkProps?.disabled || derivedSelectedKeySet.has(key); + }), + 'all', ); - onSelectInvert(keys); - } - - setSelectedKeys(keys, 'invert'); - }, - }; - } - if (selection === SELECTION_NONE) { - return { - key: 'none', - text: tableLocale.selectNone, - onSelect() { - onSelectNone?.(); - setSelectedKeys( - Array.from(derivedSelectedKeySet).filter(key => { + }, + }; + } + if (selection === SELECTION_INVERT) { + return { + key: 'invert', + text: tableLocale.selectInvert, + onSelect() { + const keySet = new Set(derivedSelectedKeySet); + pageData.forEach((record, index) => { + const key = getRowKey(record, index); const checkProps = checkboxPropsMap.get(key); - return checkProps?.disabled; - }), - 'none', - ); - }, - }; - } - return selection as SelectionItem; - }); + + if (!checkProps?.disabled) { + if (keySet.has(key)) { + keySet.delete(key); + } else { + keySet.add(key); + } + } + }); + + const keys = Array.from(keySet); + if (onSelectInvert) { + warning( + false, + 'Table', + '`onSelectInvert` will be removed in future. Please use `onChange` instead.', + ); + onSelectInvert(keys); + } + + setSelectedKeys(keys, 'invert'); + }, + }; + } + if (selection === SELECTION_NONE) { + return { + key: 'none', + text: tableLocale.selectNone, + onSelect() { + onSelectNone?.(); + setSelectedKeys( + Array.from(derivedSelectedKeySet).filter(key => { + const checkProps = checkboxPropsMap.get(key); + return checkProps?.disabled; + }), + 'none', + ); + }, + }; + } + return selection as SelectionItem; + }) + .map(selection => ({ + ...selection, + onSelect: (...rest) => { + selection.onSelect?.(...rest); + setLastSelectedKey(null); + }, + })); }, [selections, derivedSelectedKeySet, pageData, getRowKey, onSelectInvert, setSelectedKeys]); // ======================= Columns ======================== @@ -393,6 +401,7 @@ export default function useSelection( ); setSelectedKeys(keys, 'all'); + setLastSelectedKey(null); }; // ===================== Render ===================== @@ -605,7 +614,11 @@ export default function useSelection( } } - setLastSelectedKey(key); + if (checked) { + setLastSelectedKey(null); + } else { + setLastSelectedKey(key); + } }} /> ), diff --git a/components/table/style/expand.tsx b/components/table/style/expand.tsx index 1f5ba06b2f..30fcdf0eca 100644 --- a/components/table/style/expand.tsx +++ b/components/table/style/expand.tsx @@ -10,12 +10,12 @@ const genExpandStyle: GenerateStyle = token => { controlInteractiveSize: checkboxSize, motionDurationSlow, controlLineWidth, - padding, paddingXXS, paddingXS, controlLineType, tableBorderColor, tableExpandIconBg, + tableExpandColumnWidth, radiusBase, tablePaddingVertical, tablePaddingHorizontal, @@ -30,7 +30,7 @@ const genExpandStyle: GenerateStyle = token => { return { [`${componentCls}-wrapper`]: { [`${componentCls}-expand-icon-col`]: { - width: checkboxSize + padding * 2, + width: tableExpandColumnWidth, }, [`${componentCls}-row-expand-icon-cell`]: { diff --git a/components/table/style/index.less b/components/table/style/index.less index 8142f15513..d18f5e4802 100644 --- a/components/table/style/index.less +++ b/components/table/style/index.less @@ -12,6 +12,7 @@ // @table-sticky-zindex: calc(@zindex-table-fixed + 1); // @table-sticky-scroll-bar-active-bg: fade(@table-sticky-scroll-bar-bg, 80%); // @table-filter-dropdown-max-height: 264px; +// @table-expand-column-width: 48px; // .@{table-prefix-cls}-wrapper { // clear: both; @@ -130,8 +131,8 @@ // > .@{table-prefix-cls}-wrapper:only-child, // > .@{table-prefix-cls}-expanded-row-fixed > .@{table-prefix-cls}-wrapper:only-child { // .@{table-prefix-cls} { -// margin: -@table-padding-vertical -@table-padding-horizontal -@table-padding-vertical (@table-padding-horizontal + -// ceil(@font-size-sm * 1.4)); +// margin: -@table-padding-vertical -@table-padding-horizontal -@table-padding-vertical (@table-expand-column-width - +// @table-padding-horizontal); // &-tbody > tr:last-child > td { // border-bottom: 0; @@ -476,7 +477,7 @@ // // ========================== Expandable ========================== // &-expand-icon-col { -// width: 48px; +// width: @table-expand-column-width; // } // &-row-expand-icon-cell { diff --git a/components/table/style/index.tsx b/components/table/style/index.tsx index 020f9fdd36..43a621e55b 100644 --- a/components/table/style/index.tsx +++ b/components/table/style/index.tsx @@ -57,6 +57,7 @@ export interface TableToken extends FullToken<'Table'> { tableFontSizeSmall: number; tableSelectionColumnWidth: number; tableExpandIconBg: string; + tableExpandColumnWidth: number; tableExpandedRowBg: string; tableFilterDropdownWidth: number; tableFilterDropdownSearchWidth: number; @@ -181,7 +182,9 @@ const genTableStyle: GenerateStyle = token => { `]: { [componentCls]: { marginBlock: `-${tablePaddingVertical}px`, - marginInline: `${tablePaddingHorizontal * 2}px -${tablePaddingHorizontal}px`, + marginInline: `${ + token.tableExpandColumnWidth - tablePaddingHorizontal + }px -${tablePaddingHorizontal}px`, [`${componentCls}-tbody > tr:last-child > td`]: { borderBottom: 0, '&:first-child, &:last-child': { @@ -245,6 +248,7 @@ export default genComponentStyleHook( radiusBase, headerHoverBgColor, headerSortActiveBgColor, + controlInteractiveSize: checkboxSize, } = token; const baseColorAction = new TinyColor(colorAction); @@ -305,6 +309,8 @@ export default genComponentStyleHook( tableScrollThumbBg: colorTextPlaceholder, tableScrollThumbBgHover: colorTextHeading, tableScrollBg: colorSplit, + + tableExpandColumnWidth: checkboxSize + 2 * token.padding, }); return [ diff --git a/components/table/style/size.less b/components/table/style/size.less index dd920eb523..fd7f9e4d51 100644 --- a/components/table/style/size.less +++ b/components/table/style/size.less @@ -27,8 +27,8 @@ // // ========================= Nest Table =========================== // .@{table-prefix-cls}-wrapper:only-child { // .@{table-prefix-cls} { -// margin: -@padding-vertical -@padding-horizontal -@padding-vertical (@padding-horizontal + -// ceil((@font-size-sm * 1.4))); +// margin: -@padding-vertical -@padding-horizontal -@padding-vertical (@table-expand-column-width - +// @padding-horizontal); // } // } // } diff --git a/components/table/style/size.tsx b/components/table/style/size.tsx index 41ee1b0b10..49206dbedc 100644 --- a/components/table/style/size.tsx +++ b/components/table/style/size.tsx @@ -35,7 +35,9 @@ const genSizeStyle: GenerateStyle = token => { // ========================= Nest Table =========================== [`${componentCls}-wrapper:only-child ${componentCls}`]: { marginBlock: `-${paddingVertical}px`, - marginInline: `${paddingHorizontal * 2}px -${paddingHorizontal}px`, + marginInline: `${ + token.tableExpandColumnWidth - paddingHorizontal + }px -${paddingHorizontal}px`, }, }, diff --git a/components/tree-select/index.tsx b/components/tree-select/index.tsx index 0ed7d94c04..f698a16f62 100644 --- a/components/tree-select/index.tsx +++ b/components/tree-select/index.tsx @@ -47,6 +47,7 @@ export interface TreeSelectProps< | 'getInputElement' | 'backfill' | 'treeLine' + | 'switcherIcon' > { suffixIcon?: React.ReactNode; size?: SizeType; @@ -55,7 +56,7 @@ export interface TreeSelectProps< bordered?: boolean; treeLine?: TreeProps['showLine']; status?: InputStatus; - switcherIcon?: SwitcherIcon; + switcherIcon?: SwitcherIcon | RcTreeSelectProps['switcherIcon']; } const InternalTreeSelect = ( diff --git a/components/tree/Tree.tsx b/components/tree/Tree.tsx index 9403b409c5..f4b1980c50 100644 --- a/components/tree/Tree.tsx +++ b/components/tree/Tree.tsx @@ -104,7 +104,10 @@ interface DraggableConfig { } export interface TreeProps - extends Omit, 'prefixCls' | 'showLine' | 'direction' | 'draggable'> { + extends Omit< + RcTreeProps, + 'prefixCls' | 'showLine' | 'direction' | 'draggable' | 'icon' | 'switcherIcon' + > { showLine?: boolean | { showLeafIcon: boolean }; className?: string; /** 是否支持多选 */ @@ -141,8 +144,11 @@ export interface TreeProps draggable?: DraggableFn | boolean | DraggableConfig; style?: React.CSSProperties; showIcon?: boolean; - icon?: ((nodeProps: AntdTreeNodeAttribute) => React.ReactNode) | React.ReactNode; - switcherIcon?: SwitcherIcon; + icon?: + | ((nodeProps: AntdTreeNodeAttribute) => React.ReactNode) + | React.ReactNode + | RcTreeProps['icon']; + switcherIcon?: SwitcherIcon | RcTreeProps['switcherIcon']; prefixCls?: string; children?: React.ReactNode; blockNode?: boolean; diff --git a/components/upload/Upload.tsx b/components/upload/Upload.tsx index 09508ca1db..0fa9d612b2 100644 --- a/components/upload/Upload.tsx +++ b/components/upload/Upload.tsx @@ -411,6 +411,8 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr ); + const uploadButton = renderUploadButton(children ? undefined : { display: 'none' }); + if (listType === 'picture-card') { return wrapSSR( = (pr hashId, )} > - {renderUploadList(renderUploadButton(), !!children)} + {renderUploadList(uploadButton, !!children)} , ); } return wrapSSR( - {renderUploadButton(children ? undefined : { display: 'none' })} + {uploadButton} {renderUploadList()} , ); diff --git a/components/upload/__tests__/upload.test.js b/components/upload/__tests__/upload.test.js index 59577d3507..e866efcd0a 100644 --- a/components/upload/__tests__/upload.test.js +++ b/components/upload/__tests__/upload.test.js @@ -937,8 +937,11 @@ describe('Upload', () => { ); rerender(); - expect(container.querySelector('.ant-upload-select')).not.toHaveStyle({ - display: 'none', + expect(container.querySelector('.ant-upload-select-picture-card')).toHaveClass( + 'ant-upload-animate-inline-leave-start', + ); + expect(container.querySelector('.ant-upload-select-picture-card')).toHaveStyle({ + pointerEvents: 'none', }); // Motion leave status change: start > active @@ -946,11 +949,10 @@ describe('Upload', () => { jest.runAllTimers(); }); - fireEvent.animationEnd(container.querySelector('.ant-upload-select')); - - expect(container.querySelector('.ant-upload-select')).toHaveStyle({ - display: 'none', - }); + fireEvent.animationEnd(container.querySelector('.ant-upload-select-picture-card')); + expect(container.querySelector('.ant-upload-select-picture-card')).not.toHaveClass( + 'ant-upload-animate-inline-leave-start', + ); jest.useRealTimers(); }); diff --git a/components/upload/__tests__/uploadlist.test.js b/components/upload/__tests__/uploadlist.test.js index c424651b52..b9475ba534 100644 --- a/components/upload/__tests__/uploadlist.test.js +++ b/components/upload/__tests__/uploadlist.test.js @@ -1499,4 +1499,43 @@ describe('Upload List', () => { }); unmount(); }); + + describe('should not display upload file-select button when listType is picture-card and children is empty', () => { + it('when showUploadList is true', () => { + const list = [ + { + uid: '0', + name: 'xxx.png', + status: 'done', + url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', + thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png', + }, + ]; + const { container: wrapper, unmount } = render( + , + ); + expect(wrapper.querySelectorAll('.ant-upload-select').length).toBe(1); + expect(wrapper.querySelectorAll('.ant-upload-select')[0].style.display).toBe('none'); + unmount(); + }); + + // https://github.com/ant-design/ant-design/issues/36183 + it('when showUploadList is false', () => { + const list = [ + { + uid: '0', + name: 'xxx.png', + status: 'done', + url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', + thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png', + }, + ]; + const { container: wrapper, unmount } = render( + , + ); + expect(wrapper.querySelectorAll('.ant-upload-select').length).toBe(1); + expect(wrapper.querySelectorAll('.ant-upload-select')[0].style.display).toBe('none'); + unmount(); + }); + }); }); diff --git a/docs/react/faq.en-US.md b/docs/react/faq.en-US.md index 43ca552d0e..a7c75fd829 100644 --- a/docs/react/faq.en-US.md +++ b/docs/react/faq.en-US.md @@ -11,6 +11,16 @@ Here are the frequently asked questions about Ant Design and antd that you shoul There is currently no plan to add support for Sass/Stylus(etc.) style files, but using tools on Google you can easily convert the provided Less files to your desired style format. +## Is there a difference between undefined and null in the controlled components of antd? + +**Yes. antd will treats `undefined` as uncontrolled but `null` as controlled components which means empty value of it.** + +As input element, React treats both `undefined` and `null` as uncontrolled. When the `value` is converted from a valid value to `undefined` or `null`, the component is no longer controlled, which causes some unexpected cases. + +But in antd, `undefined` is treated as uncontrolled, and `null` is used as an explicit empty value of controlled components. To deal with some cases (e.g. `allowClear`) like clearing the `value` when the `value` is non-primitive. If you need a component controlled with the `value` valid, just set the `value` as `null`. + +Note: For `options` in `Select-like` components, it is **strongly recommended not** to use `undefined` and `null` as `value` in `option`. Please use `string | number` as a valid `value` in `option`. + ## `Select Dropdown DatePicker TimePicker Popover Popconfirm` disappears when I click another popup component inside it. How do I resolve this? This is an old bug that has been fixed since `v3.11.x`. If you're using an older version, you can use ` trigger.parentElement}>` 来在 Popover 中渲染组件,或者使用其他的 `getXxxxContainer` 参数。 @@ -85,9 +95,9 @@ antd 内部会对 props 进行浅比较实现性能优化。当状态变更, - 2.x: https://ant-design-2x.gitee.io/ - 1.x: https://ant-design-1x.gitee.io/ -## `antd` 会像 `React` 那样提供单文件引入吗? +## `antd` 可以像 `React` 那样使用单文件引入吗? -是的,[你可以用 script 标签引入](https://ant.design/docs/react/introduce-cn#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%BC%95%E5%85%A5)。但是我们推荐使用 `npm` 来引入 `antd`,这样维护起来更简单方便。 +可以,[你可以用 script 标签引入](https://ant.design/docs/react/introduce-cn#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%BC%95%E5%85%A5)。但是我们推荐使用 `npm` 来引入 `antd`,这样维护起来更简单方便。 ## 在我的网络环境下没法获取到 `icon` 文件。 diff --git a/package.json b/package.json index a2dc069546..b025346feb 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "@ant-design/cssinjs": "^0.0.0-alpha.34", "@ant-design/icons": "^4.7.0", "@ant-design/react-slick": "~0.29.1", - "@babel/runtime": "^7.12.5", + "@babel/runtime": "^7.18.3", "@ctrl/tinycolor": "^3.4.0", "classnames": "^2.2.6", "copy-to-clipboard": "^3.2.0", @@ -157,14 +157,14 @@ "rc-tree-select": "~5.4.0", "rc-trigger": "^5.2.10", "rc-upload": "~4.3.0", - "rc-util": "^5.22.3", + "rc-util": "^5.22.5", "scroll-into-view-if-needed": "^2.2.25", "shallowequal": "^1.1.0" }, "devDependencies": { "@ant-design/bisheng-plugin": "^3.3.0-alpha.4", "@ant-design/hitu": "^0.0.0-alpha.13", - "@ant-design/tools": "^15.0.3", + "@ant-design/tools": "^15.0.4", "@docsearch/css": "^3.0.0", "@qixian.cs/github-contributors-list": "^1.0.3", "@stackblitz/sdk": "^1.3.0", diff --git a/site/theme/template/Home/util.tsx b/site/theme/template/Home/util.tsx index ce3c2f80c2..5c267662f9 100644 --- a/site/theme/template/Home/util.tsx +++ b/site/theme/template/Home/util.tsx @@ -15,7 +15,7 @@ export function preLoad(list: string[]) { } } -export function useSiteData(): [T, boolean] { +export function useSiteData(): [T, boolean] { const [data, setData] = React.useState({} as any); const [loading, setLoading] = React.useState(false); diff --git a/site/theme/template/utils.tsx b/site/theme/template/utils.tsx index 73704ea823..1152e14bc0 100644 --- a/site/theme/template/utils.tsx +++ b/site/theme/template/utils.tsx @@ -1,5 +1,5 @@ -import flattenDeep from 'lodash/flattenDeep'; import flatten from 'lodash/flatten'; +import flattenDeep from 'lodash/flattenDeep'; import themeConfig from '../../themeConfig'; interface Meta { @@ -212,7 +212,7 @@ export function getMetaDescription(jml?: any[] | null) { .join(''); return [tag, content]; }), - ).find(p => p && typeof p === 'string' && !COMMON_TAGS.includes(p)); + ).find(p => p && typeof p === 'string' && !COMMON_TAGS.includes(p)) as string; return paragraph; }