Skip to content

Commit 9262517

Browse files
committed
feat(ws): added selectable rows to workspace list
Signed-off-by: paulovmr <[email protected]>
1 parent aa45741 commit 9262517

File tree

3 files changed

+150
-38
lines changed

3 files changed

+150
-38
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import {
3+
Button,
4+
DrawerActions,
5+
DrawerHead,
6+
DrawerPanelBody,
7+
DrawerPanelContent,
8+
Title,
9+
} from '@patternfly/react-core';
10+
import { WorkspaceAggregatedDetailsActions } from '~/app/pages/Workspaces/Details/WorkspaceAggregatedDetailsActions';
11+
12+
type WorkspaceAggregatedDetailsProps = {
13+
workspaceNames: string[];
14+
onCloseClick: React.MouseEventHandler;
15+
onDeleteClick: React.MouseEventHandler;
16+
};
17+
18+
export const WorkspaceAggregatedDetails: React.FunctionComponent<
19+
WorkspaceAggregatedDetailsProps
20+
> = ({ workspaceNames, onCloseClick, onDeleteClick }) => (
21+
<DrawerPanelContent>
22+
<DrawerHead>
23+
<Title headingLevel="h6">Multiple selected workspaces</Title>
24+
<WorkspaceAggregatedDetailsActions onDeleteClick={onDeleteClick} />
25+
<DrawerActions>
26+
<Button onClick={onCloseClick} aria-label="Clear workspoaces selection" variant="link">
27+
Clear selection
28+
</Button>
29+
</DrawerActions>
30+
</DrawerHead>
31+
<DrawerPanelBody>
32+
<Title headingLevel="h6" size="md">
33+
{'Selected workspaces: '}
34+
</Title>
35+
{workspaceNames.join(', ')}
36+
</DrawerPanelBody>
37+
</DrawerPanelContent>
38+
);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react';
2+
import {
3+
Dropdown,
4+
DropdownList,
5+
MenuToggle,
6+
DropdownItem,
7+
Flex,
8+
FlexItem,
9+
} from '@patternfly/react-core';
10+
11+
interface WorkspaceAggregatedDetailsActionsProps {
12+
onDeleteClick: React.MouseEventHandler;
13+
}
14+
15+
export const WorkspaceAggregatedDetailsActions: React.FC<
16+
WorkspaceAggregatedDetailsActionsProps
17+
> = ({ onDeleteClick }) => {
18+
const [isOpen, setOpen] = React.useState(false);
19+
20+
return (
21+
<Flex>
22+
<FlexItem>
23+
<Dropdown
24+
isOpen={isOpen}
25+
onSelect={() => setOpen(false)}
26+
onOpenChange={(open) => setOpen(open)}
27+
popperProps={{ position: 'end' }}
28+
toggle={(toggleRef) => (
29+
<MenuToggle
30+
variant="primary"
31+
ref={toggleRef}
32+
onClick={() => setOpen(!isOpen)}
33+
isExpanded={isOpen}
34+
aria-label="Workspace aggregated details action toggle"
35+
data-testid="workspace-aggregated-details-action-toggle"
36+
>
37+
Actions
38+
</MenuToggle>
39+
)}
40+
>
41+
<DropdownList>
42+
<DropdownItem
43+
id="workspace-aggregated-details-action-delete-button"
44+
aria-label="Delete selected workspace"
45+
key="delete-aggregated-workspace-button"
46+
onClick={onDeleteClick}
47+
>
48+
Delete selected
49+
</DropdownItem>
50+
</DropdownList>
51+
</Dropdown>
52+
</FlexItem>
53+
</Flex>
54+
);
55+
};

workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { WorkspaceConnectAction } from '~/app/pages/Workspaces/WorkspaceConnectA
4848
import { WorkspaceStartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStartActionModal';
4949
import { WorkspaceRestartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceRestartActionModal';
5050
import { WorkspaceStopActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStopActionModal';
51+
import { WorkspaceAggregatedDetails } from '~/app/pages/Workspaces/Details/WorkspaceAggregatedDetails';
5152
import { useNamespaceContext } from '~/app/context/NamespaceContextProvider';
5253
import { WorkspacesColumnNames } from '~/app/types';
5354
import Filter, { FilteredColumn } from 'shared/components/Filter';
@@ -112,17 +113,6 @@ export const Workspaces: React.FunctionComponent = () => {
112113
setWorkspaces(initialWorkspaces ?? []);
113114
}, [initialWorkspaces, initialWorkspacesLoaded]);
114115

115-
const selectWorkspace = React.useCallback(
116-
(newSelectedWorkspace: Workspace | null) => {
117-
if (selectedWorkspace?.name === newSelectedWorkspace?.name) {
118-
setSelectedWorkspace(null);
119-
} else {
120-
setSelectedWorkspace(newSelectedWorkspace);
121-
}
122-
},
123-
[selectedWorkspace],
124-
);
125-
126116
const setWorkspaceExpanded = (workspace: Workspace, isExpanding = true) =>
127117
setExpandedWorkspacesNames((prevExpanded) => {
128118
const newExpandedWorkspacesNames = prevExpanded.filter((wsName) => wsName !== workspace.name);
@@ -239,11 +229,6 @@ export const Workspaces: React.FunctionComponent = () => {
239229

240230
// Actions
241231

242-
const viewDetailsClick = React.useCallback((workspace: Workspace) => {
243-
setSelectedWorkspace(workspace);
244-
setActiveActionType(ActionType.ViewDetails);
245-
}, []);
246-
247232
const editAction = React.useCallback((workspace: Workspace) => {
248233
setSelectedWorkspace(workspace);
249234
setActiveActionType(ActionType.Edit);
@@ -280,11 +265,6 @@ export const Workspaces: React.FunctionComponent = () => {
280265

281266
const workspaceDefaultActions = (workspace: Workspace): IActions => {
282267
const workspaceActions = [
283-
{
284-
id: 'view-details',
285-
title: 'View Details',
286-
onClick: () => viewDetailsClick(workspace),
287-
},
288268
{
289269
id: 'edit',
290270
title: 'Edit',
@@ -427,25 +407,49 @@ export const Workspaces: React.FunctionComponent = () => {
427407
setPage(newPage);
428408
};
429409

430-
const workspaceDetailsContent = (
431-
<>
432-
{selectedWorkspace && (
433-
<WorkspaceDetails
434-
workspace={selectedWorkspace}
435-
onCloseClick={() => selectWorkspace(null)}
436-
onEditClick={() => editAction(selectedWorkspace)}
437-
onDeleteClick={() => handleDeleteClick(selectedWorkspace)}
438-
/>
439-
)}
440-
</>
441-
);
410+
const [selectedWorkspaceNames, setSelectedWorkspaceNames] = React.useState<string[]>([]);
411+
const setWorkspaceSelected = (workspace: Workspace, isSelecting = true) =>
412+
setSelectedWorkspaceNames((prevSelected) => {
413+
const otherSelectedWorkspaceNames = prevSelected.filter((w) => w !== workspace.name);
414+
return isSelecting
415+
? [...otherSelectedWorkspaceNames, workspace.name]
416+
: otherSelectedWorkspaceNames;
417+
});
418+
const selectAllWorkspaces = (isSelecting = true) =>
419+
setSelectedWorkspaceNames(isSelecting ? sortedWorkspaces.map((r) => r.name) : []);
420+
const areAllWorkspacesSelected = selectedWorkspaceNames.length === sortedWorkspaces.length;
421+
const isWorkspaceSelected = (workspace: Workspace) =>
422+
selectedWorkspaceNames.includes(workspace.name);
423+
424+
const workspaceDetailsContent = () => {
425+
const selectedWorkspaceForDetails =
426+
selectedWorkspaceNames.length === 1
427+
? sortedWorkspaces.find((w) => w.name === selectedWorkspaceNames[0])
428+
: undefined;
429+
return (
430+
<>
431+
{selectedWorkspaceForDetails && (
432+
<WorkspaceDetails
433+
workspace={selectedWorkspaceForDetails}
434+
onCloseClick={() => selectAllWorkspaces(false)}
435+
onEditClick={() => editAction(selectedWorkspaceForDetails)}
436+
onDeleteClick={() => handleDeleteClick(selectedWorkspaceForDetails)}
437+
/>
438+
)}
439+
{selectedWorkspaceNames.length > 1 && (
440+
<WorkspaceAggregatedDetails
441+
workspaceNames={selectedWorkspaceNames}
442+
onCloseClick={() => selectAllWorkspaces(false)}
443+
onDeleteClick={() => console.log('Delete selected workspaces')}
444+
/>
445+
)}
446+
</>
447+
);
448+
};
442449

443450
return (
444-
<Drawer
445-
isInline
446-
isExpanded={selectedWorkspace != null && activeActionType === ActionType.ViewDetails}
447-
>
448-
<DrawerContent panelContent={workspaceDetailsContent}>
451+
<Drawer isInline isExpanded={selectedWorkspaceNames.length >= 1}>
452+
<DrawerContent panelContent={workspaceDetailsContent()}>
449453
<DrawerContentBody>
450454
<PageSection isFilled>
451455
<Content>
@@ -462,6 +466,13 @@ export const Workspaces: React.FunctionComponent = () => {
462466
<Table aria-label="Sortable table" ouiaId="SortableTable">
463467
<Thead>
464468
<Tr>
469+
<Th
470+
select={{
471+
onSelect: (_event, isSelecting) => selectAllWorkspaces(isSelecting),
472+
isSelected: areAllWorkspacesSelected,
473+
}}
474+
aria-label="Row select"
475+
/>
465476
<Th screenReaderText="expand-action" />
466477
{Object.values(columnNames).map((columnName, index) => (
467478
<Th
@@ -482,6 +493,14 @@ export const Workspaces: React.FunctionComponent = () => {
482493
data-testid="table-body"
483494
>
484495
<Tr id={`workspaces-table-row-${rowIndex + 1}`}>
496+
<Td
497+
select={{
498+
rowIndex,
499+
onSelect: (_event, isSelecting) =>
500+
setWorkspaceSelected(workspace, isSelecting),
501+
isSelected: isWorkspaceSelected(workspace),
502+
}}
503+
/>
485504
<Td
486505
expand={{
487506
rowIndex,

0 commit comments

Comments
 (0)