clear list selection on navigate (first batch)

This commit is contained in:
Keith J. Grant
2021-05-25 16:48:38 -07:00
parent fef24355ab
commit 143a4a61b3
15 changed files with 182 additions and 194 deletions

View File

@@ -37,7 +37,6 @@ function DataListToolbar({
onExpand,
onSelectAll,
additionalControls,
qsConfig,
pagination,
}) {

View File

@@ -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,14 @@ function JobList({ defaultParams, showTypeColumn = false }) {
const jobs = useWsJobs(results, fetchJobsById, qsConfig);
const isAllSelected = selected.length === jobs.length && selected.length > 0;
const {
selected,
isAllSelected,
handleSelect,
setSelected,
selectAll,
clearSelected,
} = useSelected(jobs);
const {
error: cancelJobsError,
@@ -143,18 +148,6 @@ function JobList({ defaultParams, showTypeColumn = false }) {
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));
}
};
const cannotDeleteItems = selected.filter(job => isJobRunning(job.status));
return (
@@ -226,6 +219,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
<HeaderCell>{t`Actions`}</HeaderCell>
</HeaderRow>
}
clearSelected={clearSelected}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
renderToolbar={props => (
@@ -233,7 +227,7 @@ function JobList({ defaultParams, showTypeColumn = false }) {
{...props}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={handleSelectAll}
onSelectAll={selectAll}
qsConfig={qsConfig}
additionalControls={[
<ToolbarDeleteButton

View File

@@ -1,5 +1,5 @@
import 'styled-components/macro';
import React, { Fragment } from 'react';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { TableComposable, Tbody } from '@patternfly/react-table';
@@ -33,10 +33,16 @@ function PaginatedTable({
showPageSizeOptions,
renderToolbar,
emptyContentMessage,
clearSelected,
ouiaId,
}) {
const { search, pathname } = useLocation();
const history = useHistory();
const location = useLocation();
useEffect(() => {
clearSelected();
}, [location.search, clearSelected]);
const pushHistoryState = qs => {
history.push(qs ? `${pathname}?${qs}` : pathname);
@@ -127,7 +133,7 @@ function PaginatedTable({
);
return (
<Fragment>
<>
<ListHeader
itemCount={itemCount}
renderToolbar={renderToolbar}
@@ -159,7 +165,7 @@ function PaginatedTable({
onPerPageSelect={handleSetPageSize}
/>
) : null}
</Fragment>
</>
);
}
@@ -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 => <DataListToolbar {...props} />,
ouiaId: null,
clearSelected: () => {},
};
export { PaginatedTable as _PaginatedTable };

View File

@@ -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

View File

@@ -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 (
<Fragment>
<>
<Card>
<PaginatedTable
contentError={contentError}
@@ -188,7 +181,7 @@ function TemplateList({ defaultParams }) {
itemCount={count}
pluralizedItemName={t`Templates`}
qsConfig={qsConfig}
onRowClick={handleSelect}
clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -235,7 +228,7 @@ function TemplateList({ defaultParams }) {
{...props}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={handleSelectAll}
onSelectAll={selectAll}
qsConfig={qsConfig}
additionalControls={[
...(canAddJT || canAddWFJT ? [addButton] : []),
@@ -281,7 +274,7 @@ function TemplateList({ defaultParams }) {
{t`Failed to delete one or more templates.`}
<ErrorDetail error={deletionError} />
</AlertModal>
</Fragment>
</>
);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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={
<HeaderRow qsConfig={QS_CONFIG}>
<HeaderCell sortKey="name">{t`Name`}</HeaderCell>
@@ -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)}
/>
)}

View File

@@ -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"
/>
</>

View File

@@ -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

View File

@@ -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 (
<Fragment>
<>
<PageSection>
<Card>
<PaginatedTable
@@ -131,7 +126,7 @@ function ProjectList() {
itemCount={itemCount}
pluralizedItemName={t`Projects`}
qsConfig={QS_CONFIG}
onRowClick={handleSelect}
clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -182,7 +177,7 @@ function ProjectList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={handleSelectAll}
onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
@@ -239,7 +234,7 @@ function ProjectList() {
{t`Failed to delete one or more projects.`}
<ErrorDetail error={deletionError} />
</AlertModal>
</Fragment>
</>
);
}

View File

@@ -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 (
<Fragment>
<>
<PageSection>
<Card>
<PaginatedTable
@@ -123,7 +118,7 @@ function TeamList() {
itemCount={itemCount}
pluralizedItemName={t`Teams`}
qsConfig={QS_CONFIG}
onRowClick={handleSelect}
clearSelected={clearSelected}
toolbarSearchColumns={[
{
name: t`Name`,
@@ -161,7 +156,7 @@ function TeamList() {
{...props}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={handleSelectAll}
onSelectAll={selectAll}
qsConfig={QS_CONFIG}
additionalControls={[
...(canAdd
@@ -208,7 +203,7 @@ function TeamList() {
{t`Failed to delete one or more teams.`}
<ErrorDetail error={deletionError} />
</AlertModal>
</Fragment>
</>
);
}

View File

@@ -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={[
<Button
@@ -110,7 +112,7 @@ function SurveyList({
aria-label={t`cancel delete`}
onClick={() => {
setIsDeleteModalOpen(false);
setSelected([]);
clearSelected();
}}
>
{t`Cancel`}
@@ -181,7 +183,7 @@ function SurveyList({
<>
<SurveyToolbar
isAllSelected={isAllSelected}
onSelectAll={handleSelectAll}
onSelectAll={selectAll}
surveyEnabled={surveyEnabled}
onToggleSurvey={toggleSurvey}
isDeleteDisabled={selected?.length === 0}

View File

@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useCallback } from 'react';
/**
* useSelected hook provides a way to read and update a selected list
@@ -8,6 +8,7 @@ import { useState } from 'react';
* isAllSelected: boolean that indicates if all items are selected
* handleSelect: function that adds and removes items from selected list
* setSelected: setter function
* clearSelected: de-select all items
* }
*/
@@ -16,6 +17,9 @@ export default function useSelected(list = []) {
const isAllSelected = selected.length > 0 && selected.length === list.length;
const handleSelect = row => {
if (!row.id) {
throw new Error(`Selected row does not have an id`);
}
if (selected.some(s => s.id === row.id)) {
setSelected(prevState => [...prevState.filter(i => i.id !== row.id)]);
} else {
@@ -23,5 +27,23 @@ export default function useSelected(list = []) {
}
};
return { selected, isAllSelected, handleSelect, setSelected };
const selectAll = useCallback(
isSelected => {
setSelected(isSelected ? [...list] : []);
},
[list]
);
const clearSelected = useCallback(() => {
setSelected([]);
}, []);
return {
selected,
isAllSelected,
handleSelect,
setSelected,
selectAll,
clearSelected,
};
}