import React from 'react'; import type { TableProps } from '..'; import Table from '..'; import { fireEvent, render, act } from '../../../tests/utils'; import ConfigProvider from '../../config-provider'; import { resetWarned } from '../../_util/warning'; import type { TableRowSelection } from '../interface'; describe('Table.rowSelection', () => { window.requestAnimationFrame = (callback) => window.setTimeout(callback, 16); window.cancelAnimationFrame = window.clearTimeout; const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); afterEach(() => { errorSpy.mockReset(); }); afterAll(() => { errorSpy.mockRestore(); }); const columns = [ { title: 'Name', dataIndex: 'name', }, ]; const data = [ { key: 0, name: 'Jack' }, { key: 1, name: 'Lucy' }, { key: 2, name: 'Tom' }, { key: 3, name: 'Jerry' }, ]; function createTable(props: TableProps = {}) { return ; } function renderedNames(container: ReturnType['container']) { const namesList: Node['textContent'][] = []; container ?.querySelector('.ant-table-tbody') ?.querySelectorAll('tr') ?.forEach((tr) => { namesList.push(tr?.querySelectorAll('td')?.[1]?.textContent); }); return namesList; } function getSelections(container: ReturnType['container']) { const keys: React.Key[] = []; container.querySelectorAll('.ant-table-tbody tr').forEach((row) => { const key = row.getAttribute('data-row-key'); if (row.querySelector('input')?.checked) { if (isNaN(Number(key))) { // rowKey keys.push(key!); } else { keys.push(Number(key)); } } }); return keys; } function getIndeterminateSelection(container: ReturnType['container']) { const keys: React.Key[] = []; container.querySelectorAll('.ant-table-tbody tr').forEach((row) => { const key = row.getAttribute('data-row-key'); if (row.querySelector('.ant-checkbox-indeterminate')) { if (isNaN(Number(key))) { // rowKey keys.push(key!); } else { keys.push(Number(key)); } } }); return keys; } it('select default row', () => { const { container } = render(createTable({ rowSelection: { defaultSelectedRowKeys: [0] } })); const checkboxes = container.querySelectorAll('input[type="checkbox"]'); expect(getSelections(container)).toEqual([0]); fireEvent.click(checkboxes[1]); expect(getSelections(container)).toEqual([]); fireEvent.click(checkboxes[0]); expect(getSelections(container)).toEqual([0, 1, 2, 3]); fireEvent.click(checkboxes[0]); expect(getSelections(container)).toEqual([]); }); it('select by checkbox', () => { const { container } = render(createTable()); const checkboxes = container.querySelectorAll('input[type="checkbox"]'); fireEvent.click(checkboxes[0]); expect(getSelections(container)).toEqual([0, 1, 2, 3]); fireEvent.click(checkboxes[1]); expect(getSelections(container)).toEqual([1, 2, 3]); fireEvent.click(checkboxes[1]); expect(getSelections(container)).toEqual([0, 1, 2, 3]); }); it('select by radio', () => { const { container } = render(createTable({ rowSelection: { type: 'radio' } })); const radios = container.querySelectorAll('input[type="radio"]'); expect(radios.length).toBe(4); fireEvent.click(radios[0]); expect(getSelections(container)).toEqual([0]); fireEvent.click(radios[radios.length - 1]); expect(getSelections(container)).toEqual([3]); }); it('pass getCheckboxProps to checkbox', () => { const rowSelection = { getCheckboxProps: (record: any) => ({ disabled: record.name === 'Lucy', indeterminate: record.name === 'Tom', name: record.name, }), }; const { container } = render(createTable({ rowSelection })); const checkboxes = container.querySelectorAll('input[type="checkbox"]'); expect(checkboxes[1].disabled).toBe(false); expect(checkboxes[1].name).toEqual(data[0].name); expect(checkboxes[2].disabled).toBe(true); expect(checkboxes[2].name).toEqual(data[1].name); expect(getIndeterminateSelection(container)).toEqual([2]); }); it("make getCheckboxProps's `indeterminate` override selectedRowKeys' effect", () => { const rowSelection: TableProps['rowSelection'] = { getCheckboxProps: (record) => ({ disabled: record.name === 'Lucy', indeterminate: record.name === 'Tom', name: record.name, }), selectedRowKeys: [2], }; const { container } = render(createTable({ rowSelection })); expect(getIndeterminateSelection(container)).toEqual([2]); }); it('works with pagination', () => { const { container } = render(createTable({ pagination: { pageSize: 2 } })); const pagers = container.querySelectorAll('.ant-pagination-item'); const checkboxAll = container.querySelector('input[type="checkbox"]'); const objectContaining: { checked?: boolean; indeterminate?: boolean } = {}; fireEvent.click(checkboxAll!); objectContaining.checked = checkboxAll?.checked; // true objectContaining.indeterminate = getIndeterminateSelection(container).length > 0; // false expect.objectContaining(objectContaining); fireEvent.click(pagers[1]); objectContaining.checked = checkboxAll?.checked; // false objectContaining.indeterminate = getIndeterminateSelection(container).length > 0; // false expect.objectContaining(objectContaining); fireEvent.click(pagers[0]); objectContaining.checked = checkboxAll?.checked; // true objectContaining.indeterminate = getIndeterminateSelection(container).length > 0; // false expect.objectContaining(objectContaining); }); // https://github.com/ant-design/ant-design/issues/4020 it('handles defaultChecked', () => { resetWarned(); const rowSelection = { getCheckboxProps: (record) => ({ defaultChecked: record.key === 0, }), } as TableRowSelection; render(createTable({ rowSelection })); expect(errorSpy).toHaveBeenCalledWith( 'Warning: [antd: Table] Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.', ); }); it('can be controlled', () => { const { container, rerender } = render(createTable({ rowSelection: { selectedRowKeys: [0] } })); expect(getSelections(container)).toEqual([0]); rerender(createTable({ rowSelection: { selectedRowKeys: [1] } })); expect(getSelections(container)).toEqual([1]); }); it('fires change & select events', () => { const order: string[] = []; const handleChange = jest.fn().mockImplementation(() => { order.push('onChange'); }); const handleSelect = jest.fn().mockImplementation(() => { order.push('onSelect'); }); const rowSelection = { onChange: handleChange, onSelect: handleSelect, }; const { container } = render(createTable({ rowSelection })); fireEvent.click(container.querySelectorAll('input[type="checkbox"]')[4]); expect(handleChange).toHaveBeenCalledWith([3], [{ key: 3, name: 'Jerry' }], { type: 'single', }); expect(handleSelect.mock.calls.length).toBe(1); expect(handleSelect.mock.calls[0][0]).toEqual({ key: 3, name: 'Jerry' }); expect(handleSelect.mock.calls[0][1]).toEqual(true); expect(handleSelect.mock.calls[0][2]).toEqual([{ key: 3, name: 'Jerry' }]); expect(handleSelect.mock.calls[0][3].type).toBe('click'); expect(order).toEqual(['onSelect', 'onChange']); }); it('fires selectMulti event', () => { const order: string[] = []; const handleSelectMulti = jest.fn().mockImplementation(() => { order.push('onSelectMultiple'); }); const handleSelect = jest.fn().mockImplementation(() => { order.push('onSelect'); }); const handleChange = jest.fn().mockImplementation(() => { order.push('onChange'); }); const rowSelection = { onChange: handleChange, onSelect: handleSelect, onSelectMultiple: handleSelectMulti, }; const { container } = render(createTable({ rowSelection })); fireEvent.click(container.querySelectorAll('tbody input[type="checkbox"]')[0], { shiftKey: true, }); expect(handleSelect).toHaveBeenCalled(); expect(handleChange).toHaveBeenLastCalledWith([0], [{ key: 0, name: 'Jack' }], { type: 'single', }); fireEvent.click(container.querySelectorAll('tbody input[type="checkbox"]')[2], { shiftKey: true, }); expect(handleSelectMulti).toHaveBeenCalledWith( true, [data[0], data[1], data[2]], [data[1], data[2]], ); expect(handleChange).toHaveBeenLastCalledWith( [0, 1, 2], [ { key: 0, name: 'Jack' }, { key: 1, name: 'Lucy' }, { key: 2, name: 'Tom' }, ], { type: 'multiple' }, ); fireEvent.click(container.querySelectorAll('tbody input[type="checkbox"]')[0], { shiftKey: true, }); expect(handleSelectMulti).toHaveBeenCalledWith(false, [], [data[0], data[1], data[2]]); expect(handleChange).toHaveBeenLastCalledWith([], [], { type: 'multiple' }); expect(order).toEqual([ 'onSelect', 'onChange', 'onSelectMultiple', 'onChange', 'onSelectMultiple', 'onChange', ]); }); 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), }, } as TableProps), ); 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()!); 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: string[] = []; const handleSelectAll = jest.fn().mockImplementation(() => { order.push('onSelectAll'); }); const handleChange = jest.fn().mockImplementation(() => { order.push('onChange'); }); const rowSelection = { onChange: handleChange, onSelectAll: handleSelectAll, }; const { container } = render(createTable({ rowSelection })); const checkAll = container.querySelector('input[type="checkbox"]'); fireEvent.click(checkAll!); expect(handleSelectAll).toHaveBeenCalledWith(true, data, data); expect(order).toEqual(['onSelectAll', 'onChange']); fireEvent.click(checkAll!); expect(handleSelectAll).toHaveBeenCalledWith(false, [], data); }); it('works with selectAll option inside selection menu', () => { jest.useFakeTimers(); const handleChange = jest.fn(); const rowSelection = { onChange: handleChange, selections: true, }; const { container } = render(createTable({ rowSelection })); // Open fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); fireEvent.click(container.querySelectorAll('.ant-dropdown-menu-item')[0]); expect(handleChange.mock.calls[0][0]).toEqual([0, 1, 2, 3]); }); it('render with default selection correctly', () => { jest.useFakeTimers(); const rowSelection = { selections: true, }; const { container } = render(createTable({ rowSelection })); fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); expect(container.querySelector('.ant-dropdown')).toMatchSnapshot(); }); it('fires selectInvert event', () => { jest.useFakeTimers(); const order: string[] = []; const handleSelectInvert = jest.fn().mockImplementation(() => { order.push('onSelectInvert'); }); const handleChange = jest.fn().mockImplementation(() => { order.push('onChange'); }); const rowSelection = { onChange: handleChange, onSelectInvert: handleSelectInvert, selections: true, }; const { container } = render(createTable({ rowSelection })); fireEvent.click(container.querySelectorAll('.ant-checkbox')[1]); // Open fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); fireEvent.click(container.querySelectorAll('li.ant-dropdown-menu-item')[1]); expect(handleSelectInvert).toHaveBeenCalledWith([1, 2, 3]); expect(order).toEqual(['onChange', 'onSelectInvert', 'onChange']); jest.useRealTimers(); }); it('fires selectNone event', () => { jest.useFakeTimers(); const order: string[] = []; const handleChange = jest.fn().mockImplementation(() => { order.push('onChange'); }); const handleSelectNone = jest.fn().mockImplementation(() => { order.push('onSelectNone'); }); const rowSelection = { onChange: handleChange, onSelectNone: handleSelectNone, selections: true, }; const { container } = render(createTable({ rowSelection })); fireEvent.click(container.querySelectorAll('.ant-checkbox')[1]); // Open fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); const dropdownMenuItems = container.querySelectorAll('.ant-dropdown-menu-item'); fireEvent.click(dropdownMenuItems[dropdownMenuItems.length - 1]); expect(handleSelectNone).toHaveBeenCalled(); expect(order).toEqual(['onChange', 'onSelectNone', 'onChange']); }); it('fires selection event', () => { jest.useFakeTimers(); const handleSelectOdd = jest.fn(); const handleSelectEven = jest.fn(); const rowSelection = { selections: [ Table.SELECTION_ALL, Table.SELECTION_INVERT, { key: 'odd', text: '奇数项', onSelect: handleSelectOdd, }, { key: 'even', text: '偶数项', onSelect: handleSelectEven, }, ], }; const { container } = render(createTable({ rowSelection })); // Open fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); const dropdownMenuItems = container.querySelectorAll('.ant-dropdown-menu-item'); expect(dropdownMenuItems.length).toBe(4); fireEvent.click(dropdownMenuItems[2]); expect(handleSelectOdd).toHaveBeenCalledWith([0, 1, 2, 3]); fireEvent.click(dropdownMenuItems[3]); expect(handleSelectEven).toHaveBeenCalledWith([0, 1, 2, 3]); }); describe('preset selection options', () => { const presetData = [ { key: 0, name: 'Jack' }, { key: 1, name: 'Lucy', disabled: true }, { key: 2, name: 'Tom' }, ]; const getCheckboxProps = (record: any) => record; it('SELECTION_ALL', () => { jest.useFakeTimers(); const onChange = jest.fn(); const { container } = render( createTable({ dataSource: presetData, rowSelection: { onChange, defaultSelectedRowKeys: [2], getCheckboxProps, selections: [Table.SELECTION_ALL], }, }), ); fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); fireEvent.click(container.querySelector('li.ant-dropdown-menu-item')!); expect(onChange).toHaveBeenCalledWith([0, 2], expect.anything(), { type: 'all' }); }); it('SELECTION_INVERT', () => { jest.useFakeTimers(); const onChange = jest.fn(); const { container } = render( createTable({ dataSource: presetData, rowSelection: { onChange, defaultSelectedRowKeys: [2], getCheckboxProps, selections: [Table.SELECTION_INVERT], }, }), ); fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); fireEvent.click(container.querySelector('li.ant-dropdown-menu-item')!); expect(onChange).toHaveBeenCalledWith([0], expect.anything(), { type: 'invert' }); }); it('SELECTION_NONE', () => { jest.useFakeTimers(); const onChange = jest.fn(); const { container } = render( createTable({ dataSource: presetData, rowSelection: { onChange, defaultSelectedRowKeys: [1, 2], getCheckboxProps, selections: [Table.SELECTION_NONE], }, }), ); fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); fireEvent.click(container.querySelector('li.ant-dropdown-menu-item')!); expect(onChange).toHaveBeenCalledWith([1], expect.anything(), { type: 'none' }); }); }); it('could hide selectAll checkbox and custom selection', () => { const rowSelection = { hideSelectAll: true, }; const { container } = render(createTable({ rowSelection })); expect(container.querySelector('.ant-selection')).toBeFalsy(); }); it('handle custom selection onSelect correctly when hide default selection options', () => { jest.useFakeTimers(); const handleSelectOdd = jest.fn(); const handleSelectEven = jest.fn(); const rowSelection = { selections: [ { key: 'odd', text: '奇数项', onSelect: handleSelectOdd, }, { key: 'even', text: '偶数项', onSelect: handleSelectEven, }, ], }; const { container } = render(createTable({ rowSelection })); // Open fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); const dropdownMenuItems = container.querySelectorAll('li.ant-dropdown-menu-item'); expect(dropdownMenuItems.length).toBe(2); fireEvent.click(dropdownMenuItems[0]); expect(handleSelectOdd).toHaveBeenCalledWith([0, 1, 2, 3]); fireEvent.click(dropdownMenuItems[1]); expect(handleSelectEven).toHaveBeenCalledWith([0, 1, 2, 3]); }); // https://github.com/ant-design/ant-design/issues/4245 it('handles disabled checkbox correctly when dataSource changes', () => { const rowSelection: TableProps['rowSelection'] = { getCheckboxProps: (record) => ({ disabled: record.disabled }), }; const { container, rerender } = render(createTable({ rowSelection })); const newData = [ { key: 0, name: 'Jack', disabled: true }, { key: 1, name: 'Lucy', disabled: true }, ]; rerender(createTable({ rowSelection, dataSource: newData })); container.querySelectorAll('input').forEach((checkbox) => { expect(checkbox.disabled).toBe(true); }); }); // https://github.com/ant-design/ant-design/issues/4245 it('should allow dynamic getCheckboxProps', () => { const { container, rerender } = render(
({ disabled: record.name === 'Jack' }), }} />, ); let checkboxList = container.querySelectorAll('input'); expect(checkboxList[1]).toHaveAttribute('disabled'); expect(checkboxList[2]).not.toHaveAttribute('disabled'); rerender(
({ disabled: record.name === 'Lucy' }), }} />, ); checkboxList = container.querySelectorAll('input'); expect(checkboxList[1]).not.toHaveAttribute('disabled'); expect(checkboxList[2]).toHaveAttribute('disabled'); }); // https://github.com/ant-design/ant-design/issues/4779 it('should not switch pagination when select record', () => { const newData: Record<'key' | 'name', string>[] = []; for (let i = 0; i < 20; i += 1) { newData.push({ key: i.toString(), name: i.toString(), }); } const { container } = render( createTable({ rowSelection: {}, dataSource: newData, }), ); fireEvent.click(container.querySelectorAll('.ant-pagination-item')[1]); // switch to second page fireEvent.click(container.querySelector('.ant-checkbox')!); expect(renderedNames(container)).toEqual([ '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', ]); }); it('highlight selected row', () => { const { container } = render(createTable()); fireEvent.click(container.querySelectorAll('input')[1]); expect( container.querySelectorAll('tbody tr')[0].className.includes('ant-table-row-selected'), ).toBe(true); }); it('fix selection column on the left', () => { const { asFragment } = render( createTable({ rowSelection: { fixed: true }, scroll: { x: 903 }, }), ); expect(asFragment().firstChild).toMatchSnapshot(); }); it('fix expand on th left when selection column fixed on the left', () => { const { asFragment } = render( createTable({ expandable: { expandedRowRender() { return
; }, }, rowSelection: { fixed: true }, scroll: { x: 903 }, }), ); expect(asFragment().firstChild).toMatchSnapshot(); }); it('fix selection column on the left when any other column is fixed', () => { const { asFragment } = render( createTable({ rowSelection: {}, columns: [ { title: 'Name', dataIndex: 'name', fixed: 'left', }, ], scroll: { x: 903 }, }), ); expect(asFragment().firstChild).toMatchSnapshot(); }); it('use column as selection column when key is `selection-column`', () => { const { asFragment } = render( createTable({ rowSelection: {}, columns: [ { title: 'Name', dataIndex: 'name', key: 'selection-column', }, ], }), ); expect(asFragment().firstChild).toMatchSnapshot(); }); // https://github.com/ant-design/ant-design/issues/10629 it('should keep all checked state when remove item from dataSource', () => { const { container, rerender } = render(
, ); const checkboxs = container.querySelectorAll('.ant-checkbox'); expect(checkboxs.length).toBe(5); checkboxs.forEach((checkbox) => { expect(checkbox.querySelector('input')?.checked).toBe(true); expect(checkbox.className.includes('ant-checkbox-indeterminate')).toBe(false); }); rerender(
, ); expect(container.querySelectorAll('.ant-checkbox').length).toBe(4); container.querySelectorAll('.ant-checkbox').forEach((checkbox) => { expect(checkbox.querySelector('input')?.checked).toBe(true); expect(checkbox.className.includes('ant-checkbox-indeterminate')).toBe(false); }); }); // https://github.com/ant-design/ant-design/issues/11042 it('add columnTitle for rowSelection', () => { const { container, rerender } = render(
, ); expect(container.querySelector('thead tr th')?.textContent).toBe('多选'); rerender(
, ); expect(container.querySelector('thead tr th')?.textContent).toBe('单选'); }); // https://github.com/ant-design/ant-design/issues/11384 it('should keep item even if in filter', () => { const filterColumns = [ { title: 'Name', dataIndex: 'name', filters: [ { text: 'Jack', value: 'Jack', }, { text: 'Lucy', value: 'Lucy', }, ], filterDropdownOpen: true, onFilter: (value: any, record: any) => record.name.indexOf(value) === 0, }, ]; const onChange = jest.fn(); const rowSelection = { onChange, }; const { container } = render(
, ); function clickFilter(indexList: number[]) { indexList.forEach((index) => { // wrapper.find('.ant-dropdown-menu-item .ant-checkbox-wrapper').at(index).simulate('click'); fireEvent.click( container.querySelectorAll('.ant-dropdown-menu-item .ant-checkbox-wrapper')[index], ); }); // wrapper.find('.ant-table-filter-dropdown-btns .ant-btn-primary').simulate('click'); fireEvent.click(container.querySelector('.ant-table-filter-dropdown-btns .ant-btn-primary')!); } function clickItem() { fireEvent.click( container.querySelectorAll('tbody .ant-table-selection-column .ant-checkbox-input')[0], ); // wrapper // .find('tbody .ant-table-selection-column .ant-checkbox-input') // .at(0) // .simulate('change', { // target: { checked: true }, // }); } // Check Jack clickFilter([0]); expect(container.querySelectorAll('tbody tr').length).toBe(1); clickItem(); expect(onChange.mock.calls[0][0].length).toBe(1); expect(onChange.mock.calls[0][1].length).toBe(1); // Check Lucy clickFilter([0, 1]); expect(container.querySelectorAll('tbody tr').length).toBe(1); clickItem(); expect(onChange.mock.calls[1][0].length).toBe(2); expect(onChange.mock.calls[1][1].length).toBe(2); }); it('render correctly when set childrenColumnName', () => { const newDatas = [ { key: 1, name: 'Jack', children: [ { key: 11, name: 'John Brown', }, ], }, { key: 2, name: 'Lucy', children: [ { key: 21, name: 'Lucy Brown', }, ], }, ]; const { container } = render(
, ); const checkboxes = container.querySelectorAll('input'); const objectContaining: { checked?: boolean; indeterminate?: boolean } = {}; fireEvent.click(checkboxes[1]); objectContaining.checked = checkboxes[0].checked; // false objectContaining.indeterminate = getIndeterminateSelection(container).length > 0; // true expect.objectContaining(objectContaining); fireEvent.click(checkboxes[2]); objectContaining.checked = checkboxes[0].checked; // true objectContaining.indeterminate = getIndeterminateSelection(container).length > 0; // false expect.objectContaining(objectContaining); }); // https://github.com/ant-design/ant-design/issues/16614 it('should get selectedRows correctly when set childrenColumnName', () => { const onChange = jest.fn(); const newDatas = [ { key: 1, name: 'Jack', list: [ { key: 11, name: 'John Brown', }, ], }, ]; const { container } = render(
, ); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[2]); expect(onChange).toHaveBeenLastCalledWith([11], [newDatas[0].list[0]], { type: 'single' }); onChange.mockReset(); fireEvent.click(checkboxes[1]); const item0 = newDatas[0]; expect(onChange).toHaveBeenLastCalledWith([11, 1], [newDatas[0].list[0], item0], { type: 'single', }); }); it('clear selection className when remove `rowSelection`', () => { const dataSource = [ { id: 1, name: 'Hello', age: 10 }, { id: 2, name: 'World', age: 30 }, ]; const { container, rerender } = render(
null} rowKey="id" />, ); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[1]); expect(container.querySelectorAll('tr.ant-table-row-selected').length).toBe(1); rerender(
} expandedRowRender={() => null} rowKey="id" />, ); expect(container.querySelectorAll('tr.ant-table-row-selected').length).toBe(0); }); it('select by checkbox to trigger stopPropagation', () => { const { container } = render(createTable()); expect(() => { fireEvent.click(container.querySelectorAll('span')[10]); }).not.toThrow(); }); it('all disabled should not make select all checked', () => { const { container } = render( createTable({ rowSelection: { getCheckboxProps: () => ({ disabled: true }), }, }), ); expect( container.querySelector('thead .ant-checkbox-input')?.disabled, ).toBeTruthy(); expect( container.querySelector('thead .ant-checkbox-input')?.checked, ).toBeFalsy(); }); it('should make select all checked when each item is checked and disabled', () => { const { container } = render( createTable({ rowSelection: { selectedRowKeys: [0, 1, 2, 3], getCheckboxProps: () => ({ disabled: true, }), }, }), ); expect( container.querySelector('thead .ant-checkbox-input')?.disabled, ).toBeTruthy(); expect( container.querySelector('thead .ant-checkbox-input')?.checked, ).toBeTruthy(); }); it('should make select all indeterminated when each item is disabled and some item is checked', () => { const { container } = render( createTable({ rowSelection: { selectedRowKeys: [0], getCheckboxProps: () => ({ disabled: true, }), }, }), ); expect( container.querySelector('thead .ant-checkbox-input')?.disabled, ).toBeTruthy(); expect( container.querySelector('thead .ant-checkbox-input')?.checked, ).toBeFalsy(); expect( container.querySelector('thead .ant-checkbox-indeterminate.ant-checkbox-disabled'), ).toBeTruthy(); }); it('should make select all checked when each item is checked and some item is disabled', () => { const { container } = render( createTable({ rowSelection: { selectedRowKeys: [0, 1, 2, 3], getCheckboxProps: (record) => ({ disabled: record.key === 0, }), }, }), ); expect( container.querySelector('thead .ant-checkbox-input')?.disabled, ).toBeFalsy(); expect( container.querySelector('thead .ant-checkbox-input')?.checked, ).toBeTruthy(); }); it('should not make select all checked when some item is checked and disabled', () => { const { container } = render( createTable({ rowSelection: { selectedRowKeys: [1], getCheckboxProps: (record) => ({ disabled: record.key === 0, }), }, }), ); expect( container.querySelector('thead .ant-checkbox-input')?.disabled, ).toBeFalsy(); expect( container.querySelector('thead .ant-checkbox-input')?.checked, ).toBeFalsy(); expect(container.querySelector('thead .ant-checkbox-indeterminate')).toBeTruthy(); }); it('should onRowClick not called when checkbox clicked', () => { const onRowClick = jest.fn(); const { container } = render( createTable({ onRow: () => ({ onClick: onRowClick, }), }), ); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[checkboxes.length - 1]); expect(onRowClick).not.toHaveBeenCalled(); }); it('should support getPopupContainer', () => { const rowSelection = { selections: true, }; const getPopupContainer = jest.fn((node) => node); const { container } = render( createTable({ rowSelection, getPopupContainer, }), ); jest.useFakeTimers(); fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); expect(container.firstChild).toMatchSnapshot(); expect(getPopupContainer).toHaveBeenCalled(); }); it('should support getPopupContainer from ConfigProvider', () => { const rowSelection = { selections: true, }; const { container } = render( node?.parentNode as HTMLElement}> {createTable({ rowSelection, })} , ); jest.useFakeTimers(); fireEvent.mouseEnter(container.querySelector('.ant-dropdown-trigger')!); act(() => { jest.runAllTimers(); }); expect(container.firstChild).toMatchSnapshot(); }); it('Table selection should check', () => { const onChange = jest.fn(); const { container } = render(
, ); const checkboxs = container.querySelectorAll('input'); fireEvent.click(checkboxs[checkboxs.length - 1]); expect(onChange.mock.calls[0][1]).toEqual([expect.objectContaining({ name: 'bamboo' })]); }); describe('supports children', () => { const dataWithChildren = [ { key: 0, name: 'Jack' }, { key: 1, name: 'Lucy' }, { key: 2, name: 'Tom' }, { key: 3, name: 'Jerry', children: [ { key: 4, name: 'Jerry Jack', }, { key: 5, name: 'Jerry Lucy', }, { key: 6, name: 'Jerry Tom', children: [ { key: 7, name: 'Jerry Tom Jack', }, { key: 8, name: 'Jerry Tom Lucy', }, { key: 9, name: 'Jerry Tom Tom', }, ], }, ], }, ]; describe('supports checkStrictly', () => { it('use data entity key', () => { const onChange = jest.fn(); const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, onChange, }, }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual([3, 4, 5, 6, 7, 8, 9]); expect(getIndeterminateSelection(container)).toEqual([]); expect(onChange.mock.calls[0][0]).toEqual([3, 4, 5, 6, 7, 8, 9]); fireEvent.click(checkboxes[7]); expect(getSelections(container)).toEqual([4, 5]); expect(getIndeterminateSelection(container)).toEqual([3]); expect(onChange.mock.calls[1][0]).toEqual([4, 5]); }); it('use function rowkey', () => { const onChange = jest.fn(); const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, onChange, }, rowKey: (entity) => entity.name, }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom', 'Jerry Tom Jack', 'Jerry Tom Lucy', 'Jerry Tom Tom', ]); expect(getIndeterminateSelection(container)).toEqual([]); expect(onChange.mock.calls[0][0]).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom', 'Jerry Tom Jack', 'Jerry Tom Lucy', 'Jerry Tom Tom', ]); fireEvent.click(checkboxes[7]); expect(getSelections(container)).toEqual(['Jerry Jack', 'Jerry Lucy']); expect(getIndeterminateSelection(container)).toEqual(['Jerry']); expect(onChange.mock.calls[1][0]).toEqual(['Jerry Jack', 'Jerry Lucy']); }); it('use string rowkey', () => { const onChange = jest.fn(); const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, onChange, }, rowKey: 'name', }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom', 'Jerry Tom Jack', 'Jerry Tom Lucy', 'Jerry Tom Tom', ]); expect(getIndeterminateSelection(container)).toEqual([]); expect(onChange.mock.calls[0][0]).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom', 'Jerry Tom Jack', 'Jerry Tom Lucy', 'Jerry Tom Tom', ]); fireEvent.click(checkboxes[7]); expect(getSelections(container)).toEqual(['Jerry Jack', 'Jerry Lucy']); expect(getIndeterminateSelection(container)).toEqual(['Jerry']); expect(onChange.mock.calls[1][0]).toEqual(['Jerry Jack', 'Jerry Lucy']); }); it('initialized correctly', () => { const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, selectedRowKeys: [7, 8, 9], }, rowKey: 'key', }); const { container } = render(table); expect(getSelections(container)).toEqual([6, 7, 8, 9]); expect(getIndeterminateSelection(container)).toEqual([3]); }); it('works with disabled checkbox', () => { const onChange = jest.fn(); const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, onChange, getCheckboxProps(record) { return { disabled: record.name === 'Jerry Tom', }; }, }, }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[10]); fireEvent.click(checkboxes[4]); expect(getSelections(container).sort()).toEqual([3, 4, 5, 9]); expect(getIndeterminateSelection(container)).toEqual([]); expect(Array.from(onChange.mock.calls[1][0]).sort()).toEqual([3, 4, 5, 9]); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual([9]); expect(getIndeterminateSelection(container)).toEqual([]); expect(onChange.mock.calls[2][0]).toEqual([9]); }); it('works with disabled checkbox and function rowkey', () => { const onChange = jest.fn(); const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, onChange, getCheckboxProps(record) { return { disabled: record.name === 'Jerry Tom', }; }, }, rowKey: (entity) => entity.name, }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[10]); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom Tom', ]); expect(getIndeterminateSelection(container)).toEqual([]); expect(Array.from(onChange.mock.calls[1][0]).sort()).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom Tom', ]); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual(['Jerry Tom Tom']); expect(getIndeterminateSelection(container)).toEqual([]); expect(onChange.mock.calls[2][0]).toEqual(['Jerry Tom Tom']); }); it('works with disabled checkbox and string rowkey', () => { const onChange = jest.fn(); const table = createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, onChange, getCheckboxProps(record) { return { disabled: record.name === 'Jerry Tom', }; }, }, rowKey: 'name', }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); fireEvent.click(checkboxes[10]); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom Tom', ]); expect(getIndeterminateSelection(container)).toEqual([]); expect(Array.from(onChange.mock.calls[1][0]).sort()).toEqual([ 'Jerry', 'Jerry Jack', 'Jerry Lucy', 'Jerry Tom Tom', ]); fireEvent.click(checkboxes[4]); expect(getSelections(container)).toEqual(['Jerry Tom Tom']); expect(getIndeterminateSelection(container)).toEqual([]); expect(onChange.mock.calls[2][0]).toEqual(['Jerry Tom Tom']); }); it('should support `childrenColumnName`', () => { const onChange = jest.fn(); const table = createTable({ dataSource: [ { key: 0, name: 'Jack', childList: [ { key: 1, name: 'Light' }, { key: 2, name: 'Bamboo' }, ], }, ], expandable: { childrenColumnName: 'childList', defaultExpandAllRows: true, }, rowSelection: { checkStrictly: false, onChange, }, }); const { container } = render(table); const checkboxes = container.querySelectorAll('input'); expect(checkboxes).toHaveLength(1 + 3); fireEvent.click(checkboxes[1]); expect(getSelections(container)).toEqual([0, 1, 2]); }); }); it('warns when set `indeterminate` using `rowSelection.getCheckboxProps` is not allowed with tree structured data.', () => { resetWarned(); render( createTable({ dataSource: dataWithChildren, defaultExpandAllRows: true, rowSelection: { checkStrictly: false, getCheckboxProps() { return { indeterminate: true, }; }, }, }), ); expect(errorSpy).toHaveBeenCalledWith( 'Warning: [antd: Table] set `indeterminate` using `rowSelection.getCheckboxProps` is not allowed with tree structured dataSource.', ); }); }); describe('cache with selected keys', () => { it('default not cache', () => { const onChange = jest.fn(); const { container, rerender } = render(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith(['light'], [{ name: 'light' }], { type: 'single' }); rerender(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith(['bamboo'], [{ name: 'bamboo' }], { type: 'single' }); }); it('cache with preserveSelectedRowKeys', () => { const onChange = jest.fn(); const { container, rerender } = render(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith(['light'], [{ name: 'light' }], { type: 'single' }); rerender(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith( ['light', 'bamboo'], [{ name: 'light' }, { name: 'bamboo' }], { type: 'single' }, ); }); it('works with receive selectedRowKeys fron [] to undefined', () => { const onChange = jest.fn(); const dataSource = [{ name: 'Jack' }]; const { container, rerender } = render(
, ); rerender(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith(['Jack'], [{ name: 'Jack' }], { type: 'single' }); }); it('works with selectionType radio receive selectedRowKeys from [] to undefined', () => { const onChange = jest.fn(); const dataSource = [{ name: 'Jack' }]; const { container, rerender } = render(
, ); rerender(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith(['Jack'], [{ name: 'Jack' }], { type: 'single' }); }); it('selectedRows ant selectedKeys should keep sync in initial state', () => { const dataSource = [{ name: 'Jack' }, { name: 'Tom' }, { name: 'Lucy' }, { name: 'John' }]; const onChange = jest.fn(); const rowSelection = { preserveSelectedRowKeys: true, onChange, selectedRowKeys: ['Jack'], }; const { container, rerender } = render(
, ); rerender(
, ); fireEvent.click(container.querySelector('tbody input')!); expect(onChange).toHaveBeenCalledWith( ['Jack', 'Lucy'], [{ name: 'Jack' }, { name: 'Lucy' }], { type: 'single' }, ); }); }); });