mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 01:57:35 -03:30
Merge pull request #10623 from AlexSCorey/4208-ExpandWholeList
Adds functionality for expand all SUMMARY Closes #4208. Adds basically the same work as useSelected, in a useExpanded hook. ISSUE TYPE Feature Pull Request COMPONENT NAME UI AWX VERSION ADDITIONAL INFORMATION Reviewed-by: Keith Grant <keithjgrant@gmail.com> Reviewed-by: Sarah Akus <sakus@redhat.com>
This commit is contained in:
commit
9bb7d918eb
@ -4,6 +4,7 @@ import styled from 'styled-components';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Toolbar,
|
||||
ToolbarContent as PFToolbarContent,
|
||||
@ -14,7 +15,11 @@ import {
|
||||
DropdownPosition,
|
||||
KebabToggle,
|
||||
} from '@patternfly/react-core';
|
||||
import { SearchIcon } from '@patternfly/react-icons';
|
||||
import {
|
||||
AngleDownIcon,
|
||||
AngleRightIcon,
|
||||
SearchIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
import ExpandCollapse from '../ExpandCollapse';
|
||||
import Search from '../Search';
|
||||
import Sort from '../Sort';
|
||||
@ -28,14 +33,16 @@ const ToolbarContent = styled(PFToolbarContent)`
|
||||
`;
|
||||
|
||||
function DataListToolbar({
|
||||
isAllExpanded,
|
||||
onExpandAll,
|
||||
itemCount,
|
||||
clearAllFilters,
|
||||
searchColumns,
|
||||
searchableKeys,
|
||||
relatedSearchableKeys,
|
||||
sortColumns,
|
||||
showSelectAll,
|
||||
isAllSelected,
|
||||
onSelectAll,
|
||||
isCompact,
|
||||
onSort,
|
||||
onSearch,
|
||||
@ -43,7 +50,6 @@ function DataListToolbar({
|
||||
onRemove,
|
||||
onCompact,
|
||||
onExpand,
|
||||
onSelectAll,
|
||||
additionalControls,
|
||||
qsConfig,
|
||||
pagination,
|
||||
@ -70,7 +76,6 @@ function DataListToolbar({
|
||||
setIsKebabOpen(false);
|
||||
}
|
||||
}, [isKebabModalOpen]);
|
||||
|
||||
return (
|
||||
<Toolbar
|
||||
id={`${qsConfig.namespace}-list-toolbar`}
|
||||
@ -79,7 +84,27 @@ function DataListToolbar({
|
||||
clearFiltersButtonText={t`Clear all filters`}
|
||||
>
|
||||
<ToolbarContent>
|
||||
{showSelectAll && (
|
||||
{onExpandAll && (
|
||||
<ToolbarGroup>
|
||||
<ToolbarItem>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onExpandAll(!isAllExpanded);
|
||||
}}
|
||||
aria-label={t`Expand all rows`}
|
||||
ouiaId="expand-all-rows"
|
||||
variant="plain"
|
||||
>
|
||||
{isAllExpanded ? (
|
||||
<AngleDownIcon aria-label={t`Is expanded`} />
|
||||
) : (
|
||||
<AngleRightIcon aria-label={t`Is not expanded`} />
|
||||
)}
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
</ToolbarGroup>
|
||||
)}
|
||||
{onSelectAll && (
|
||||
<ToolbarGroup>
|
||||
<ToolbarItem>
|
||||
<Checkbox
|
||||
@ -178,7 +203,6 @@ DataListToolbar.propTypes = {
|
||||
searchableKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
relatedSearchableKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
sortColumns: SortColumns,
|
||||
showSelectAll: PropTypes.bool,
|
||||
isAllSelected: PropTypes.bool,
|
||||
isCompact: PropTypes.bool,
|
||||
onCompact: PropTypes.func,
|
||||
@ -198,7 +222,6 @@ DataListToolbar.defaultProps = {
|
||||
relatedSearchableKeys: [],
|
||||
sortColumns: null,
|
||||
clearAllFilters: null,
|
||||
showSelectAll: false,
|
||||
isAllSelected: false,
|
||||
isCompact: false,
|
||||
onCompact: null,
|
||||
|
||||
@ -19,6 +19,7 @@ describe('<DataListToolbar />', () => {
|
||||
const onReplaceSearch = jest.fn();
|
||||
const onSort = jest.fn();
|
||||
const onSelectAll = jest.fn();
|
||||
const onExpandAll = jest.fn();
|
||||
|
||||
test('it triggers the expected callbacks', () => {
|
||||
const searchColumns = [
|
||||
@ -285,4 +286,61 @@ describe('<DataListToolbar />', () => {
|
||||
1
|
||||
);
|
||||
});
|
||||
|
||||
test('should handle expanded rows', async () => {
|
||||
const searchColumns = [
|
||||
{ name: 'Name', key: 'name__icontains', isDefault: true },
|
||||
];
|
||||
const sortColumns = [{ name: 'Name', key: 'name' }];
|
||||
|
||||
const newtoolbar = mountWithContexts(
|
||||
<DataListToolbar
|
||||
qsConfig={QS_CONFIG}
|
||||
isAllSelected={false}
|
||||
searchColumns={searchColumns}
|
||||
sortColumns={sortColumns}
|
||||
onSearch={onSearch}
|
||||
onReplaceSearch={onReplaceSearch}
|
||||
onSort={onSort}
|
||||
onSelectAll={onSelectAll}
|
||||
showSelectAll
|
||||
showExpandAll
|
||||
isAllExpanded={false}
|
||||
onExpandAll={onExpandAll}
|
||||
/>
|
||||
);
|
||||
await act(async () =>
|
||||
newtoolbar.find('Button[aria-label="Expand all rows"]').prop('onClick')()
|
||||
);
|
||||
expect(newtoolbar.find('AngleRightIcon')).toHaveLength(1);
|
||||
expect(newtoolbar.find('AngleDownIcon')).toHaveLength(0);
|
||||
expect(onExpandAll).toBeCalledWith(true);
|
||||
});
|
||||
|
||||
test('should render angle down icon', async () => {
|
||||
const searchColumns = [
|
||||
{ name: 'Name', key: 'name__icontains', isDefault: true },
|
||||
];
|
||||
const sortColumns = [{ name: 'Name', key: 'name' }];
|
||||
|
||||
const newtoolbar = mountWithContexts(
|
||||
<DataListToolbar
|
||||
qsConfig={QS_CONFIG}
|
||||
isAllSelected={false}
|
||||
searchColumns={searchColumns}
|
||||
sortColumns={sortColumns}
|
||||
onSearch={onSearch}
|
||||
onReplaceSearch={onReplaceSearch}
|
||||
onSort={onSort}
|
||||
onSelectAll={onSelectAll}
|
||||
showSelectAll
|
||||
showExpandAll
|
||||
isAllExpanded
|
||||
onExpandAll={onExpandAll}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(newtoolbar.find('AngleDownIcon')).toHaveLength(1);
|
||||
expect(newtoolbar.find('AngleRightIcon')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@ -17,6 +17,7 @@ import useRequest, {
|
||||
} from '../../util/useRequest';
|
||||
import { useConfig } from '../../contexts/Config';
|
||||
import useSelected from '../../util/useSelected';
|
||||
import useExpanded from '../../util/useExpanded';
|
||||
import { isJobRunning, getJobModel } from '../../util/jobs';
|
||||
import { getQSConfig, parseQueryString } from '../../util/qs';
|
||||
import JobListItem from './JobListItem';
|
||||
@ -97,6 +98,10 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
clearSelected,
|
||||
} = useSelected(jobs);
|
||||
|
||||
const { expanded, isAllExpanded, handleExpand, expandAll } = useExpanded(
|
||||
jobs
|
||||
);
|
||||
|
||||
const {
|
||||
error: cancelJobsError,
|
||||
isLoading: isCancelLoading,
|
||||
@ -227,7 +232,8 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllExpanded={isAllExpanded}
|
||||
onExpandAll={expandAll}
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={qsConfig}
|
||||
@ -264,6 +270,8 @@ function JobList({ defaultParams, showTypeColumn = false }) {
|
||||
<JobListItem
|
||||
key={job.id}
|
||||
job={job}
|
||||
isExpanded={expanded.some(row => row.id === job.id)}
|
||||
onExpand={() => handleExpand(job)}
|
||||
isSuperUser={me?.is_superuser}
|
||||
showTypeColumn={showTypeColumn}
|
||||
onSelect={() => handleSelect(job)}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
@ -20,6 +20,8 @@ import JobCancelButton from '../JobCancelButton';
|
||||
|
||||
const Dash = styled.span``;
|
||||
function JobListItem({
|
||||
isExpanded,
|
||||
onExpand,
|
||||
job,
|
||||
rowIndex,
|
||||
isSelected,
|
||||
@ -28,7 +30,6 @@ function JobListItem({
|
||||
isSuperUser = false,
|
||||
}) {
|
||||
const labelId = `check-action-${job.id}`;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const jobTypes = {
|
||||
project_update: t`Source Control Update`,
|
||||
@ -63,7 +64,7 @@ function JobListItem({
|
||||
expand={{
|
||||
rowIndex: job.id,
|
||||
isExpanded,
|
||||
onToggle: () => setIsExpanded(!isExpanded),
|
||||
onToggle: onExpand,
|
||||
}}
|
||||
/>
|
||||
<Td
|
||||
|
||||
@ -204,7 +204,6 @@ function ScheduleList({
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -17,6 +17,7 @@ import PaginatedTable, {
|
||||
} from '../PaginatedTable';
|
||||
import useRequest, { useDeleteItems } from '../../util/useRequest';
|
||||
import useSelected from '../../util/useSelected';
|
||||
import useExpanded from '../../util/useExpanded';
|
||||
import { getQSConfig, parseQueryString } from '../../util/qs';
|
||||
import useWsTemplates from '../../util/useWsTemplates';
|
||||
import AddDropDownButton from '../AddDropDownButton';
|
||||
@ -97,6 +98,10 @@ function TemplateList({ defaultParams }) {
|
||||
clearSelected,
|
||||
} = useSelected(templates);
|
||||
|
||||
const { expanded, isAllExpanded, handleExpand, expandAll } = useExpanded(
|
||||
templates
|
||||
);
|
||||
|
||||
const {
|
||||
isLoading: isDeleteLoading,
|
||||
deleteItems: deleteTemplates,
|
||||
@ -229,9 +234,10 @@ function TemplateList({ defaultParams }) {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
isAllExpanded={isAllExpanded}
|
||||
onExpandAll={expandAll}
|
||||
qsConfig={qsConfig}
|
||||
additionalControls={[
|
||||
...(canAddJT || canAddWFJT ? [addButton] : []),
|
||||
@ -259,6 +265,8 @@ function TemplateList({ defaultParams }) {
|
||||
template={template}
|
||||
detailUrl={`/templates/${template.type}/${template.id}`}
|
||||
onSelect={() => handleSelect(template)}
|
||||
isExpanded={expanded.some(row => row.id === template.id)}
|
||||
onExpand={() => handleExpand(template)}
|
||||
isSelected={selected.some(row => row.id === template.id)}
|
||||
fetchTemplates={fetchTemplates}
|
||||
rowIndex={index}
|
||||
|
||||
@ -34,6 +34,8 @@ const ExclamationTriangleIconWarning = styled(ExclamationTriangleIcon)`
|
||||
ExclamationTriangleIconWarning.displayName = 'ExclamationTriangleIconWarning';
|
||||
|
||||
function TemplateListItem({
|
||||
isExpanded,
|
||||
onExpand,
|
||||
template,
|
||||
isSelected,
|
||||
onSelect,
|
||||
@ -42,7 +44,6 @@ function TemplateListItem({
|
||||
rowIndex,
|
||||
}) {
|
||||
const config = useConfig();
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [isDisabled, setIsDisabled] = useState(false);
|
||||
const labelId = `check-action-${template.id}`;
|
||||
|
||||
@ -119,7 +120,7 @@ function TemplateListItem({
|
||||
expand={{
|
||||
rowIndex,
|
||||
isExpanded,
|
||||
onToggle: () => setIsExpanded(!isExpanded),
|
||||
onToggle: onExpand,
|
||||
}}
|
||||
/>
|
||||
<Td
|
||||
|
||||
@ -381,6 +381,7 @@ describe('<TemplateListItem />', () => {
|
||||
<tbody>
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
isExpanded
|
||||
detailUrl="/templates/job_template/1/details"
|
||||
template={{
|
||||
...mockJobTemplateData,
|
||||
@ -390,15 +391,7 @@ describe('<TemplateListItem />', () => {
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Tr')
|
||||
.last()
|
||||
.prop('isExpanded')
|
||||
).toBe(false);
|
||||
await act(async () =>
|
||||
wrapper.find('button[aria-label="Details"]').simulate('click')
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper
|
||||
|
||||
@ -121,7 +121,6 @@ function ApplicationTokenList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -134,7 +134,6 @@ function ApplicationsList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -161,7 +161,6 @@ function CredentialList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -148,7 +148,6 @@ function CredentialTypeList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -164,7 +164,6 @@ function ExecutionEnvironmentList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -196,7 +196,6 @@ function HostGroupsList({ host }) {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -171,7 +171,6 @@ function HostList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -258,7 +258,6 @@ function InstanceGroupList({
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -174,7 +174,6 @@ function InstanceList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -218,7 +218,6 @@ function InventoryGroupHostList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={isSelected =>
|
||||
setSelected(isSelected ? [...hosts] : [])
|
||||
|
||||
@ -159,7 +159,6 @@ function InventoryGroupsList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -197,7 +197,6 @@ function InventoryHostGroupsList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -140,7 +140,6 @@ function InventoryHostList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -214,7 +214,6 @@ function InventoryList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -192,7 +192,6 @@ function InventoryRelatedGroupList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={isSelected =>
|
||||
setSelected(isSelected ? [...groups] : [])
|
||||
|
||||
@ -169,7 +169,6 @@ function InventorySourceList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -87,7 +87,6 @@ function SmartInventoryHostList({ inventory }) {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -91,11 +91,7 @@ function ManagementJobList() {
|
||||
},
|
||||
]}
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll={false}
|
||||
qsConfig={QS_CONFIG}
|
||||
/>
|
||||
<DatalistToolbar {...props} qsConfig={QS_CONFIG} />
|
||||
)}
|
||||
headerRow={
|
||||
<HeaderRow qsConfig={QS_CONFIG}>
|
||||
|
||||
@ -176,7 +176,6 @@ function NotificationTemplatesList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -156,7 +156,6 @@ function OrganizationsList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -141,7 +141,6 @@ function ProjectJobTemplatesList() {
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -18,6 +18,7 @@ import PaginatedTable, {
|
||||
} from '../../../components/PaginatedTable';
|
||||
import useWsProjects from './useWsProjects';
|
||||
import useSelected from '../../../util/useSelected';
|
||||
import useExpanded from '../../../util/useExpanded';
|
||||
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
|
||||
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
||||
|
||||
@ -103,6 +104,10 @@ function ProjectList() {
|
||||
clearSelected,
|
||||
} = useSelected(projects);
|
||||
|
||||
const { expanded, isAllExpanded, handleExpand, expandAll } = useExpanded(
|
||||
projects
|
||||
);
|
||||
|
||||
const {
|
||||
isLoading: isDeleteLoading,
|
||||
deleteItems: deleteProjects,
|
||||
@ -212,7 +217,8 @@ function ProjectList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllExpanded={isAllExpanded}
|
||||
onExpandAll={expandAll}
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
@ -244,6 +250,8 @@ function ProjectList() {
|
||||
)}
|
||||
renderRow={(project, index) => (
|
||||
<ProjectListItem
|
||||
isExpanded={expanded.some(row => row.id === project.id)}
|
||||
onExpand={() => handleExpand(project)}
|
||||
fetchProjects={fetchProjects}
|
||||
key={project.id}
|
||||
project={project}
|
||||
|
||||
@ -38,6 +38,8 @@ const ExclamationTriangleIcon = styled(PFExclamationTriangleIcon)`
|
||||
`;
|
||||
|
||||
function ProjectListItem({
|
||||
isExpanded,
|
||||
onExpand,
|
||||
project,
|
||||
isSelected,
|
||||
onSelect,
|
||||
@ -46,7 +48,6 @@ function ProjectListItem({
|
||||
rowIndex,
|
||||
onRefreshRow,
|
||||
}) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [isDisabled, setIsDisabled] = useState(false);
|
||||
ProjectListItem.propTypes = {
|
||||
project: Project.isRequired,
|
||||
@ -165,7 +166,7 @@ function ProjectListItem({
|
||||
expand={{
|
||||
rowIndex,
|
||||
isExpanded,
|
||||
onToggle: () => setIsExpanded(!isExpanded),
|
||||
onToggle: onExpand,
|
||||
}}
|
||||
/>
|
||||
<Td
|
||||
|
||||
@ -393,6 +393,7 @@ describe('<ProjectsListItem />', () => {
|
||||
<tbody>
|
||||
<ProjectsListItem
|
||||
rowIndex={1}
|
||||
isExpanded
|
||||
isSelected={false}
|
||||
detailUrl="/project/1"
|
||||
onSelect={() => {}}
|
||||
@ -431,16 +432,7 @@ describe('<ProjectsListItem />', () => {
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('Tr')
|
||||
.last()
|
||||
.prop('isExpanded')
|
||||
).toBe(false);
|
||||
await act(async () =>
|
||||
wrapper.find('button[aria-label="Details"]').simulate('click')
|
||||
);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('Tr')
|
||||
|
||||
@ -152,7 +152,6 @@ function TeamList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -135,7 +135,6 @@ function UserList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -202,7 +202,6 @@ function UserTeamList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
@ -150,7 +150,6 @@ function UserTokenList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
qsConfig={QS_CONFIG}
|
||||
onSelectAll={selectAll}
|
||||
|
||||
@ -189,7 +189,6 @@ function WorkflowApprovalsList() {
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
showSelectAll
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={selectAll}
|
||||
qsConfig={QS_CONFIG}
|
||||
|
||||
32
awx/ui_next/src/util/useExpanded.jsx
Normal file
32
awx/ui_next/src/util/useExpanded.jsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
export default function useExpanded(list = []) {
|
||||
const [expanded, setExpanded] = useState([]);
|
||||
const isAllExpanded = expanded.length > 0 && expanded.length === list.length;
|
||||
|
||||
const handleExpand = row => {
|
||||
if (!row.id) {
|
||||
throw new Error(`Selected row does not have an id`);
|
||||
}
|
||||
if (expanded.some(s => s.id === row.id)) {
|
||||
setExpanded(prevState => [...prevState.filter(i => i.id !== row.id)]);
|
||||
} else {
|
||||
setExpanded(prevState => [...prevState, row]);
|
||||
}
|
||||
};
|
||||
|
||||
const expandAll = useCallback(
|
||||
isExpanded => {
|
||||
setExpanded(isExpanded ? [...list] : []);
|
||||
},
|
||||
[list]
|
||||
);
|
||||
|
||||
return {
|
||||
expanded,
|
||||
isAllExpanded,
|
||||
handleExpand,
|
||||
setExpanded,
|
||||
expandAll,
|
||||
};
|
||||
}
|
||||
116
awx/ui_next/src/util/useExpanded.test.jsx
Normal file
116
awx/ui_next/src/util/useExpanded.test.jsx
Normal file
@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from 'enzyme';
|
||||
import useExpanded from './useExpanded';
|
||||
|
||||
const array = [{ id: '1' }, { id: '2' }, { id: '3' }];
|
||||
|
||||
const TestHook = ({ callback }) => {
|
||||
callback();
|
||||
return null;
|
||||
};
|
||||
|
||||
const testHook = callback => {
|
||||
mount(<TestHook callback={callback} />);
|
||||
};
|
||||
|
||||
describe('useSelected hook', () => {
|
||||
let expanded;
|
||||
let isAllExpanded;
|
||||
let handleExpand;
|
||||
let setExpanded;
|
||||
let expandAll;
|
||||
|
||||
test('should return expected initial values', () => {
|
||||
testHook(() => {
|
||||
({
|
||||
expanded,
|
||||
isAllExpanded,
|
||||
handleExpand,
|
||||
setExpanded,
|
||||
expandAll,
|
||||
} = useExpanded());
|
||||
});
|
||||
expect(expanded).toEqual([]);
|
||||
expect(isAllExpanded).toEqual(false);
|
||||
expect(handleExpand).toBeInstanceOf(Function);
|
||||
expect(setExpanded).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
test('handleSelect should update and filter selected items', () => {
|
||||
testHook(() => {
|
||||
({
|
||||
expanded,
|
||||
isAllExpanded,
|
||||
handleExpand,
|
||||
setExpanded,
|
||||
expandAll,
|
||||
} = useExpanded());
|
||||
});
|
||||
|
||||
act(() => {
|
||||
handleExpand(array[0]);
|
||||
});
|
||||
expect(expanded).toEqual([array[0]]);
|
||||
|
||||
act(() => {
|
||||
handleExpand(array[0]);
|
||||
});
|
||||
expect(expanded).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return expected isAllSelected value', () => {
|
||||
testHook(() => {
|
||||
({
|
||||
expanded,
|
||||
isAllExpanded,
|
||||
handleExpand,
|
||||
setExpanded,
|
||||
expandAll,
|
||||
} = useExpanded(array));
|
||||
});
|
||||
|
||||
act(() => {
|
||||
handleExpand(array[0]);
|
||||
});
|
||||
expect(expanded).toEqual([array[0]]);
|
||||
expect(isAllExpanded).toEqual(false);
|
||||
|
||||
act(() => {
|
||||
handleExpand(array[1]);
|
||||
handleExpand(array[2]);
|
||||
});
|
||||
expect(expanded).toEqual(array);
|
||||
expect(isAllExpanded).toEqual(true);
|
||||
|
||||
act(() => {
|
||||
setExpanded([]);
|
||||
});
|
||||
expect(expanded).toEqual([]);
|
||||
expect(isAllExpanded).toEqual(false);
|
||||
});
|
||||
|
||||
test('should return selectAll', () => {
|
||||
testHook(() => {
|
||||
({
|
||||
expanded,
|
||||
isAllExpanded,
|
||||
handleExpand,
|
||||
setExpanded,
|
||||
expandAll,
|
||||
} = useExpanded(array));
|
||||
});
|
||||
|
||||
act(() => {
|
||||
expandAll(true);
|
||||
});
|
||||
expect(isAllExpanded).toEqual(true);
|
||||
expect(expanded).toEqual(array);
|
||||
|
||||
act(() => {
|
||||
expandAll(false);
|
||||
});
|
||||
expect(isAllExpanded).toEqual(false);
|
||||
expect(expanded).toEqual([]);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user