mirror of
https://github.com/ansible/awx.git
synced 2026-03-22 11:25:08 -02:30
convert InstanceList to table
This commit is contained in:
@@ -5,9 +5,11 @@ import { useLocation, useParams } from 'react-router-dom';
|
|||||||
import 'styled-components/macro';
|
import 'styled-components/macro';
|
||||||
|
|
||||||
import DataListToolbar from '../../../components/DataListToolbar';
|
import DataListToolbar from '../../../components/DataListToolbar';
|
||||||
import PaginatedDataList, {
|
import PaginatedTable, {
|
||||||
ToolbarAddButton,
|
HeaderRow,
|
||||||
} from '../../../components/PaginatedDataList';
|
HeaderCell,
|
||||||
|
} from '../../../components/PaginatedTable';
|
||||||
|
import { ToolbarAddButton } from '../../../components/PaginatedDataList';
|
||||||
import DisassociateButton from '../../../components/DisassociateButton';
|
import DisassociateButton from '../../../components/DisassociateButton';
|
||||||
import AssociateModal from '../../../components/AssociateModal';
|
import AssociateModal from '../../../components/AssociateModal';
|
||||||
import AlertModal from '../../../components/AlertModal';
|
import AlertModal from '../../../components/AlertModal';
|
||||||
@@ -73,9 +75,13 @@ function InstanceList() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
|
const {
|
||||||
instances
|
selected,
|
||||||
);
|
isAllSelected,
|
||||||
|
handleSelect,
|
||||||
|
clearSelected,
|
||||||
|
selectAll,
|
||||||
|
} = useSelected(instances);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchInstances();
|
fetchInstances();
|
||||||
@@ -116,7 +122,7 @@ function InstanceList() {
|
|||||||
|
|
||||||
const handleDisassociate = async () => {
|
const handleDisassociate = async () => {
|
||||||
await disassociateInstances();
|
await disassociateInstances();
|
||||||
setSelected([]);
|
clearSelected();
|
||||||
};
|
};
|
||||||
|
|
||||||
const { error, dismissError } = useDismissableError(
|
const { error, dismissError } = useDismissableError(
|
||||||
@@ -140,14 +146,14 @@ function InstanceList() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PaginatedDataList
|
<PaginatedTable
|
||||||
contentError={contentError}
|
contentError={contentError}
|
||||||
hasContentLoading={isLoading || isDisassociateLoading}
|
hasContentLoading={isLoading || isDisassociateLoading}
|
||||||
items={instances}
|
items={instances}
|
||||||
itemCount={count}
|
itemCount={count}
|
||||||
pluralizedItemName={t`Instances`}
|
pluralizedItemName={t`Instances`}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={handleSelect}
|
clearSelected={clearSelected}
|
||||||
toolbarSearchableKeys={searchableKeys}
|
toolbarSearchableKeys={searchableKeys}
|
||||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||||
toolbarSearchColumns={[
|
toolbarSearchColumns={[
|
||||||
@@ -168,9 +174,7 @@ function InstanceList() {
|
|||||||
{...props}
|
{...props}
|
||||||
showSelectAll
|
showSelectAll
|
||||||
isAllSelected={isAllSelected}
|
isAllSelected={isAllSelected}
|
||||||
onSelectAll={isSelected =>
|
onSelectAll={selectAll}
|
||||||
setSelected(isSelected ? [...instances] : [])
|
|
||||||
}
|
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
additionalControls={[
|
additionalControls={[
|
||||||
...(canAdd
|
...(canAdd
|
||||||
@@ -200,7 +204,18 @@ function InstanceList() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
renderItem={instance => (
|
headerRow={
|
||||||
|
<HeaderRow qsConfig={QS_CONFIG}>
|
||||||
|
<HeaderCell sortKey="name">{t`Name`}</HeaderCell>
|
||||||
|
<HeaderCell>{t`Type`}</HeaderCell>
|
||||||
|
<HeaderCell>{t`Running Jobs`}</HeaderCell>
|
||||||
|
<HeaderCell>{t`Total Jobs`}</HeaderCell>
|
||||||
|
<HeaderCell>{t`Capacity Adjustment`}</HeaderCell>
|
||||||
|
<HeaderCell>{t`Used Capacity`}</HeaderCell>
|
||||||
|
<HeaderCell>{t`Actions`}</HeaderCell>
|
||||||
|
</HeaderRow>
|
||||||
|
}
|
||||||
|
renderRow={(instance, index) => (
|
||||||
<InstanceListItem
|
<InstanceListItem
|
||||||
key={instance.id}
|
key={instance.id}
|
||||||
value={instance.hostname}
|
value={instance.hostname}
|
||||||
@@ -208,6 +223,7 @@ function InstanceList() {
|
|||||||
onSelect={() => handleSelect(instance)}
|
onSelect={() => handleSelect(instance)}
|
||||||
isSelected={selected.some(row => row.id === instance.id)}
|
isSelected={selected.some(row => row.id === instance.id)}
|
||||||
fetchInstances={fetchInstances}
|
fetchInstances={fetchInstances}
|
||||||
|
rowIndex={index}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,19 +4,14 @@ import { t, Plural } from '@lingui/macro';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import 'styled-components/macro';
|
import 'styled-components/macro';
|
||||||
import {
|
import {
|
||||||
Badge as PFBadge,
|
|
||||||
Progress,
|
Progress,
|
||||||
ProgressMeasureLocation,
|
ProgressMeasureLocation,
|
||||||
ProgressSize,
|
ProgressSize,
|
||||||
DataListAction as PFDataListAction,
|
|
||||||
DataListCheck,
|
|
||||||
DataListItem as PFDataListItem,
|
|
||||||
DataListItemRow as PFDataListItemRow,
|
|
||||||
DataListItemCells as PFDataListItemCells,
|
|
||||||
Slider,
|
Slider,
|
||||||
} from '@patternfly/react-core';
|
} 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 InstanceToggle from '../../../components/InstanceToggle';
|
||||||
import { Instance } from '../../../types';
|
import { Instance } from '../../../types';
|
||||||
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
||||||
@@ -26,44 +21,10 @@ import { useConfig } from '../../../contexts/Config';
|
|||||||
import AlertModal from '../../../components/AlertModal';
|
import AlertModal from '../../../components/AlertModal';
|
||||||
import ErrorDetail from '../../../components/ErrorDetail';
|
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`
|
const Unavailable = styled.span`
|
||||||
color: var(--pf-global--danger-color--200);
|
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`
|
const SliderHolder = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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 { me = {} } = useConfig();
|
||||||
const [forks, setForks] = useState(
|
const [forks, setForks] = useState(
|
||||||
computeForks(
|
computeForks(
|
||||||
@@ -137,92 +104,64 @@ function InstanceListItem({ instance, isSelected, onSelect, fetchInstances }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DataListItem
|
<Tr id={`instance-row-${instance.id}`}>
|
||||||
aria-labelledby={labelId}
|
<Td
|
||||||
id={`${instance.id}`}
|
select={{
|
||||||
key={instance.id}
|
rowIndex,
|
||||||
>
|
isSelected,
|
||||||
<DataListItemRow>
|
onSelect,
|
||||||
<DataListCheck
|
disable: false,
|
||||||
aria-labelledby={labelId}
|
}}
|
||||||
checked={isSelected}
|
dataLabel={t`Selected`}
|
||||||
id={`instances-${instance.id}`}
|
/>
|
||||||
onChange={onSelect}
|
<Td id={labelId} dataLabel={t`Name`}>
|
||||||
/>
|
{instance.hostname}
|
||||||
|
</Td>
|
||||||
<DataListItemCells
|
<Td dataLabel={t`Type`}>
|
||||||
dataListCells={[
|
{instance.managed_by_policy ? t`Auto` : t`Manual`}
|
||||||
<DataListCell key="name" aria-label={t`instance host name`}>
|
</Td>
|
||||||
<b>{instance.hostname}</b>
|
<Td dataLabel={t`Running Jobs`}>{instance.jobs_running}</Td>
|
||||||
</DataListCell>,
|
<Td dataLabel={t`Total Jobs`}>{instance.jobs_total}</Td>
|
||||||
<DataListCell key="type" aria-label={t`instance type`}>
|
<Td dataLabel={t`Capacity Adjustment`}>
|
||||||
<b css="margin-right: 24px">{t`Type`}</b>
|
<SliderHolder data-cy="slider-holder">
|
||||||
<span id={labelId}>
|
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div>
|
||||||
{instance.managed_by_policy ? t`Auto` : t`Manual`}
|
<SliderForks data-cy="slider-forks">
|
||||||
</span>
|
<div data-cy="number-forks">
|
||||||
</DataListCell>,
|
<Plural value={forks} one="# fork" other="# forks" />
|
||||||
<DataListCell
|
</div>
|
||||||
key="related-field-counts"
|
<Slider
|
||||||
aria-label={t`instance counts`}
|
areCustomStepsContinuous
|
||||||
width={3}
|
max={1}
|
||||||
>
|
min={0}
|
||||||
<ListGroup>
|
step={0.1}
|
||||||
<b>{t`Running jobs`}</b>
|
value={instance.capacity_adjustment}
|
||||||
<Badge isRead>{instance.jobs_running}</Badge>
|
onChange={handleChangeValue}
|
||||||
</ListGroup>
|
isDisabled={!me?.is_superuser || !instance.enabled}
|
||||||
<ListGroup>
|
data-cy="slider"
|
||||||
<b>{t`Total jobs`}</b>
|
/>
|
||||||
<Badge isRead>{instance.jobs_total}</Badge>
|
</SliderForks>
|
||||||
</ListGroup>
|
<div data-cy="mem-capacity">{t`RAM ${instance.mem_capacity}`}</div>
|
||||||
</DataListCell>,
|
</SliderHolder>
|
||||||
<DataListCell
|
</Td>
|
||||||
key="capacity-adjustment"
|
<Td
|
||||||
aria-label={t`capacity adjustment`}
|
dataLabel={t`Instance group used capacity`}
|
||||||
width={4}
|
css="--pf-c-table--cell--MinWidth: 175px;"
|
||||||
>
|
>
|
||||||
<SliderHolder data-cy="slider-holder">
|
{usedCapacity(instance)}
|
||||||
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div>
|
</Td>
|
||||||
<SliderForks data-cy="slider-forks">
|
<ActionsTd
|
||||||
<div data-cy="number-forks">
|
dataLabel={t`Actions`}
|
||||||
<Plural value={forks} one="# fork" other="# forks" />
|
css="--pf-c-table--cell--Width: 125px"
|
||||||
</div>
|
>
|
||||||
<Slider
|
<ActionItem visible>
|
||||||
areCustomStepsContinuous
|
|
||||||
max={1}
|
|
||||||
min={0}
|
|
||||||
step={0.1}
|
|
||||||
value={instance.capacity_adjustment}
|
|
||||||
onChange={handleChangeValue}
|
|
||||||
isDisabled={!me?.is_superuser || !instance.enabled}
|
|
||||||
data-cy="slider"
|
|
||||||
/>
|
|
||||||
</SliderForks>
|
|
||||||
|
|
||||||
<div data-cy="mem-capacity">{t`RAM ${instance.mem_capacity}`}</div>
|
|
||||||
</SliderHolder>
|
|
||||||
</DataListCell>,
|
|
||||||
|
|
||||||
<DataListCell
|
|
||||||
key="capacity"
|
|
||||||
aria-label={t`instance group used capacity`}
|
|
||||||
>
|
|
||||||
{usedCapacity(instance)}
|
|
||||||
</DataListCell>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<DataListAction
|
|
||||||
aria-label={t`actions`}
|
|
||||||
aria-labelledby={labelId}
|
|
||||||
id={labelId}
|
|
||||||
>
|
|
||||||
<InstanceToggle
|
<InstanceToggle
|
||||||
css="display: inline-flex;"
|
css="display: inline-flex;"
|
||||||
fetchInstances={fetchInstances}
|
fetchInstances={fetchInstances}
|
||||||
instance={instance}
|
instance={instance}
|
||||||
/>
|
/>
|
||||||
</DataListAction>
|
</ActionItem>
|
||||||
</DataListItemRow>
|
</ActionsTd>
|
||||||
</DataListItem>
|
</Tr>
|
||||||
{updateError && (
|
{updateError && (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
variant="error"
|
variant="error"
|
||||||
|
|||||||
@@ -49,12 +49,16 @@ describe('<InstanceListItem/>', () => {
|
|||||||
test('should mount successfully', async () => {
|
test('should mount successfully', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InstanceListItem
|
<table>
|
||||||
instance={instance[0]}
|
<tbody>
|
||||||
isSelected={false}
|
<InstanceListItem
|
||||||
onSelect={() => {}}
|
instance={instance[0]}
|
||||||
fetchInstances={() => {}}
|
isSelected={false}
|
||||||
/>
|
onSelect={() => {}}
|
||||||
|
fetchInstances={() => {}}
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
expect(wrapper.find('InstanceListItem').length).toBe(1);
|
expect(wrapper.find('InstanceListItem').length).toBe(1);
|
||||||
@@ -63,12 +67,16 @@ describe('<InstanceListItem/>', () => {
|
|||||||
test('should calculate number of forks when slide changes', async () => {
|
test('should calculate number of forks when slide changes', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InstanceListItem
|
<table>
|
||||||
instance={instance[0]}
|
<tbody>
|
||||||
isSelected={false}
|
<InstanceListItem
|
||||||
onSelect={() => {}}
|
instance={instance[0]}
|
||||||
fetchInstances={() => {}}
|
isSelected={false}
|
||||||
/>
|
onSelect={() => {}}
|
||||||
|
fetchInstances={() => {}}
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
expect(wrapper.find('InstanceListItem').length).toBe(1);
|
expect(wrapper.find('InstanceListItem').length).toBe(1);
|
||||||
@@ -105,30 +113,41 @@ describe('<InstanceListItem/>', () => {
|
|||||||
test('should render the proper data instance', async () => {
|
test('should render the proper data instance', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InstanceListItem
|
<table>
|
||||||
instance={instance[0]}
|
<tbody>
|
||||||
isSelected={false}
|
<InstanceListItem
|
||||||
onSelect={() => {}}
|
instance={instance[0]}
|
||||||
fetchInstances={() => {}}
|
isSelected={false}
|
||||||
/>
|
onSelect={() => {}}
|
||||||
|
fetchInstances={() => {}}
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('PFDataListCell[aria-label="instance host name"]').text()
|
wrapper
|
||||||
|
.find('Td')
|
||||||
|
.at(1)
|
||||||
|
.text()
|
||||||
).toBe('awx');
|
).toBe('awx');
|
||||||
expect(wrapper.find('Progress').prop('value')).toBe(40);
|
expect(wrapper.find('Progress').prop('value')).toBe(40);
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('PFDataListCell[aria-label="instance type"]').text()
|
wrapper
|
||||||
).toBe('TypeAuto');
|
.find('Td')
|
||||||
expect(wrapper.find('input#instances-1').prop('checked')).toBe(false);
|
.at(2)
|
||||||
|
.text()
|
||||||
|
).toBe('Auto');
|
||||||
expect(
|
expect(
|
||||||
wrapper
|
wrapper
|
||||||
.find('PFDataListCell[aria-label="capacity adjustment"]')
|
.find('Td')
|
||||||
|
.at(5)
|
||||||
.containsMatchingElement(<div>CPU 24</div>)
|
.containsMatchingElement(<div>CPU 24</div>)
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
wrapper
|
wrapper
|
||||||
.find('PFDataListCell[aria-label="capacity adjustment"]')
|
.find('Td')
|
||||||
|
.at(5)
|
||||||
.containsMatchingElement(<div>RAM 24</div>)
|
.containsMatchingElement(<div>RAM 24</div>)
|
||||||
);
|
);
|
||||||
expect(wrapper.find('InstanceListItem__SliderForks').text()).toContain(
|
expect(wrapper.find('InstanceListItem__SliderForks').text()).toContain(
|
||||||
@@ -136,18 +155,27 @@ describe('<InstanceListItem/>', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should be checked', async () => {
|
test('should render checkbox', async () => {
|
||||||
|
const onSelect = jest.fn();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InstanceListItem
|
<table>
|
||||||
instance={instance[0]}
|
<tbody>
|
||||||
isSelected
|
<InstanceListItem
|
||||||
onSelect={() => {}}
|
instance={instance[0]}
|
||||||
fetchInstances={() => {}}
|
onSelect={onSelect}
|
||||||
/>
|
fetchInstances={() => {}}
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
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', () => {
|
test('should display instance toggle', () => {
|
||||||
@@ -176,12 +204,16 @@ describe('<InstanceListItem/>', () => {
|
|||||||
);
|
);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InstanceListItem
|
<table>
|
||||||
instance={instance[0]}
|
<tbody>
|
||||||
isSelected={false}
|
<InstanceListItem
|
||||||
onSelect={() => {}}
|
instance={instance[0]}
|
||||||
fetchInstances={() => {}}
|
isSelected={false}
|
||||||
/>,
|
onSelect={() => {}}
|
||||||
|
fetchInstances={() => {}}
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>,
|
||||||
{ context: { network: { handleHttpError: () => {} } } }
|
{ context: { network: { handleHttpError: () => {} } } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user