From 0b4a2961814a7bd14837f4e03e74e574f2452b20 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Wed, 9 Jun 2021 16:48:00 -0700 Subject: [PATCH] convert ApplicationTokenList, HostFilterLookup to tables --- .../CheckboxListItem/CheckboxListItem.jsx | 2 + .../components/Lookup/HostFilterLookup.jsx | 32 ++----- .../src/components/Lookup/HostListItem.jsx | 36 ++------ .../components/Lookup/HostListItem.test.jsx | 24 +++-- .../ApplicationTokenList.jsx | 60 ++++++------- .../ApplicationTokenList.test.jsx | 56 ++++++++---- .../ApplicationTokenListItem.jsx | 68 ++++++-------- .../ApplicationTokenListItem.test.jsx | 88 +++++++++++++------ .../InventoryGroupHostListItem.test.jsx | 40 +++++---- 9 files changed, 205 insertions(+), 201 deletions(-) diff --git a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx index c76d9af0d1..9671157f73 100644 --- a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx +++ b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx @@ -1,3 +1,4 @@ +import 'styled-components/macro'; import React from 'react'; import PropTypes from 'prop-types'; import { t } from '@lingui/macro'; @@ -28,6 +29,7 @@ const CheckboxListItem = ({ ouiaId={`list-item-${itemId}`} id={`list-item-${itemId}`} onClick={handleRowClick} + css="cursor: default" > - {}} pluralizedItemName={t`hosts`} qsConfig={QS_CONFIG} - renderItem={item => ( - - )} + headerRow={ + + {t`Name`} + {t`Inventory`} + + } + renderRow={item => } renderToolbar={props => ( )} toolbarSearchColumns={searchColumns} - toolbarSortColumns={[ - { - name: t`Name`, - key: 'name', - }, - { - name: t`Created`, - key: 'created', - }, - { - name: t`Modified`, - key: 'modified', - }, - ]} toolbarSearchableKeys={searchableKeys} toolbarRelatedSearchableKeys={relatedSearchableKeys} /> diff --git a/awx/ui_next/src/components/Lookup/HostListItem.jsx b/awx/ui_next/src/components/Lookup/HostListItem.jsx index 1fc10326a3..1e32c788cc 100644 --- a/awx/ui_next/src/components/Lookup/HostListItem.jsx +++ b/awx/ui_next/src/components/Lookup/HostListItem.jsx @@ -1,39 +1,13 @@ import React from 'react'; -import { Link } from 'react-router-dom'; - import { t } from '@lingui/macro'; -import { - DataListItem, - DataListItemRow, - DataListItemCells, - TextContent, -} from '@patternfly/react-core'; -import DataListCell from '../DataListCell'; +import { Td, Tr } from '@patternfly/react-table'; function HostListItem({ item }) { return ( - - - - - - {item.name} - - - , - - {item.summary_fields.inventory.name} - , - ]} - /> - - + + {item.name} + {item.summary_fields.inventory.name} + ); } diff --git a/awx/ui_next/src/components/Lookup/HostListItem.test.jsx b/awx/ui_next/src/components/Lookup/HostListItem.test.jsx index 7e6fdbb16b..1d18fbdbe5 100644 --- a/awx/ui_next/src/components/Lookup/HostListItem.test.jsx +++ b/awx/ui_next/src/components/Lookup/HostListItem.test.jsx @@ -15,11 +15,25 @@ describe('HostListItem', () => { }, }; test('initially renders successfully', () => { - wrapper = mountWithContexts(); - expect(wrapper.find('HostListItem').length).toBe(1); - expect(wrapper.find('DataListCell[aria-label="name"]').text()).toBe('Foo'); - expect(wrapper.find('DataListCell[aria-label="inventory"]').text()).toBe( - 'Bar' + wrapper = mountWithContexts( + + + + +
); + expect(wrapper.find('HostListItem').length).toBe(1); + expect( + wrapper + .find('Td') + .at(0) + .text() + ).toBe('Foo'); + expect( + wrapper + .find('Td') + .at(1) + .text() + ).toBe('Bar'); }); }); diff --git a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.jsx b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.jsx index 02f17863a4..b7066e4c39 100644 --- a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.jsx +++ b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.jsx @@ -2,9 +2,11 @@ import React, { useCallback, useEffect } from 'react'; import { useParams, useLocation } from 'react-router-dom'; import { t } from '@lingui/macro'; -import PaginatedDataList, { - ToolbarDeleteButton, -} from '../../../components/PaginatedDataList'; +import PaginatedTable, { + HeaderCell, + HeaderRow, +} from '../../../components/PaginatedTable'; +import { ToolbarDeleteButton } from '../../../components/PaginatedDataList'; import { getQSConfig, parseQueryString } from '../../../util/qs'; import { TokensAPI, ApplicationsAPI } from '../../../api'; import ErrorDetail from '../../../components/ErrorDetail'; @@ -67,9 +69,13 @@ function ApplicationTokenList() { fetchTokens(); }, [fetchTokens]); - const { selected, isAllSelected, handleSelect, setSelected } = useSelected( - tokens - ); + const { + selected, + isAllSelected, + handleSelect, + selectAll, + clearSelected, + } = useSelected(tokens); const { isLoading: deleteLoading, deletionError, @@ -90,19 +96,18 @@ function ApplicationTokenList() { const handleDelete = async () => { await handleDeleteApplications(); - setSelected([]); + clearSelected(); }; return ( <> - ( @@ -139,9 +123,7 @@ function ApplicationTokenList() { {...props} showSelectAll isAllSelected={isAllSelected} - onSelectAll={isSelected => - setSelected(isSelected ? [...tokens] : []) - } + onSelectAll={selectAll} qsConfig={QS_CONFIG} additionalControls={[ )} - renderItem={token => ( + headerRow={ + + {t`Name`} + {t`Scope`} + {t`Expires`} + + } + renderRow={(token, index) => ( handleSelect(token)} isSelected={selected.some(row => row.id === token.id)} + rowIndex={index} /> )} /> diff --git a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.test.jsx b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.test.jsx index ccc9c5f4cc..67d7d2e4c7 100644 --- a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.test.jsx +++ b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenList.test.jsx @@ -1,10 +1,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; -import { - mountWithContexts, - waitForElement, -} from '../../../../testUtils/enzymeHelpers'; +import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import { ApplicationsAPI, TokensAPI } from '../../../api'; import ApplicationTokenList from './ApplicationTokenList'; @@ -100,14 +97,16 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); - await waitForElement(wrapper, 'ApplicationTokenList', el => el.length > 0); + wrapper.update(); + expect(wrapper.find('ApplicationTokenList')).toHaveLength(1); }); + test('should have data fetched and render 2 rows', async () => { ApplicationsAPI.readTokens.mockResolvedValue(tokens); await act(async () => { wrapper = mountWithContexts(); }); - await waitForElement(wrapper, 'ApplicationTokenList', el => el.length > 0); + wrapper.update(); expect(wrapper.find('ApplicationTokenListItem').length).toBe(2); expect(ApplicationsAPI.readTokens).toBeCalled(); }); @@ -117,15 +116,22 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); - waitForElement(wrapper, 'ApplicationTokenList', el => el.length > 0); - - wrapper - .find('input#select-token-2') - .simulate('change', tokens.data.results[0]); - wrapper.update(); - expect(wrapper.find('input#select-token-2').prop('checked')).toBe(true); + wrapper + .find('.pf-c-table__check') + .at(0) + .find('input') + .simulate('change', tokens.data.results[0]); + wrapper.update(); + + expect( + wrapper + .find('.pf-c-table__check') + .at(0) + .find('input') + .prop('checked') + ).toBe(true); await act(async () => wrapper.find('Button[aria-label="Delete"]').prop('onClick')() ); @@ -153,8 +159,8 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); + wrapper.update(); - await waitForElement(wrapper, 'ApplicationTokenList', el => el.length > 0); expect(wrapper.find('ContentError').length).toBe(1); }); @@ -174,13 +180,23 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); - waitForElement(wrapper, 'ApplicationTokenList', el => el.length > 0); + wrapper.update(); - wrapper.find('input#select-token-2').simulate('change', 'a'); + wrapper + .find('.pf-c-table__check') + .at(0) + .find('input') + .simulate('change', 'a'); wrapper.update(); - expect(wrapper.find('input#select-token-2').prop('checked')).toBe(true); + expect( + wrapper + .find('.pf-c-table__check') + .at(0) + .find('input') + .prop('checked') + ).toBe(true); await act(async () => wrapper.find('Button[aria-label="Delete"]').prop('onClick')() ); @@ -191,7 +207,9 @@ describe('', () => { wrapper.find('Button[aria-label="confirm delete"]').prop('onClick')() ); wrapper.update(); - expect(wrapper.find('ErrorDetail').length).toBe(1); + + expect(!!wrapper.find('AlertModal').prop('isOpen')).toEqual(true); + expect(wrapper.find('ErrorDetail')).toHaveLength(1); }); test('should not render add button', async () => { @@ -200,7 +218,7 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); - waitForElement(wrapper, 'ApplicationTokenList', el => el.length > 0); + wrapper.update(); expect(wrapper.find('ToolbarAddButton').length).toBe(0); }); }); diff --git a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.jsx b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.jsx index 7a32738f83..cff37b2d92 100644 --- a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.jsx +++ b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.jsx @@ -1,55 +1,36 @@ import React from 'react'; -import { string, bool, func } from 'prop-types'; - +import { string, bool, func, number } from 'prop-types'; import { t } from '@lingui/macro'; import { Link } from 'react-router-dom'; -import { - DataListCheck, - DataListItem, - DataListItemCells, - DataListItemRow, -} from '@patternfly/react-core'; -import styled from 'styled-components'; +import { Tr, Td } from '@patternfly/react-table'; import { Token } from '../../../types'; import { formatDateString } from '../../../util/dates'; import { toTitleCase } from '../../../util/strings'; -import DataListCell from '../../../components/DataListCell'; -const Label = styled.b` - margin-right: 20px; -`; - -function ApplicationTokenListItem({ token, isSelected, onSelect, detailUrl }) { - const labelId = `check-action-${token.id}`; +function ApplicationTokenListItem({ + token, + isSelected, + onSelect, + detailUrl, + rowIndex, +}) { return ( - - - - - - {token.summary_fields.user.username} - - , - - - {toTitleCase(token.scope)} - , - - - {formatDateString(token.expires)} - , - ]} - /> - - + + + + {token.summary_fields.user.username} + + {toTitleCase(token.scope)} + {formatDateString(token.expires)} + ); } @@ -58,6 +39,7 @@ ApplicationTokenListItem.propTypes = { detailUrl: string.isRequired, isSelected: bool.isRequired, onSelect: func.isRequired, + rowIndex: number.isRequired, }; export default ApplicationTokenListItem; diff --git a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.test.jsx b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.test.jsx index 94d0355951..78c0fe1d14 100644 --- a/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.test.jsx +++ b/awx/ui_next/src/screens/Application/ApplicationTokens/ApplicationTokenListItem.test.jsx @@ -42,49 +42,79 @@ describe('', () => { test('should mount successfully', async () => { await act(async () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + rowIndex={1} + /> + +
); }); expect(wrapper.find('ApplicationTokenListItem').length).toBe(1); }); + test('should render the proper data', async () => { await act(async () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + rowIndex={1} + /> + +
); }); - expect(wrapper.find('DataListCell[aria-label="token name"]').text()).toBe( - 'admin' - ); - expect(wrapper.find('DataListCell[aria-label="scope"]').text()).toBe( - 'ScopeRead' - ); - expect(wrapper.find('DataListCell[aria-label="expiration"]').text()).toBe( - 'Expiration10/25/3019, 7:56:38 PM' - ); - expect(wrapper.find('input#select-token-2').prop('checked')).toBe(false); + expect( + wrapper + .find('Td') + .at(1) + .text() + ).toBe('admin'); + expect( + wrapper + .find('Td') + .at(2) + .text() + ).toBe('Read'); + expect( + wrapper + .find('Td') + .at(3) + .text() + ).toBe('10/25/3019, 7:56:38 PM'); }); + test('should be checked', async () => { await act(async () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + rowIndex={1} + /> + +
); }); - expect(wrapper.find('input#select-token-2').prop('checked')).toBe(true); + expect( + wrapper + .find('Td') + .at(0) + .prop('select').isSelected + ).toBe(true); }); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroupHosts/InventoryGroupHostListItem.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroupHosts/InventoryGroupHostListItem.test.jsx index c5225346fb..85ea79463e 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroupHosts/InventoryGroupHostListItem.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroupHosts/InventoryGroupHostListItem.test.jsx @@ -11,14 +11,18 @@ describe('', () => { beforeEach(() => { wrapper = mountWithContexts( - {}} - rowIndex={0} - /> + + + {}} + rowIndex={0} + /> + +
); }); @@ -42,14 +46,18 @@ describe('', () => { const copyMockHost = Object.assign({}, mockHost); copyMockHost.summary_fields.user_capabilities.edit = false; wrapper = mountWithContexts( - {}} - rowIndex={0} - /> + + + {}} + rowIndex={0} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); });