diff --git a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx
index fceaa60ed3..c87d8cbc92 100644
--- a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx
+++ b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx
@@ -37,7 +37,6 @@ function DataListToolbar({
onExpand,
onSelectAll,
additionalControls,
-
qsConfig,
pagination,
}) {
diff --git a/awx/ui_next/src/components/JobList/JobList.jsx b/awx/ui_next/src/components/JobList/JobList.jsx
index eaaa26095c..c2d113b829 100644
--- a/awx/ui_next/src/components/JobList/JobList.jsx
+++ b/awx/ui_next/src/components/JobList/JobList.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { t, Plural } from '@lingui/macro';
@@ -13,7 +13,7 @@ import useRequest, {
useDismissableError,
} from '../../util/useRequest';
import { useConfig } from '../../contexts/Config';
-
+import useSelected from '../../util/useSelected';
import { isJobRunning, getJobModel } from '../../util/jobs';
import { getQSConfig, parseQueryString } from '../../util/qs';
import JobListItem from './JobListItem';
@@ -35,8 +35,6 @@ function JobList({ defaultParams, showTypeColumn = false }) {
);
const { me } = useConfig();
-
- const [selected, setSelected] = useState([]);
const location = useLocation();
const {
result: { results, count, relatedSearchableKeys, searchableKeys },
@@ -88,7 +86,13 @@ function JobList({ defaultParams, showTypeColumn = false }) {
const jobs = useWsJobs(results, fetchJobsById, qsConfig);
- const isAllSelected = selected.length === jobs.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(jobs);
const {
error: cancelJobsError,
@@ -135,24 +139,12 @@ function JobList({ defaultParams, showTypeColumn = false }) {
const handleJobCancel = async () => {
await cancelJobs();
- setSelected([]);
+ clearSelected();
};
const handleJobDelete = async () => {
await deleteJobs();
- setSelected([]);
- };
-
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...jobs] : []);
- };
-
- const handleSelect = item => {
- if (selected.some(s => s.id === item.id)) {
- setSelected(selected.filter(s => s.id !== item.id));
- } else {
- setSelected(selected.concat(item));
- }
+ clearSelected();
};
const cannotDeleteItems = selected.filter(job => isJobRunning(job.status));
@@ -226,6 +218,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
{t`Actions`}
}
+ clearSelected={clearSelected}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
renderToolbar={props => (
@@ -233,7 +226,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={handleSelectAll}
+ onSelectAll={selectAll}
qsConfig={qsConfig}
additionalControls={[
{
+ clearSelected();
+ }, [location.search, clearSelected]);
const pushHistoryState = qs => {
history.push(qs ? `${pathname}?${qs}` : pathname);
@@ -127,7 +133,7 @@ function PaginatedTable({
);
return (
-
+ <>
) : null}
-
+ >
);
}
@@ -182,6 +188,7 @@ PaginatedTable.propTypes = {
renderToolbar: PropTypes.func,
hasContentLoading: PropTypes.bool,
contentError: PropTypes.shape(),
+ clearSelected: PropTypes.func,
ouiaId: PropTypes.string,
};
@@ -195,6 +202,7 @@ PaginatedTable.defaultProps = {
showPageSizeOptions: true,
renderToolbar: props => ,
ouiaId: null,
+ clearSelected: () => {},
};
export { PaginatedTable as _PaginatedTable };
diff --git a/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx
index 60592f9e99..4a1a28e509 100644
--- a/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx
+++ b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { bool, func } from 'prop-types';
@@ -10,6 +10,7 @@ import PaginatedTable, { HeaderRow, HeaderCell } from '../../PaginatedTable';
import DataListToolbar from '../../DataListToolbar';
import { ToolbarAddButton, ToolbarDeleteButton } from '../../PaginatedDataList';
import useRequest, { useDeleteItems } from '../../../util/useRequest';
+import useSelected from '../../../util/useSelected';
import { getQSConfig, parseQueryString } from '../../../util/qs';
import ScheduleListItem from './ScheduleListItem';
@@ -27,8 +28,6 @@ function ScheduleList({
launchConfig,
surveyConfig,
}) {
- const [selected, setSelected] = useState([]);
-
const location = useLocation();
const {
@@ -76,8 +75,13 @@ function ScheduleList({
fetchSchedules();
}, [fetchSchedules]);
- const isAllSelected =
- selected.length === schedules.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(schedules);
const {
isLoading: isDeleteLoading,
@@ -95,21 +99,9 @@ function ScheduleList({
}
);
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...schedules] : []);
- };
-
- const handleSelect = row => {
- if (selected.some(s => s.id === row.id)) {
- setSelected(selected.filter(s => s.id !== row.id));
- } else {
- setSelected(selected.concat(row));
- }
- };
-
const handleDelete = async () => {
await deleteJobs();
- setSelected([]);
+ clearSelected();
};
const canAdd =
@@ -183,6 +175,7 @@ function ScheduleList({
isMissingSurvey={isTemplate && hasMissingSurveyValue(item)}
/>
)}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -209,7 +202,7 @@ function ScheduleList({
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={handleSelectAll}
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/components/TemplateList/TemplateList.jsx b/awx/ui_next/src/components/TemplateList/TemplateList.jsx
index 9ab0905306..69a0e6b7f6 100644
--- a/awx/ui_next/src/components/TemplateList/TemplateList.jsx
+++ b/awx/ui_next/src/components/TemplateList/TemplateList.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment, useEffect, useState, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useLocation, Link } from 'react-router-dom';
import { t, Plural } from '@lingui/macro';
import { Card, DropdownItem } from '@patternfly/react-core';
@@ -13,6 +13,7 @@ import ErrorDetail from '../ErrorDetail';
import { ToolbarDeleteButton } from '../PaginatedDataList';
import PaginatedTable, { HeaderRow, HeaderCell } from '../PaginatedTable';
import useRequest, { useDeleteItems } from '../../util/useRequest';
+import useSelected from '../../util/useSelected';
import { getQSConfig, parseQueryString } from '../../util/qs';
import useWsTemplates from '../../util/useWsTemplates';
import AddDropDownButton from '../AddDropDownButton';
@@ -35,8 +36,6 @@ function TemplateList({ defaultParams }) {
);
const location = useLocation();
- const [selected, setSelected] = useState([]);
-
const {
result: {
results,
@@ -87,8 +86,14 @@ function TemplateList({ defaultParams }) {
const templates = useWsTemplates(results);
- const isAllSelected =
- selected.length === templates.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(templates);
+
const {
isLoading: isDeleteLoading,
deleteItems: deleteTemplates,
@@ -117,19 +122,7 @@ function TemplateList({ defaultParams }) {
const handleTemplateDelete = async () => {
await deleteTemplates();
- setSelected([]);
- };
-
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...templates] : []);
- };
-
- const handleSelect = template => {
- if (selected.some(s => s.id === template.id)) {
- setSelected(selected.filter(s => s.id !== template.id));
- } else {
- setSelected(selected.concat(template));
- }
+ clearSelected();
};
const canAddJT =
@@ -179,7 +172,7 @@ function TemplateList({ defaultParams }) {
);
return (
-
+ <>
-
+ >
);
}
diff --git a/awx/ui_next/src/screens/Application/ApplicationsList/ApplicationsList.jsx b/awx/ui_next/src/screens/Application/ApplicationsList/ApplicationsList.jsx
index 23af1fb071..e9cb51e8f7 100644
--- a/awx/ui_next/src/screens/Application/ApplicationsList/ApplicationsList.jsx
+++ b/awx/ui_next/src/screens/Application/ApplicationsList/ApplicationsList.jsx
@@ -77,9 +77,13 @@ function ApplicationsList() {
fetchApplications();
}, [fetchApplications]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- applications
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(applications);
const {
isLoading: deleteLoading,
@@ -99,7 +103,7 @@ function ApplicationsList() {
const handleDeleteApplications = async () => {
await deleteApplications();
- setSelected([]);
+ clearSelected();
};
const canAdd = actions && actions.POST;
@@ -115,7 +119,7 @@ function ApplicationsList() {
itemCount={itemCount}
pluralizedItemName={t`Applications`}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -134,9 +138,7 @@ function ApplicationsList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...applications] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx
index 6f80e18ceb..bd78478cdd 100644
--- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx
+++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx
@@ -76,9 +76,14 @@ function CredentialList() {
fetchCredentials();
}, [fetchCredentials]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- credentials
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ setSelected,
+ selectAll,
+ clearSelected,
+ } = useSelected(credentials);
const {
isLoading: isDeleteLoading,
@@ -115,7 +120,7 @@ function CredentialList() {
items={credentials}
itemCount={credentialCount}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
toolbarSearchColumns={[
@@ -160,9 +165,7 @@ function CredentialList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...credentials] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/CredentialType/CredentialTypeList/CredentialTypeList.jsx b/awx/ui_next/src/screens/CredentialType/CredentialTypeList/CredentialTypeList.jsx
index bf8e371e31..4b93b28438 100644
--- a/awx/ui_next/src/screens/CredentialType/CredentialTypeList/CredentialTypeList.jsx
+++ b/awx/ui_next/src/screens/CredentialType/CredentialTypeList/CredentialTypeList.jsx
@@ -77,9 +77,13 @@ function CredentialTypeList() {
fetchCredentialTypes();
}, [fetchCredentialTypes]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- credentialTypes
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(credentialTypes);
const {
isLoading: deleteLoading,
@@ -101,7 +105,7 @@ function CredentialTypeList() {
const handleDelete = async () => {
await deleteCredentialTypes();
- setSelected([]);
+ clearSelected();
};
const canAdd = actions && actions.POST;
@@ -121,7 +125,7 @@ function CredentialTypeList() {
itemCount={credentialTypesCount}
pluralizedItemName={t`Credential Types`}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -148,9 +152,7 @@ function CredentialTypeList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...credentialTypes] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx
index 534a683038..6db687b031 100644
--- a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx
+++ b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx
@@ -76,9 +76,13 @@ function ExecutionEnvironmentList() {
fetchExecutionEnvironments();
}, [fetchExecutionEnvironments]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- executionEnvironments
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(executionEnvironments);
const {
isLoading: deleteLoading,
@@ -100,7 +104,7 @@ function ExecutionEnvironmentList() {
const handleDelete = async () => {
await deleteExecutionEnvironments();
- setSelected([]);
+ clearSelected();
};
const canAdd = actions && actions.POST;
@@ -119,7 +123,7 @@ function ExecutionEnvironmentList() {
itemCount={executionEnvironmentsCount}
pluralizedItemName={t`Execution Environments`}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
toolbarSearchColumns={[
@@ -164,9 +168,7 @@ function ExecutionEnvironmentList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...executionEnvironments] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/Host/HostList/HostList.jsx b/awx/ui_next/src/screens/Host/HostList/HostList.jsx
index 5c2bbd8670..f9ed610f8a 100644
--- a/awx/ui_next/src/screens/Host/HostList/HostList.jsx
+++ b/awx/ui_next/src/screens/Host/HostList/HostList.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useHistory, useLocation, useRouteMatch } from 'react-router-dom';
import { t } from '@lingui/macro';
@@ -17,6 +17,7 @@ import PaginatedTable, {
HeaderCell,
} from '../../../components/PaginatedTable';
import useRequest, { useDeleteItems } from '../../../util/useRequest';
+import useSelected from '../../../util/useSelected';
import {
encodeQueryString,
getQSConfig,
@@ -36,7 +37,6 @@ function HostList() {
const history = useHistory();
const location = useLocation();
const match = useRouteMatch();
- const [selected, setSelected] = useState([]);
const parsedQueryStrings = parseQueryString(QS_CONFIG, location.search);
const nonDefaultSearchParams = {};
@@ -86,7 +86,14 @@ function HostList() {
fetchHosts();
}, [fetchHosts]);
- const isAllSelected = selected.length === hosts.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(hosts);
+
const {
isLoading: isDeleteLoading,
deleteItems: deleteHosts,
@@ -105,19 +112,7 @@ function HostList() {
const handleHostDelete = async () => {
await deleteHosts();
- setSelected([]);
- };
-
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...hosts] : []);
- };
-
- const handleSelect = host => {
- if (selected.some(h => h.id === host.id)) {
- setSelected(selected.filter(h => h.id !== host.id));
- } else {
- setSelected(selected.concat(host));
- }
+ clearSelected();
};
const handleSmartInventoryClick = () => {
@@ -141,7 +136,7 @@ function HostList() {
itemCount={count}
pluralizedItemName={t`Hosts`}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -175,7 +170,7 @@ function HostList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={handleSelectAll}
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/InstanceGroup/InstanceGroupList/InstanceGroupList.jsx b/awx/ui_next/src/screens/InstanceGroup/InstanceGroupList/InstanceGroupList.jsx
index f125fd2003..776a9e9afb 100644
--- a/awx/ui_next/src/screens/InstanceGroup/InstanceGroupList/InstanceGroupList.jsx
+++ b/awx/ui_next/src/screens/InstanceGroup/InstanceGroupList/InstanceGroupList.jsx
@@ -92,9 +92,13 @@ function InstanceGroupList() {
fetchInstanceGroups();
}, [fetchInstanceGroups]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- instanceGroups
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(instanceGroups);
const modifiedSelected = modifyInstanceGroups(selected);
@@ -118,7 +122,7 @@ function InstanceGroupList() {
const handleDelete = async () => {
await deleteInstanceGroups();
- setSelected([]);
+ clearSelected();
};
const canAdd = actions && actions.POST;
@@ -201,7 +205,7 @@ function InstanceGroupList() {
itemCount={instanceGroupsCount}
pluralizedItemName={pluralizedItemName}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
renderToolbar={props => (
@@ -209,9 +213,7 @@ function InstanceGroupList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...instanceGroups] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd ? [addButton] : []),
diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx
index ec9d562453..1c680f9050 100644
--- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx
@@ -77,9 +77,13 @@ function InventoryGroupsList() {
fetchData();
}, [fetchData]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- groups
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(groups);
const renderTooltip = () => {
const itemsUnableToDelete = selected
@@ -111,7 +115,7 @@ function InventoryGroupsList() {
items={groups}
itemCount={groupCount}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -159,9 +163,7 @@ function InventoryGroupsList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...groups] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
@@ -185,7 +187,7 @@ function InventoryGroupsList() {
}
onAfterDelete={() => {
fetchData();
- setSelected([]);
+ clearSelected();
}}
/>
,
diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx
index 49868207c5..e493fb2197 100644
--- a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx
@@ -84,9 +84,13 @@ function InventoryHostGroupsList() {
fetchGroups();
}, [fetchGroups]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- groups
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(groups);
const {
isLoading: isDisassociateLoading,
@@ -107,7 +111,7 @@ function InventoryHostGroupsList() {
const handleDisassociate = async () => {
await disassociateHosts();
- setSelected([]);
+ clearSelected();
};
const fetchGroupsToAssociate = useCallback(
@@ -156,7 +160,7 @@ function InventoryHostGroupsList() {
items={groups}
itemCount={itemCount}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -195,9 +199,7 @@ function InventoryHostGroupsList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={isSelected =>
- setSelected(isSelected ? [...groups] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx
index 9b8bd79873..44a52af3b2 100644
--- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx
@@ -15,6 +15,7 @@ import {
ToolbarAddButton,
ToolbarDeleteButton,
} from '../../../components/PaginatedDataList';
+import useSelected from '../../../util/useSelected';
import AdHocCommands from '../../../components/AdHocCommands/AdHocCommands';
import InventoryHostItem from './InventoryHostItem';
@@ -25,7 +26,6 @@ const QS_CONFIG = getQSConfig('host', {
});
function InventoryHostList() {
- const [selected, setSelected] = useState([]);
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
const { id } = useParams();
const { search } = useLocation();
@@ -74,17 +74,14 @@ function InventoryHostList() {
fetchData();
}, [fetchData]);
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...hosts] : []);
- };
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(hosts);
- const handleSelect = row => {
- if (selected.some(s => s.id === row.id)) {
- setSelected(selected.filter(s => s.id !== row.id));
- } else {
- setSelected(selected.concat(row));
- }
- };
const {
isLoading: isDeleteLoading,
deleteItems: deleteHosts,
@@ -99,12 +96,11 @@ function InventoryHostList() {
const handleDeleteHosts = async () => {
await deleteHosts();
- setSelected([]);
+ clearSelected();
};
const canAdd =
actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
- const isAllSelected = selected.length > 0 && selected.length === hosts.length;
return (
<>
@@ -115,6 +111,7 @@ function InventoryHostList() {
itemCount={hostCount}
pluralizedItemName={t`Hosts`}
qsConfig={QS_CONFIG}
+ clearSelected={clearSelected}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
headerRow={
@@ -128,7 +125,7 @@ function InventoryHostList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={handleSelectAll}
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx
index 6b674fe973..1ca1b7511a 100644
--- a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx
@@ -1,9 +1,10 @@
-import React, { useState, useCallback, useEffect } from 'react';
+import React, { useCallback, useEffect } from 'react';
import { useLocation, useRouteMatch, Link } from 'react-router-dom';
import { t, Plural } from '@lingui/macro';
import { Card, PageSection, DropdownItem } from '@patternfly/react-core';
import { InventoriesAPI } from '../../../api';
import useRequest, { useDeleteItems } from '../../../util/useRequest';
+import useSelected from '../../../util/useSelected';
import AlertModal from '../../../components/AlertModal';
import DatalistToolbar from '../../../components/DataListToolbar';
import ErrorDetail from '../../../components/ErrorDetail';
@@ -27,7 +28,6 @@ const QS_CONFIG = getQSConfig('inventory', {
function InventoryList() {
const location = useLocation();
const match = useRouteMatch();
- const [selected, setSelected] = useState([]);
const {
result: {
@@ -89,8 +89,14 @@ function InventoryList() {
QS_CONFIG
);
- const isAllSelected =
- selected.length === inventories.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(inventories);
+
const {
isLoading: isDeleteLoading,
deleteItems: deleteInventories,
@@ -107,26 +113,12 @@ function InventoryList() {
const handleInventoryDelete = async () => {
await deleteInventories();
- setSelected([]);
+ clearSelected();
};
const hasContentLoading = isDeleteLoading || isLoading;
const canAdd = actions && actions.POST;
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...inventories] : []);
- };
-
- const handleSelect = row => {
- if (!row.pending_deletion) {
- if (selected.some(s => s.id === row.id)) {
- setSelected(selected.filter(s => s.id !== row.id));
- } else {
- setSelected(selected.concat(row));
- }
- }
- };
-
const deleteDetailsRequests = relatedResourceDeleteRequests.inventory(
selected[0]
);
@@ -197,6 +189,7 @@ function InventoryList() {
]}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
+ clearSelected={clearSelected}
headerRow={
{t`Name`}
@@ -211,7 +204,7 @@ function InventoryList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={handleSelectAll}
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd ? [addButton] : []),
@@ -251,7 +244,11 @@ function InventoryList() {
? `${match.url}/smart_inventory/${inventory.id}/details`
: `${match.url}/inventory/${inventory.id}/details`
}
- onSelect={() => handleSelect(inventory)}
+ onSelect={() => {
+ if (!inventory.pending_deletion) {
+ handleSelect(inventory);
+ }
+ }}
isSelected={selected.some(row => row.id === inventory.id)}
/>
)}
diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx
index 5147773c86..14c22a06b5 100644
--- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx
@@ -83,9 +83,13 @@ function InventorySourceList() {
fetchSources();
}, [fetchSources]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- sources
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(sources);
const {
isLoading: isDeleteLoading,
@@ -140,7 +144,7 @@ function InventorySourceList() {
if (!deleteRelatedResourcesError) {
await handleDeleteSources();
}
- setSelected([]);
+ clearSelected();
};
const canAdd =
sourceChoicesOptions &&
@@ -164,14 +168,13 @@ function InventorySourceList() {
itemCount={sourceCount}
pluralizedItemName={t`Inventory Sources`}
qsConfig={QS_CONFIG}
+ clearSelected={clearSelected}
renderToolbar={props => (
- setSelected(isSelected ? [...sources] : [])
- }
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx
index 554660b15b..236ec0b656 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.jsx
@@ -306,7 +306,7 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
label={t`HTTP Headers`}
value={JSON.stringify(configuration.headers)}
mode="json"
- rows="6"
+ rows={6}
dataCy="nt-detail-webhook-headers"
/>
>
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
index 849290a166..a927ab77ca 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.jsx
@@ -82,9 +82,13 @@ function NotificationTemplatesList() {
fetchTemplates();
}, [fetchTemplates]);
- const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
- templates
- );
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ clearSelected,
+ selectAll,
+ } = useSelected(templates);
const {
isLoading: isDeleteLoading,
@@ -106,7 +110,7 @@ function NotificationTemplatesList() {
const handleDelete = async () => {
await deleteTemplates();
- setSelected([]);
+ clearSelected();
};
const addTestToast = useCallback(notification => {
@@ -133,7 +137,7 @@ function NotificationTemplatesList() {
itemCount={count}
pluralizedItemName={t`Notification Templates`}
qsConfig={QS_CONFIG}
- onRowClick={handleSelect}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -176,7 +180,7 @@ function NotificationTemplatesList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={set => setSelected(set ? [...templates] : [])}
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx
index 4b5907dad9..1217c6e0b7 100644
--- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx
+++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useLocation, useRouteMatch } from 'react-router-dom';
import { t, Plural } from '@lingui/macro';
import { Card, PageSection } from '@patternfly/react-core';
@@ -17,6 +17,7 @@ import PaginatedTable, {
HeaderCell,
} from '../../../components/PaginatedTable';
import { getQSConfig, parseQueryString } from '../../../util/qs';
+import useSelected from '../../../util/useSelected';
import OrganizationListItem from './OrganizationListItem';
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
@@ -30,8 +31,6 @@ function OrganizationsList() {
const location = useLocation();
const match = useRouteMatch();
- const [selected, setSelected] = useState([]);
-
const addUrl = `${match.url}/add`;
const {
@@ -77,8 +76,14 @@ function OrganizationsList() {
fetchOrganizations();
}, [fetchOrganizations]);
- const isAllSelected =
- selected.length === organizations.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(organizations);
+
const {
isLoading: isDeleteLoading,
deleteItems: deleteOrganizations,
@@ -99,23 +104,12 @@ function OrganizationsList() {
const handleOrgDelete = async () => {
await deleteOrganizations();
- setSelected([]);
+ clearSelected();
};
const hasContentLoading = isDeleteLoading || isOrgsLoading;
const canAdd = actions && actions.POST;
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...organizations] : []);
- };
-
- const handleSelect = row => {
- if (selected.some(s => s.id === row.id)) {
- setSelected(selected.filter(s => s.id !== row.id));
- } else {
- setSelected(selected.concat(row));
- }
- };
const deleteDetailsRequests = relatedResourceDeleteRequests.organization(
selected[0]
);
@@ -131,6 +125,7 @@ function OrganizationsList() {
itemCount={organizationCount}
pluralizedItemName={t`Organizations`}
qsConfig={QS_CONFIG}
+ clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -165,7 +160,7 @@ function OrganizationsList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
- onSelectAll={handleSelectAll}
+ onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
diff --git a/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx b/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx
index eae938776d..66b9dff1f9 100644
--- a/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx
+++ b/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment, useState, useEffect, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useLocation, useRouteMatch } from 'react-router-dom';
import { t, Plural } from '@lingui/macro';
import { Card, PageSection } from '@patternfly/react-core';
@@ -17,6 +17,7 @@ import PaginatedTable, {
HeaderCell,
} from '../../../components/PaginatedTable';
import useWsProjects from './useWsProjects';
+import useSelected from '../../../util/useSelected';
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
import { getQSConfig, parseQueryString } from '../../../util/qs';
@@ -31,7 +32,6 @@ const QS_CONFIG = getQSConfig('project', {
function ProjectList() {
const location = useLocation();
const match = useRouteMatch();
- const [selected, setSelected] = useState([]);
const {
result: {
@@ -78,8 +78,15 @@ function ProjectList() {
const projects = useWsProjects(results);
- const isAllSelected =
- selected.length === projects.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ setSelected,
+ selectAll,
+ clearSelected,
+ } = useSelected(projects);
+
const {
isLoading: isDeleteLoading,
deleteItems: deleteProjects,
@@ -104,24 +111,12 @@ function ProjectList() {
const hasContentLoading = isDeleteLoading || isLoading;
const canAdd = actions && actions.POST;
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...projects] : []);
- };
-
- const handleSelect = row => {
- if (selected.some(s => s.id === row.id)) {
- setSelected(selected.filter(s => s.id !== row.id));
- } else {
- setSelected(selected.concat(row));
- }
- };
-
const deleteDetailsRequests = relatedResourceDeleteRequests.project(
selected[0]
);
return (
-
+ <>
-
+ >
);
}
diff --git a/awx/ui_next/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.jsx b/awx/ui_next/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.jsx
index 9f15164b86..04a9b964af 100644
--- a/awx/ui_next/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.jsx
+++ b/awx/ui_next/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.jsx
@@ -52,7 +52,7 @@ function SubscriptionModal({
[]
);
- const { selected, handleSelect } = useSelected(subscriptions);
+ const { selected, setSelected } = useSelected(subscriptions);
function handleConfirm() {
const [subscription] = selected;
@@ -64,6 +64,14 @@ function SubscriptionModal({
fetchSubscriptions();
}, [fetchSubscriptions]);
+ const handleSelect = item => {
+ if (selected.some(s => s.pool_id === item.pool_id)) {
+ setSelected(selected.filter(s => s.pool_id !== item.pool_id));
+ } else {
+ setSelected(selected.concat(item));
+ }
+ };
+
useEffect(() => {
if (selectedSubscription?.pool_id) {
handleSelect({ pool_id: selectedSubscription.pool_id });
diff --git a/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx b/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx
index 6bfbe89005..36c43a1525 100644
--- a/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx
+++ b/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment, useState, useEffect, useCallback } from 'react';
+import React, { useEffect, useCallback } from 'react';
import { useLocation, useRouteMatch } from 'react-router-dom';
import { t } from '@lingui/macro';
@@ -17,6 +17,7 @@ import {
ToolbarAddButton,
ToolbarDeleteButton,
} from '../../../components/PaginatedDataList';
+import useSelected from '../../../util/useSelected';
import { getQSConfig, parseQueryString } from '../../../util/qs';
import TeamListItem from './TeamListItem';
@@ -30,7 +31,6 @@ const QS_CONFIG = getQSConfig('team', {
function TeamList() {
const location = useLocation();
const match = useRouteMatch();
- const [selected, setSelected] = useState([]);
const {
result: {
@@ -75,7 +75,14 @@ function TeamList() {
fetchTeams();
}, [fetchTeams]);
- const isAllSelected = selected.length === teams.length && selected.length > 0;
+ const {
+ selected,
+ isAllSelected,
+ handleSelect,
+ selectAll,
+ clearSelected,
+ } = useSelected(teams);
+
const {
isLoading: isDeleteLoading,
deleteItems: deleteTeams,
@@ -94,26 +101,14 @@ function TeamList() {
const handleTeamDelete = async () => {
await deleteTeams();
- setSelected([]);
+ clearSelected();
};
const hasContentLoading = isDeleteLoading || isLoading;
const canAdd = actions && actions.POST;
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...teams] : []);
- };
-
- const handleSelect = row => {
- if (selected.some(s => s.id === row.id)) {
- setSelected(selected.filter(s => s.id !== row.id));
- } else {
- setSelected(selected.concat(row));
- }
- };
-
return (
-
+ <>
-
+ >
);
}
diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyList.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyList.jsx
index 55a6e9a68d..ff86fa7b7d 100644
--- a/awx/ui_next/src/screens/Template/Survey/SurveyList.jsx
+++ b/awx/ui_next/src/screens/Template/Survey/SurveyList.jsx
@@ -19,6 +19,7 @@ import { ToolbarAddButton } from '../../../components/PaginatedDataList';
import SurveyListItem from './SurveyListItem';
import SurveyToolbar from './SurveyToolbar';
import SurveyPreviewModal from './SurveyPreviewModal';
+import useSelected from '../../../util/useSelected';
const Button = styled(_Button)`
margin: 20px;
@@ -36,15 +37,16 @@ function SurveyList({
const match = useRouteMatch();
const questions = survey?.spec || [];
- const [selected, setSelected] = useState([]);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isPreviewModalOpen, setIsPreviewModalOpen] = useState(false);
- const isAllSelected =
- selected.length === questions?.length && selected.length > 0;
- const handleSelectAll = isSelected => {
- setSelected(isSelected ? [...questions] : []);
- };
+ const {
+ selected,
+ isAllSelected,
+ setSelected,
+ selectAll,
+ clearSelected,
+ } = useSelected(questions);
const handleSelect = item => {
if (selected.some(q => q.variable === item.variable)) {
@@ -61,7 +63,7 @@ function SurveyList({
await updateSurvey(questions.filter(q => !selected.includes(q)));
}
setIsDeleteModalOpen(false);
- setSelected([]);
+ clearSelected();
};
const moveUp = question => {
@@ -91,7 +93,7 @@ function SurveyList({
isOpen={isDeleteModalOpen}
onClose={() => {
setIsDeleteModalOpen(false);
- setSelected([]);
+ clearSelected();
}}
actions={[