diff --git a/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceList.jsx b/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceList.jsx index ea705e1b93..e66a13457e 100644 --- a/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceList.jsx +++ b/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceList.jsx @@ -5,9 +5,11 @@ import { useLocation, useParams } from 'react-router-dom'; import 'styled-components/macro'; import DataListToolbar from '../../../components/DataListToolbar'; -import PaginatedDataList, { - ToolbarAddButton, -} from '../../../components/PaginatedDataList'; +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; +import { ToolbarAddButton } from '../../../components/PaginatedDataList'; import DisassociateButton from '../../../components/DisassociateButton'; import AssociateModal from '../../../components/AssociateModal'; import AlertModal from '../../../components/AlertModal'; @@ -73,9 +75,13 @@ function InstanceList() { } ); - const { selected, isAllSelected, handleSelect, setSelected } = useSelected( - instances - ); + const { + selected, + isAllSelected, + handleSelect, + clearSelected, + selectAll, + } = useSelected(instances); useEffect(() => { fetchInstances(); @@ -116,7 +122,7 @@ function InstanceList() { const handleDisassociate = async () => { await disassociateInstances(); - setSelected([]); + clearSelected(); }; const { error, dismissError } = useDismissableError( @@ -140,14 +146,14 @@ function InstanceList() { return ( <> - - setSelected(isSelected ? [...instances] : []) - } + onSelectAll={selectAll} qsConfig={QS_CONFIG} additionalControls={[ ...(canAdd @@ -200,7 +204,18 @@ function InstanceList() { } /> )} - renderItem={instance => ( + headerRow={ + + {t`Name`} + {t`Type`} + {t`Running Jobs`} + {t`Total Jobs`} + {t`Capacity Adjustment`} + {t`Used Capacity`} + {t`Actions`} + + } + renderRow={(instance, index) => ( handleSelect(instance)} isSelected={selected.some(row => row.id === instance.id)} fetchInstances={fetchInstances} + rowIndex={index} /> )} /> diff --git a/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceListItem.jsx b/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceListItem.jsx index b6aaa3da88..0d35176362 100644 --- a/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceListItem.jsx +++ b/awx/ui_next/src/screens/InstanceGroup/Instances/InstanceListItem.jsx @@ -4,19 +4,14 @@ import { t, Plural } from '@lingui/macro'; import styled from 'styled-components'; import 'styled-components/macro'; import { - Badge as PFBadge, Progress, ProgressMeasureLocation, ProgressSize, - DataListAction as PFDataListAction, - DataListCheck, - DataListItem as PFDataListItem, - DataListItemRow as PFDataListItemRow, - DataListItemCells as PFDataListItemCells, Slider, } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; -import _DataListCell from '../../../components/DataListCell'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import InstanceToggle from '../../../components/InstanceToggle'; import { Instance } from '../../../types'; import useRequest, { useDismissableError } from '../../../util/useRequest'; @@ -26,44 +21,10 @@ import { useConfig } from '../../../contexts/Config'; import AlertModal from '../../../components/AlertModal'; import ErrorDetail from '../../../components/ErrorDetail'; -const DataListItem = styled(PFDataListItem)` - display: flex; - flex-direction: column; - justify-content: center; -`; - -const DataListItemRow = styled(PFDataListItemRow)` - align-items: center; -`; - -const DataListItemCells = styled(PFDataListItemCells)` - align-items: center; -`; - -const DataListAction = styled(PFDataListAction)` - align-items: center; -`; const Unavailable = styled.span` color: var(--pf-global--danger-color--200); `; -const DataListCell = styled(_DataListCell)` - white-space: nowrap; - align-items: center; -`; - -const Badge = styled(PFBadge)` - margin-left: 8px; -`; - -const ListGroup = styled.span` - margin-left: 12px; - - &:first-of-type { - margin-left: 0; - } -`; - const SliderHolder = styled.div` display: flex; align-items: center; @@ -86,7 +47,13 @@ function computeForks(memCapacity, cpuCapacity, selectedCapacityAdjustment) { ); } -function InstanceListItem({ instance, isSelected, onSelect, fetchInstances }) { +function InstanceListItem({ + instance, + isSelected, + onSelect, + fetchInstances, + rowIndex, +}) { const { me = {} } = useConfig(); const [forks, setForks] = useState( computeForks( @@ -137,92 +104,64 @@ function InstanceListItem({ instance, isSelected, onSelect, fetchInstances }) { return ( <> - - - - - - {instance.hostname} - , - - {t`Type`} - - {instance.managed_by_policy ? t`Auto` : t`Manual`} - - , - - - {t`Running jobs`} - {instance.jobs_running} - - - {t`Total jobs`} - {instance.jobs_total} - - , - - -
{t`CPU ${instance.cpu_capacity}`}
- -
- -
- -
- -
{t`RAM ${instance.mem_capacity}`}
-
-
, - - - {usedCapacity(instance)} - , - ]} - /> - + + + + {instance.hostname} + + + {instance.managed_by_policy ? t`Auto` : t`Manual`} + + {instance.jobs_running} + {instance.jobs_total} + + +
{t`CPU ${instance.cpu_capacity}`}
+ +
+ +
+ +
+
{t`RAM ${instance.mem_capacity}`}
+
+ + + {usedCapacity(instance)} + + + -
-
-
+ + + {updateError && ( ', () => { test('should mount successfully', async () => { await act(async () => { wrapper = mountWithContexts( - {}} - fetchInstances={() => {}} - /> + + + {}} + fetchInstances={() => {}} + /> + +
); }); expect(wrapper.find('InstanceListItem').length).toBe(1); @@ -63,12 +67,16 @@ describe('', () => { test('should calculate number of forks when slide changes', async () => { await act(async () => { wrapper = mountWithContexts( - {}} - fetchInstances={() => {}} - /> + + + {}} + fetchInstances={() => {}} + /> + +
); }); expect(wrapper.find('InstanceListItem').length).toBe(1); @@ -105,30 +113,41 @@ describe('', () => { test('should render the proper data instance', async () => { await act(async () => { wrapper = mountWithContexts( - {}} - fetchInstances={() => {}} - /> + + + {}} + fetchInstances={() => {}} + /> + +
); }); expect( - wrapper.find('PFDataListCell[aria-label="instance host name"]').text() + wrapper + .find('Td') + .at(1) + .text() ).toBe('awx'); expect(wrapper.find('Progress').prop('value')).toBe(40); expect( - wrapper.find('PFDataListCell[aria-label="instance type"]').text() - ).toBe('TypeAuto'); - expect(wrapper.find('input#instances-1').prop('checked')).toBe(false); + wrapper + .find('Td') + .at(2) + .text() + ).toBe('Auto'); expect( wrapper - .find('PFDataListCell[aria-label="capacity adjustment"]') + .find('Td') + .at(5) .containsMatchingElement(
CPU 24
) ); expect( wrapper - .find('PFDataListCell[aria-label="capacity adjustment"]') + .find('Td') + .at(5) .containsMatchingElement(
RAM 24
) ); expect(wrapper.find('InstanceListItem__SliderForks').text()).toContain( @@ -136,18 +155,27 @@ describe('', () => { ); }); - test('should be checked', async () => { + test('should render checkbox', async () => { + const onSelect = jest.fn(); await act(async () => { wrapper = mountWithContexts( - {}} - fetchInstances={() => {}} - /> + + + {}} + /> + +
); }); - expect(wrapper.find('input#instances-1').prop('checked')).toBe(true); + expect( + wrapper + .find('Td') + .first() + .prop('select').onSelect + ).toEqual(onSelect); }); test('should display instance toggle', () => { @@ -176,12 +204,16 @@ describe('', () => { ); await act(async () => { wrapper = mountWithContexts( - {}} - fetchInstances={() => {}} - />, + + + {}} + fetchInstances={() => {}} + /> + +
, { context: { network: { handleHttpError: () => {} } } } ); });