From 885108df5405d06a02fb78c9d51cb87467fdebb6 Mon Sep 17 00:00:00 2001 From: Taylor Jones Date: Wed, 8 Mar 2023 12:34:27 -0600 Subject: [PATCH] test(datatable): refactor to use RTL (#13294) * chore: wip * chore: wip * test(datatable): refactor to use RTL * chore: typo --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../DataTable/__tests__/DataTable-test.js | 1359 ++--- .../__snapshots__/DataTable-test.js.snap | 4748 ++++------------- 2 files changed, 1694 insertions(+), 4413 deletions(-) diff --git a/packages/react/src/components/DataTable/__tests__/DataTable-test.js b/packages/react/src/components/DataTable/__tests__/DataTable-test.js index c3d829b4a7f6..f04c2a4ebab3 100644 --- a/packages/react/src/components/DataTable/__tests__/DataTable-test.js +++ b/packages/react/src/components/DataTable/__tests__/DataTable-test.js @@ -1,5 +1,5 @@ /** - * Copyright IBM Corp. 2016, 2023 + * Copyright IBM Corp. 2022 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. @@ -28,21 +28,12 @@ import DataTable, { TableToolbarSearch, TableToolbarMenu, } from '../'; -import { sortStates } from '../state/sorting'; -import { mount } from 'enzyme'; +import userEvent from '@testing-library/user-event'; +import { render, screen, within } from '@testing-library/react'; // Test helpers -const getHeaderAt = (wrapper, index) => - wrapper.find('TableHeader button').at(index); -const getRowAt = (wrapper, index) => wrapper.find('tbody tr').at(index); -const getFilterInput = (wrapper) => - wrapper.find('TableToolbarSearch Search input'); -const getSelectAll = (wrapper) => - wrapper.find('TableSelectAll input[type="checkbox"]'); const getLastCallFor = (mocker) => mocker.mock.calls[mocker.mock.calls.length - 1]; -const getInputAtIndex = ({ wrapper, index, inputType }) => - getRowAt(wrapper, index).find(`input[type="${inputType}"]`); describe('DataTable', () => { let mockProps; @@ -89,6 +80,7 @@ describe('DataTable', () => { }) => ( @@ -144,738 +136,825 @@ describe('DataTable', () => { }; }); - it('should render', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); - }); - - describe('sorting', () => { - it('should sort a row by a header when a header is clicked', () => { - const wrapper = mount(); - const header = getHeaderAt(wrapper, 0); - header.simulate('click'); - expect(wrapper.state('rowIds')).toEqual(['a', 'b', 'c']); + describe('renders as expected - Component API', () => { + it('should spread extra props onto outermost element', () => { + const { container } = render(); - header.simulate('click'); - expect(wrapper.state('rowIds')).toEqual(['c', 'b', 'a']); + expect(container.firstChild).toHaveAttribute('data-testid', 'test-id'); + }); - header.simulate('click'); - expect(wrapper.state('rowIds')).toEqual(['b', 'a', 'c']); + it('should render and match snapshot', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); }); + }); - it('should re-sort new row props by the current sort state', () => { - const wrapper = mount(); - const header = getHeaderAt(wrapper, 0); + describe('behaves as expected', () => { + describe('sorting', () => { + it('should sort a row by a header when a header is clicked', () => { + render(); + const header = within(screen.getAllByRole('columnheader')[0]).getByRole( + 'button' + ); + const cells = () => { + return screen.getAllByRole('cell').map((cell) => { + return cell.textContent; + }); + }; - header.simulate('click'); - expect(wrapper.state('rowIds')).toEqual(['a', 'b', 'c']); + expect(cells()).toEqual([ + 'Field 2:A', + 'Field 2:B', + 'Field 1:A', + 'Field 1:B', + 'Field 3:A', + 'Field 3:B', + ]); + + // Click to sort rows by Field A in ascending order + userEvent.click(header); + expect(cells()).toEqual([ + 'Field 1:A', + 'Field 1:B', + 'Field 2:A', + 'Field 2:B', + 'Field 3:A', + 'Field 3:B', + ]); + + // Click to sort rows by Field A in descending order + userEvent.click(header); + expect(cells()).toEqual([ + 'Field 3:A', + 'Field 3:B', + 'Field 2:A', + 'Field 2:B', + 'Field 1:A', + 'Field 1:B', + ]); + + // Click to unsort rows by Field A in descending order + userEvent.click(header); + expect(cells()).toEqual([ + 'Field 2:A', + 'Field 2:B', + 'Field 1:A', + 'Field 1:B', + 'Field 3:A', + 'Field 3:B', + ]); + }); - wrapper.setProps({ rows: mockProps.rows }); - expect(wrapper.state('rowIds')).toEqual(['a', 'b', 'c']); - }); + it('should re-sort new row props by the current sort state', () => { + const { rerender } = render( + + ); + const header = within(screen.getAllByRole('columnheader')[0]).getByRole( + 'button' + ); + + const cells = () => { + return screen.getAllByRole('cell').map((cell) => { + return cell.textContent; + }); + }; - it('should reset to ASC ordering when another header is clicked', () => { - const wrapper = mount(); + // Click to sort rows by Field A in ascending order + userEvent.click(header); + expect(cells()).toEqual([ + 'Field 1:A', + 'Field 1:B', + 'Field 2:A', + 'Field 2:B', + 'Field 3:A', + 'Field 3:B', + ]); + + rerender(); + expect(cells()).toEqual([ + 'Field 1:A', + 'Field 1:B', + 'Field 2:A', + 'Field 2:B', + 'Field 3:A', + 'Field 3:B', + ]); + }); - const firstHeader = getHeaderAt(wrapper, 0); - const secondHeader = getHeaderAt(wrapper, 1); + it('should reset to ASC ordering when another header is clicked', () => { + render(); - firstHeader.simulate('click'); - expect(wrapper.state('rowIds')).toEqual(['a', 'b', 'c']); + const firstHeader = () => screen.getAllByRole('columnheader')[0]; + const secondHeader = () => screen.getAllByRole('columnheader')[1]; + const firstHeaderButton = within(firstHeader()).getByRole('button'); + const secondHeaderButton = within(secondHeader()).getByRole('button'); - firstHeader.simulate('click'); - expect(wrapper.state('rowIds')).toEqual(['c', 'b', 'a']); - expect(wrapper.state('sortDirection')).toBe(sortStates.DESC); + expect(firstHeader()).toHaveTextContent('ascending'); - secondHeader.simulate('click'); - expect(wrapper.state('sortDirection')).toBe(sortStates.ASC); - }); - }); + userEvent.click(firstHeaderButton); + expect(firstHeader()).toHaveTextContent('descending'); - describe('filtering', () => { - it('should filter rows by the given input', () => { - const wrapper = mount(); - const filterInput = getFilterInput(wrapper); + userEvent.click(firstHeaderButton); + expect(firstHeader()).toHaveTextContent('unsort'); + + userEvent.click(secondHeaderButton); + // After clicking the second header once, the table will now be + // sorted ascending based on that header, which means the button + // should now be in a state where clicking _again_ will sort it + // "descending" via that header: + expect(secondHeader()).toHaveTextContent('descending'); + }); + }); - expect(wrapper.state('rowIds').length).toBe(mockProps.rows.length); + describe('filtering', () => { + it('should filter rows by the given input', () => { + render(); + const filterInput = screen.getByRole('searchbox'); + + // +1 for the header row + expect(screen.getAllByRole('row').length).toBe( + mockProps.rows.length + 1 + ); + + userEvent.type(filterInput, 'Field 1'); + + expect(mockProps.render).toHaveBeenCalledWith( + expect.objectContaining({ + rows: [ + expect.objectContaining({ + id: 'a', + }), + ], + }) + ); + }); + }); - filterInput.getDOMNode().value = 'Field 1'; - filterInput.simulate('change'); + describe('selection', () => { + let mockProps; - expect(mockProps.render).toHaveBeenCalledWith( - expect.objectContaining({ + beforeEach(() => { + mockProps = { rows: [ - expect.objectContaining({ + { + id: 'b', + fieldA: 'Field 2:A', + fieldB: 'Field 2:B', + }, + { id: 'a', - }), + fieldA: 'Field 1:A', + fieldB: 'Field 1:B', + }, + { + id: 'c', + fieldA: 'Field 3:A', + fieldB: 'Field 3:B', + }, ], - }) - ); - }); - }); - - describe('selection', () => { - let mockProps; - - beforeEach(() => { - mockProps = { - rows: [ - { - id: 'b', - fieldA: 'Field 2:A', - fieldB: 'Field 2:B', - }, - { - id: 'a', - fieldA: 'Field 1:A', - fieldB: 'Field 1:B', - }, - { - id: 'c', - fieldA: 'Field 3:A', - fieldB: 'Field 3:B', - }, - ], - headers: [ - { - key: 'fieldA', - header: 'Field A', - }, - { - key: 'fieldB', - header: 'Field B', - }, - ], - locale: 'en', - render: jest.fn( - ({ rows, headers, getHeaderProps, getSelectionProps }) => ( - - - - - - {headers.map((header, i) => ( - - {header.header} - - ))} - - - - {rows.map((row) => ( - - - {row.cells.map((cell) => ( - {cell.value} + headers: [ + { + key: 'fieldA', + header: 'Field A', + }, + { + key: 'fieldB', + header: 'Field B', + }, + ], + locale: 'en', + render: jest.fn( + ({ + rows, + headers, + getHeaderProps, + getSelectionProps, + getBatchActionProps, + onInputChange, + }) => ( + + + + + Ghost + + + Ghost + + + Ghost + + + + + + + Action 1 + + + Action 2 + + + Action 3 + + + + + +
+ + + + {headers.map((header, i) => ( + + {header.header} + ))} - ))} - -
-
- ) - ), - }; - }); + + + {rows.map((row) => ( + + + {row.cells.map((cell) => ( + {cell.value} + ))} + + ))} + + +
+ ) + ), + }; + }); - it('should render', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); - }); + it('should render and match snapshot', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); - it('should have select-all default to un-checked if no rows are present', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); - }); + it('should have select-all default to un-checked if no rows are present', () => { + render(); + expect(screen.getAllByRole('checkbox')[0]).not.toHaveAttribute( + 'checked' + ); + }); - it('should select all rows if a user interacts with select all', () => { - const wrapper = mount(); - expect(getSelectAll(wrapper).prop('checked')).toBe(false); + it('should select all rows if a user interacts with select all', () => { + render(); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; + expect(selectAllCheckbox).not.toBeChecked(); - getSelectAll(wrapper).simulate('click'); + userEvent.click(selectAllCheckbox); - expect(getSelectAll(wrapper).prop('checked')).toBe(true); + expect(selectAllCheckbox).toBeChecked(); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(mockProps.rows.length); - }); + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(mockProps.rows.length); + }); - it('should select a specific row when a user interacts with select row', () => { - const wrapper = mount(); - expect(getSelectAll(wrapper).prop('checked')).toBe(false); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'checkbox' }).prop( - 'checked' - ) - ).toBe(false); + it('should select a specific row when a user interacts with select row', () => { + render(); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; + const firstRowCheckbox = screen.getAllByRole('checkbox')[1]; - getInputAtIndex({ wrapper, index: 0, inputType: 'checkbox' }).simulate( - 'click' - ); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'checkbox' }).prop( - 'checked' - ) - ).toBe(true); + expect(selectAllCheckbox).not.toBeChecked(); + expect(firstRowCheckbox).not.toBeChecked(); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(1); - }); + userEvent.click(firstRowCheckbox); + + expect(firstRowCheckbox).toBeChecked(); + expect(selectAllCheckbox).toBePartiallyChecked(); + + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(1); + }); - it('should deselect all rows when onCancel invoked', () => { - const wrapper = mount(); - getSelectAll(wrapper).simulate('click'); - expect(getSelectAll(wrapper).prop('checked')).toBe(true); + it('should deselect all rows when batch action cancel is invoked', async () => { + render(); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; - const { getBatchActionProps } = getLastCallFor(mockProps.render)[0]; - expect(getBatchActionProps().shouldShowBatchActions).toBe(true); + userEvent.click(selectAllCheckbox); + expect(selectAllCheckbox).toBeChecked(); - getBatchActionProps().onCancel(); + const { getBatchActionProps } = getLastCallFor(mockProps.render)[0]; + expect(getBatchActionProps().shouldShowBatchActions).toBe(true); - wrapper.update(); + const cancelButton = await screen.findByText('Cancel'); + userEvent.click(cancelButton); - expect(getSelectAll(wrapper).prop('checked')).toBe(false); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(0); + expect(selectAllCheckbox).not.toBeChecked(); + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(0); + }); }); - }); - describe('selection with filtering', () => { - let mockProps; + describe('selection with filtering', () => { + let mockProps; - beforeEach(() => { - mockProps = { - rows: [ - { - id: 'b', - fieldA: 'Field 2:A', - fieldB: 'Field 2:B', - }, - { - id: 'a', - fieldA: 'Field 1:A', - fieldB: 'Field 1:B', - }, - { - id: 'c', - fieldA: 'Field 3:A', - fieldB: 'Field 3:B', - }, - ], - headers: [ - { - key: 'fieldA', - header: 'Field A', - }, - { - key: 'fieldB', - header: 'Field B', - }, - ], - locale: 'en', - render: jest.fn( - ({ - rows, - headers, - getHeaderProps, - getSelectionProps, - onInputChange, - }) => ( - - - - - - - Action 1 - - - Action 2 - - - Action 3 - - - - - - - - - - {headers.map((header, i) => ( - - {header.header} - - ))} - - - - {rows.map((row) => ( - - - {row.cells.map((cell) => ( - {cell.value} + beforeEach(() => { + mockProps = { + rows: [ + { + id: 'b', + fieldA: 'Field 2:A', + fieldB: 'Field 2:B', + }, + { + id: 'a', + fieldA: 'Field 1:A', + fieldB: 'Field 1:B', + }, + { + id: 'c', + fieldA: 'Field 3:A', + fieldB: 'Field 3:B', + }, + ], + headers: [ + { + key: 'fieldA', + header: 'Field A', + }, + { + key: 'fieldB', + header: 'Field B', + }, + ], + locale: 'en', + render: jest.fn( + ({ + rows, + headers, + getHeaderProps, + getSelectionProps, + onInputChange, + }) => ( + + + + + + + Action 1 + + + Action 2 + + + Action 3 + + + + + +
+ + + + {headers.map((header, i) => ( + + {header.header} + ))} - ))} - -
-
- ) - ), - }; - }); - - it('should only select all from filtered items', () => { - const wrapper = mount(); + + + {rows.map((row) => ( + + + {row.cells.map((cell) => ( + {cell.value} + ))} + + ))} + + + + ) + ), + }; + }); - expect(getSelectAll(wrapper).prop('checked')).toBe(false); + it('should only select all from filtered items', () => { + render(); - const filterInput = getFilterInput(wrapper); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; + const firstRowCheckbox = () => screen.getAllByRole('checkbox')[1]; + const filterInput = screen.getByRole('searchbox'); - filterInput.getDOMNode().value = 'Field 1'; - filterInput.simulate('change'); + expect(selectAllCheckbox).not.toBeChecked(); - getInputAtIndex({ wrapper, index: 0, inputType: 'checkbox' }).simulate( - 'click' - ); + userEvent.type(filterInput, 'Field 1'); + userEvent.click(firstRowCheckbox()); + userEvent.clear(filterInput); - filterInput.getDOMNode().value = ''; - filterInput.simulate('change'); + expect(selectAllCheckbox).toBePartiallyChecked(); - expect(wrapper.find('TableSelectAll').prop('indeterminate')).toBe(true); + let { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(1); - let { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(1); + userEvent.click(selectAllCheckbox); - getSelectAll(wrapper).simulate('click'); + selectedRows = getLastCallFor(mockProps.render)[0].selectedRows; + expect(selectedRows.length).toBe(0); + }); - selectedRows = getLastCallFor(mockProps.render)[0].selectedRows; - expect(selectedRows.length).toBe(0); - }); + it('should only select rows that are not disabled even when filtered', () => { + const nextRows = [ + ...mockProps.rows.map((row) => ({ ...row })), + { + id: 'd', + fieldA: 'Field 3:A', + fieldB: 'Field 3:B', + disabled: true, + }, + ]; + render(); - it('should only select rows that are not disabled even when filtered', () => { - const wrapper = mount(); + const filterInput = screen.getByRole('searchbox'); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; - const nextRows = [ - ...mockProps.rows.map((row) => ({ ...row })), - { - id: 'd', - fieldA: 'Field 3:A', - fieldB: 'Field 3:B', - disabled: true, - }, - ]; + userEvent.type(filterInput, 'Field 3'); + userEvent.click(selectAllCheckbox); - wrapper.setProps({ rows: nextRows }); + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(1); - const filterInput = getFilterInput(wrapper); + expect(selectAllCheckbox).toBePartiallyChecked(); + }); - filterInput.getDOMNode().value = 'Field 3'; - filterInput.simulate('change'); + it('does not select a row if they are all disabled', () => { + const nextRows = [ + ...mockProps.rows.map((row) => ({ ...row, disabled: true })), + ]; + render(); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; - getSelectAll(wrapper).simulate('click'); + userEvent.click(selectAllCheckbox); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(1); + expect(selectAllCheckbox).not.toBePartiallyChecked(); + expect(selectAllCheckbox).not.toBeChecked(); - expect(wrapper.find('TableSelectAll').prop('indeterminate')).toBe(true); - }); + const filterInput = screen.getByRole('searchbox'); - it('does not select a row if they are all disabled', () => { - const wrapper = mount(); + userEvent.type(filterInput, 'Field 3'); + userEvent.click(selectAllCheckbox); - const nextRows = [ - ...mockProps.rows.map((row) => ({ ...row, disabled: true })), - ]; + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(0); + }); + }); - wrapper.setProps({ rows: nextRows }); + describe('selection -- radio buttons', () => { + let mockProps; - getSelectAll(wrapper).simulate('click'); + beforeEach(() => { + mockProps = { + rows: [ + { + id: 'b', + fieldA: 'Field 2:A', + fieldB: 'Field 2:B', + }, + { + id: 'a', + fieldA: 'Field 1:A', + fieldB: 'Field 1:B', + }, + { + id: 'c', + fieldA: 'Field 3:A', + fieldB: 'Field 3:B', + }, + ], + headers: [ + { + key: 'fieldA', + header: 'Field A', + }, + { + key: 'fieldB', + header: 'Field B', + }, + ], + locale: 'en', + radio: true, + render: jest.fn( + ({ rows, headers, getHeaderProps, getSelectionProps }) => ( + + + + + {headers.map((header, i) => ( + + {header.header} + + ))} + + + + {rows.map((row) => ( + + + {row.cells.map((cell) => ( + {cell.value} + ))} + + ))} + +
+
+ ) + ), + }; + }); - expect(wrapper.find('TableSelectAll').prop('indeterminate')).toBe(false); - expect(wrapper.find('TableSelectAll').prop('checked')).toBe(false); + it('should render', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); - const filterInput = getFilterInput(wrapper); + it('should not have select-all checkbox', () => { + const { container } = render(); + expect(screen.queryAllByRole('checkbox').length).toBe(0); + expect(container).toMatchSnapshot(); + }); - filterInput.getDOMNode().value = 'Field 3'; - filterInput.simulate('change'); + it('should select a specific row when a user interacts with select row', () => { + render(); + const radioButton = screen.getAllByRole('radio')[0]; - getSelectAll(wrapper).simulate('click'); + expect(radioButton).not.toBeChecked(); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(0); - }); - }); + userEvent.click(radioButton); + expect(radioButton).toBeChecked(); - describe('selection -- radio buttons', () => { - let mockProps; + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(1); + }); - beforeEach(() => { - mockProps = { - rows: [ - { - id: 'b', - fieldA: 'Field 2:A', - fieldB: 'Field 2:B', - }, - { - id: 'a', - fieldA: 'Field 1:A', - fieldB: 'Field 1:B', - }, - { - id: 'c', - fieldA: 'Field 3:A', - fieldB: 'Field 3:B', - }, - ], - headers: [ - { - key: 'fieldA', - header: 'Field A', - }, - { - key: 'fieldB', - header: 'Field B', - }, - ], - locale: 'en', - radio: true, - render: jest.fn( - ({ rows, headers, getHeaderProps, getSelectionProps }) => ( - - - - - {headers.map((header, i) => ( - - {header.header} - - ))} - - - - {rows.map((row) => ( - - - {row.cells.map((cell) => ( - {cell.value} - ))} - - ))} - -
-
- ) - ), - }; - }); + it('should deselect all other rows when a row is selected', () => { + render(); + const radioButtonOne = screen.getAllByRole('radio')[0]; + const radioButtonTwo = screen.getAllByRole('radio')[1]; - it('should render', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); - }); + expect(radioButtonOne).not.toBeChecked(); - it('should not have select-all checkbox', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); - }); + userEvent.click(radioButtonOne); - it('should select a specific row when a user interacts with select row', () => { - const wrapper = mount(); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(false); + expect(radioButtonOne).toBeChecked(); + expect(radioButtonTwo).not.toBeChecked(); - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).simulate( - 'click' - ); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(true); + userEvent.click(radioButtonTwo); + expect(radioButtonOne).not.toBeChecked(); + expect(radioButtonTwo).toBeChecked(); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(1); + const { selectedRows } = getLastCallFor(mockProps.render)[0]; + expect(selectedRows.length).toBe(1); + }); }); - it('should deselect all other rows when a row is selected', () => { - const wrapper = mount(); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(false); + describe('updates properly when passed new props', () => { + let mockProps; - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).simulate( - 'click' - ); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(true); - expect( - getInputAtIndex({ wrapper, index: 1, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(false); - - getInputAtIndex({ wrapper, index: 1, inputType: 'radio' }).simulate( - 'click' - ); - expect( - getInputAtIndex({ wrapper, index: 0, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(false); - expect( - getInputAtIndex({ wrapper, index: 1, inputType: 'radio' }).prop( - 'checked' - ) - ).toBe(true); + beforeEach(() => { + mockProps = { + rows: [ + { + id: 'b', + fieldA: 'Field 2:A', + fieldB: 'Field 2:B', + }, + { + id: 'a', + fieldA: 'Field 1:A', + fieldB: 'Field 1:B', + }, + { + id: 'c', + fieldA: 'Field 3:A', + fieldB: 'Field 3:B', + }, + ], + headers: [ + { + key: 'fieldA', + header: 'Field A', + }, + { + key: 'fieldB', + header: 'Field B', + }, + ], + locale: 'en', + render: jest.fn( + ({ + rows, + headers, + getHeaderProps, + getExpandHeaderProps, + getSelectionProps, + getBatchActionProps, + getRowProps, + onInputChange, + }) => ( + + + + + Ghost + + + + + + + + + + {headers.map((header, i) => ( + + {header.header} + + ))} + + + + {rows.map((row) => ( + + + + {row.cells.map((cell) => ( + {cell.value} + ))} + + {row.isExpanded && ( + +

Expandable row content

+

Description here

+
+ )} +
+ ))} +
+
+
+ ) + ), + }; + }); - const { selectedRows } = getLastCallFor(mockProps.render)[0]; - expect(selectedRows.length).toBe(1); - }); - }); + it('should add additional rows when receiving new props', () => { + const { rerender } = render(); + const args = mockProps.render.mock.calls[0][0]; - describe('componentDidUpdate', () => { - let mockProps; + expect(args.rows.length).toEqual(mockProps.rows.length); - beforeEach(() => { - mockProps = { - rows: [ - { - id: 'b', - fieldA: 'Field 2:A', - fieldB: 'Field 2:B', - }, + const nextRows = [ + ...mockProps.rows, { - id: 'a', - fieldA: 'Field 1:A', - fieldB: 'Field 1:B', + id: 'd', + fieldA: 'Field 4:A', + fieldB: 'Field 4:B', }, - { - id: 'c', - fieldA: 'Field 3:A', - fieldB: 'Field 3:B', - }, - ], - headers: [ - { - key: 'fieldA', - header: 'Field A', - }, - { - key: 'fieldB', - header: 'Field B', - }, - ], - locale: 'en', - render: jest.fn( - ({ - rows, - headers, - getHeaderProps, - getExpandHeaderProps, - getSelectionProps, - getBatchActionProps, - getRowProps, - onInputChange, - }) => ( - - - - Ghost - - - - - - - - - {headers.map((header, i) => ( - - {header.header} - - ))} - - - - {rows.map((row) => ( - - - - {row.cells.map((cell) => ( - {cell.value} - ))} - - {row.isExpanded && ( - -

Expandable row content

-

Description here

-
- )} -
- ))} -
-
-
- ) - ), - }; - }); - - it('should add additional rows when receiving new props', () => { - const wrapper = mount(); - const args = mockProps.render.mock.calls[0][0]; - - expect(args.rows.length).toEqual(mockProps.rows.length); - - const nextRows = [ - ...mockProps.rows, - { - id: 'd', - fieldA: 'Field 4:A', - fieldB: 'Field 4:B', - }, - ]; + ]; + + rerender(); + + const nextArgs = getLastCallFor(mockProps.render)[0]; + expect(nextArgs.rows.length).toBe(nextRows.length); + expect(nextArgs.rows.map((row) => row.id)).toEqual([ + 'b', + 'a', + 'c', + 'd', + ]); + }); - wrapper.setProps({ rows: nextRows }); + it('should add additional headers when receiving new props', () => { + const { rerender } = render(); + const args = mockProps.render.mock.calls[0][0]; + + expect(args.headers).toEqual(mockProps.headers); + + const nextProps = { + rows: mockProps.rows.map((row) => ({ + ...row, + fieldC: 'Field X:C', + })), + headers: [ + ...mockProps.headers, + { + key: 'fieldC', + header: 'Field C', + }, + ], + }; - const nextArgs = getLastCallFor(mockProps.render)[0]; - expect(nextArgs.rows.length).toBe(nextRows.length); - expect(nextArgs.rows.map((row) => row.id)).toEqual(['b', 'a', 'c', 'd']); - }); + rerender(); - it('should add additional headers when receiving new props', () => { - const wrapper = mount(); - const args = mockProps.render.mock.calls[0][0]; + const nextArgs = getLastCallFor(mockProps.render)[0]; + expect(nextArgs.headers).toEqual(nextProps.headers); + }); - expect(args.headers).toEqual(mockProps.headers); + it('should keep batch action after adding rows, as long as some existing rows are selected', () => { + const { rerender } = render(); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; + userEvent.click(selectAllCheckbox); - const nextProps = { - rows: mockProps.rows.map((row) => ({ - ...row, - fieldC: 'Field X:C', - })), - headers: [ - ...mockProps.headers, + const nextRows = [ + ...mockProps.rows.map((row) => ({ ...row, isSelected: true })), { - key: 'fieldC', - header: 'Field C', + id: 'd', + fieldA: 'Field 4:A', + fieldB: 'Field 4:B', + isSelected: false, }, - ], - }; - - wrapper.setProps(nextProps); + ]; - const nextArgs = getLastCallFor(mockProps.render)[0]; - expect(nextArgs.headers).toEqual(nextProps.headers); - }); - - it('should keep batch action after adding rows, as long as some existing rows are selected', () => { - const wrapper = mount(); - getSelectAll(wrapper).simulate('click'); + rerender(); - const nextRows = [ - ...mockProps.rows.map((row) => ({ ...row, isSelected: true })), - { - id: 'd', - fieldA: 'Field 4:A', - fieldB: 'Field 4:B', - isSelected: false, - }, - ]; + expect(selectAllCheckbox).not.toBeChecked(); + const { getBatchActionProps, selectedRows } = getLastCallFor( + mockProps.render + )[0]; + expect(getBatchActionProps().shouldShowBatchActions).toBe(true); + expect(selectedRows.length).toBe(3); + }); - wrapper.setProps({ rows: nextRows }); - wrapper.update(); + it('should keep selected all state after adding rows, as long as all existing rows and new row are selected', () => { + const { rerender } = render(); + const selectAllCheckbox = screen.getAllByRole('checkbox')[0]; + userEvent.click(selectAllCheckbox); - expect(getSelectAll(wrapper).prop('checked')).toBe(false); - const { getBatchActionProps, selectedRows } = getLastCallFor( - mockProps.render - )[0]; - expect(getBatchActionProps().shouldShowBatchActions).toBe(true); - expect(selectedRows.length).toBe(3); - }); + const nextRows = [ + ...mockProps.rows, + { + id: 'd', + fieldA: 'Field 4:A', + fieldB: 'Field 4:B', + }, + ]; - it('should keep selected all state after adding rows, as long as all existing rows and new row are selected', () => { - const wrapper = mount(); - getSelectAll(wrapper).simulate('click'); + rerender(); - const nextRows = [ - ...mockProps.rows, - { - id: 'd', - fieldA: 'Field 4:A', - fieldB: 'Field 4:B', - }, - ]; + const { getBatchActionProps, selectedRows } = getLastCallFor( + mockProps.render + )[0]; + expect(getBatchActionProps().shouldShowBatchActions).toBe(true); + expect(selectedRows.length).toBe(3); + }); - wrapper.setProps({ rows: nextRows }); + it('should update rows when receiving new props', () => { + const { rerender } = render(); + const args = mockProps.render.mock.calls[0][0]; - const { getBatchActionProps, selectedRows } = getLastCallFor( - mockProps.render - )[0]; - expect(getBatchActionProps().shouldShowBatchActions).toBe(true); - expect(selectedRows.length).toBe(3); - }); + expect(args.rows.length).toEqual(mockProps.rows.length); - it('should update rows when receiving new props', () => { - const wrapper = mount(); - const args = mockProps.render.mock.calls[0][0]; + const nextRows = mockProps.rows.slice().reverse(); - expect(args.rows.length).toEqual(mockProps.rows.length); + rerender(); - const nextRows = mockProps.rows.slice().reverse(); + const nextArgs = getLastCallFor(mockProps.render)[0]; + expect(nextArgs.rows.map((row) => row.id)).toEqual(['c', 'a', 'b']); + }); - wrapper.setProps({ rows: nextRows }); + it('should update cells when receiving new props', () => { + const { rerender } = render(); + const args = mockProps.render.mock.calls[0][0]; - const nextArgs = getLastCallFor(mockProps.render)[0]; - expect(nextArgs.rows.map((row) => row.id)).toEqual(['c', 'a', 'b']); - }); + expect(args.rows.length).toEqual(mockProps.rows.length); - it('should update cells when receiving new props', () => { - const wrapper = mount(); - const args = mockProps.render.mock.calls[0][0]; + const nextRows = mockProps.rows.map((row) => { + return { + ...row, + fieldA: row.fieldA + '!', + }; + }); - expect(args.rows.length).toEqual(mockProps.rows.length); + rerender(); - const nextRows = mockProps.rows.map((row) => { - return { - ...row, - fieldA: row.fieldA + '!', - }; + const nextArgs = getLastCallFor(mockProps.render)[0]; + expect(nextArgs.rows.map((row) => row.cells[0].value)).toEqual([ + 'Field 2:A!', + 'Field 1:A!', + 'Field 3:A!', + ]); }); - - wrapper.setProps({ rows: nextRows }); - - const nextArgs = getLastCallFor(mockProps.render)[0]; - expect(nextArgs.rows.map((row) => row.cells[0].value)).toEqual([ - 'Field 2:A!', - 'Field 1:A!', - 'Field 3:A!', - ]); - }); - }); - - describe('sticky header', () => { - it('should render', () => { - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); }); }); }); diff --git a/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap b/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap index d6a506c787c8..6ec2b0a52cd5 100644 --- a/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap +++ b/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap @@ -1,3863 +1,1065 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DataTable selection -- radio buttons should not have select-all checkbox 1`] = ` - - - - - - Field A - - - Field B - - - - -
- , - }, - ], - } - } - rows={Array []} - size="lg" - sortRow={[Function]} - translateWithId={[Function]} -> - +
-
-

- DataTable with selection -

-

-

- +

+ +

+
-
-
- - - - - - - - - - - - - - - + + - -
-
- Field A -
-
-
- Field B -
-
-
-
- -
-
-
-`; - -exports[`DataTable selection -- radio buttons should render 1`] = ` - - + Field A + + + + + + + + + + + + + + + + + +
- - - - Field A - - - Field B - - - - - - +
+
+ + +
+
+ Field 2:A + + Field 2:B +
+
+ + +
+
+ Field 1:A + + Field 1:B +
+
+ +
- , - }, - ], - } - } - rows={ - Array [ - Object { - "fieldA": "Field 2:A", - "fieldB": "Field 2:B", - "id": "b", - }, - Object { - "fieldA": "Field 1:A", - "fieldB": "Field 1:B", - "id": "a", - }, - Object { - "fieldA": "Field 3:A", - "fieldB": "Field 3:B", - "id": "c", - }, - ] - } - size="lg" - sortRow={[Function]} - translateWithId={[Function]} -> - + Select row + + + + + + Field 3:A + + + Field 3:B + + + + + + + +`; + +exports[`DataTable behaves as expected selection -- radio buttons should render 1`] = ` +
+
-
-

- DataTable with selection -

-

-

- +

+ +

+
-
+
+ + + + + -
+
+ Field A +
+
+
+ Field B +
+
- - - - - - - - - - - - - - - + - + - - - - - - - - - - - - + + Select row + + + + + + + + + - - - - - - - - - - - - + + Select row + + + + + + + + + - - - - - - - - - - - - - -
-
- Field A -
-
-
- Field B -
-
-
- -
- - -
-
-
- Field 2:A - - Field 2:B -
+ Field 2:A + + Field 2:B +
+
+ +
- -
- - -
-
-
- Field 1:A - - Field 1:B -
+ Field 1:A + + Field 1:B +
+
+ +
- -
- - -
-
-
- Field 3:A - - Field 3:B -
-
- + + + Select row + + +
+ + + Field 3:A + + + Field 3:B + + + +
-
-
+ + `; -exports[`DataTable selection should have select-all default to un-checked if no rows are present 1`] = ` - - - - - - - Field A - - - Field B - - - - -
- , - }, - ], - } - } - rows={Array []} - size="lg" - sortRow={[Function]} - translateWithId={[Function]} -> - +
+

+ DataTable with selection +

+

+

+
- +

+ + 0 items selected + +

+
-
- - - - - - - - - - - - - - - - - - - -
- -
- -
-
-
-
- Field A -
-
-
- Field B -
-
-
- - -
-
-`; - -exports[`DataTable selection should render 1`] = ` - + + + + + + -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
-
-
-
- Field A -
-
-
- Field B -
-
- -
- -
-
-
- Field 2:A - - Field 2:B -
- -
- -
-
-
- Field 1:A - - Field 1:B -
- -
- -
-
-
- Field 3:A - - Field 3:B -
+ + + + + + - - - -
-`; - -exports[`DataTable should render 1`] = ` - + - - + + +
- , - }, - ], - } - } - rows={ - Array [ - Object { - "fieldA": "Field 2:A", - "fieldB": "Field 2:B", - "id": "b", - }, - Object { - "fieldA": "Field 1:A", - "fieldB": "Field 1:B", - "id": "a", - }, - Object { - "fieldA": "Field 3:A", - "fieldB": "Field 3:B", - "id": "c", - }, - ] - } - size="lg" - sortRow={[Function]} - translateWithId={[Function]} -> - -
-
-

+ + + + +

-

+ Add new +

- +
+ -
- -
+
+ +
-

- - - 0 items selected - - -

+ +
- -
- - - - - - - - - - - - - - -
-
- - - -
+
- - -
-
- - - - - - - - - -
- - - -
-
-
- - - - - - - - - - - - - - - - - - - - - -
+
- - -
- - - - -
+ + + +
-
- - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- Field A -
-
-
- Field B -
-
-
- Field 2:A - - Field 2:B -
- Field 1:A - - Field 1:B -
- Field 3:A - - Field 3:B -
- -
-
- - -`; - -exports[`DataTable sticky header should render 1`] = ` - - +
+ + + Field 2:A + + + Field 2:B + + + + - - - Ghost - - - Ghost - - - Ghost - - - - - - - Action 1 - - - Action 2 - - - Action 3 - - - - - - + + + + + + +
+ Field 1:A + + Field 1:B +
- - - - Field A - - - Field B - - - - - - - Field 2:A - - - Field 2:B - - - - - Field 1:A - - - Field 1:B - - - - - Field 3:A - - - Field 3:B - - - -
-
, - }, - ], - } - } - rows={ - Array [ - Object { - "fieldA": "Field 2:A", - "fieldB": "Field 2:B", - "id": "b", - }, - Object { - "fieldA": "Field 1:A", - "fieldB": "Field 1:B", - "id": "a", - }, - Object { - "fieldA": "Field 3:A", - "fieldB": "Field 3:B", - "id": "c", - }, - ] - } - size="lg" - sortRow={[Function]} - translateWithId={[Function]} -> - + -
+ + `;