Skip to content

Commit edfe9b4

Browse files
committed
fix: implement DragDropSort and BulkSelect
1 parent 59f4d82 commit edfe9b4

File tree

2 files changed

+76
-98
lines changed

2 files changed

+76
-98
lines changed

packages/module/src/ColumnManagement/ColumnManagement.test.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ import userEvent from '@testing-library/user-event';
33
import '@testing-library/jest-dom';
44
import ColumnManagement from './ColumnManagement';
55

6+
jest.mock('@patternfly/react-drag-drop', () => {
7+
const originalModule = jest.requireActual('@patternfly/react-drag-drop');
8+
return {
9+
...originalModule,
10+
DragDropSort: ({ onDrop, items }) => {
11+
const handleDrop = () => {
12+
const reorderedItems = [ ...items ].reverse();
13+
onDrop({}, reorderedItems);
14+
};
15+
return <div onDrop={handleDrop}>{items.map(item => item.content)}</div>;
16+
},
17+
};
18+
});
19+
620
const mockColumns = [
721
{ key: 'name', title: 'Name', isShown: true, isShownByDefault: true },
822
{ key: 'status', title: 'Status', isShown: true, isShownByDefault: true },
@@ -43,11 +57,11 @@ describe('Column', () => {
4357

4458
it('selects all columns', async () => {
4559
render(<ColumnManagement columns={mockColumns} />);
46-
const menuToggle = screen.getByLabelText('Select all').closest('button');
60+
const menuToggle = screen.getByLabelText('Bulk select toggle');
4761
if (menuToggle) {
4862
await userEvent.click(menuToggle);
4963
}
50-
const selectAllButton = screen.getByText('Select all');
64+
const selectAllButton = screen.getByText('Select all (3)');
5165
await userEvent.click(selectAllButton);
5266
expect(screen.getByTestId('column-check-name')).toBeChecked();
5367
expect(screen.getByTestId('column-check-status')).toBeChecked();
@@ -56,11 +70,11 @@ describe('Column', () => {
5670

5771
it('selects no columns', async () => {
5872
render(<ColumnManagement columns={mockColumns} />);
59-
const menuToggle = screen.getByLabelText('Select all').closest('button');
73+
const menuToggle = screen.getByLabelText('Bulk select toggle');
6074
if (menuToggle) {
6175
await userEvent.click(menuToggle);
6276
}
63-
const selectNoneButton = screen.getByText('Select none');
77+
const selectNoneButton = screen.getByText('Select none (0)');
6478
await userEvent.click(selectNoneButton);
6579
expect(screen.getByTestId('column-check-name')).not.toBeChecked();
6680
expect(screen.getByTestId('column-check-status')).not.toBeChecked();

packages/module/src/ColumnManagement/ColumnManagement.tsx

Lines changed: 58 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,17 @@
11
import type { FunctionComponent } from 'react';
22
import { useState, useEffect } from 'react';
33
import {
4-
DataListItem,
54
DataList,
65
DataListItemRow,
76
DataListCheck,
87
DataListCell,
98
DataListItemCells,
10-
DataListControl,
11-
DataListDragButton,
129
Button,
1310
ButtonVariant,
14-
Title,
15-
Checkbox,
16-
Dropdown,
17-
DropdownItem,
18-
DropdownList,
19-
MenuToggle
11+
Title
2012
} from '@patternfly/react-core';
21-
import {
22-
DragDrop,
23-
Droppable,
24-
Draggable
25-
} from '@patternfly/react-core/deprecated';
13+
import { DragDropSort, Droppable } from '@patternfly/react-drag-drop';
14+
import BulkSelect, { BulkSelectValue } from '../BulkSelect';
2615

2716
export interface Column {
2817
/** Internal identifier of a column by which table displayed columns are filtered. */
@@ -69,7 +58,6 @@ const ColumnManagement: FunctionComponent<ColumnProps> = (
6958
onSave,
7059
onCancel }: ColumnProps) => {
7160

72-
const [ isDropdownOpen, setIsDropdownOpen ] = useState(false);
7361
const [ currentColumns, setCurrentColumns ] = useState(
7462
() => columns.map(column => ({ ...column, isShown: column.isShown ?? column.isShownByDefault, id: column.key }))
7563
);
@@ -89,103 +77,79 @@ const ColumnManagement: FunctionComponent<ColumnProps> = (
8977
onSelect?.(changedColumn);
9078
};
9179

92-
const onDrag = (source, dest) => {
93-
if (dest) {
94-
const newColumns = [ ...currentColumns ];
95-
const [ removed ] = newColumns.splice(source.index, 1);
96-
newColumns.splice(dest.index, 0, removed);
97-
setCurrentColumns(newColumns);
98-
onOrderChange?.(newColumns);
99-
return true;
100-
}
101-
return false;
80+
const onDrag = (_event, newOrder) => {
81+
const newColumns = newOrder.map(item => currentColumns.find(c => c.key === item.id));
82+
setCurrentColumns(newColumns);
83+
onOrderChange?.(newColumns);
10284
};
10385

10486
const handleSave = () => {
10587
onSave?.(currentColumns);
10688
}
10789

90+
const handleBulkSelect = (value: BulkSelectValue) => {
91+
const allSelected = value === 'all' || value === 'page';
92+
handleSelectAll(allSelected);
93+
};
94+
10895
const handleSelectAll = (select = true) => {
10996
const newColumns = currentColumns.map(c => ({ ...c, isShown: c.isUntoggleable ? c.isShown : select }));
11097
setCurrentColumns(newColumns);
11198
onSelectAll?.(newColumns);
11299
}
113100

114-
const isAllSelected = () => currentColumns.every(c => c.isShown || c.isUntoggleable);
115-
const isSomeSelected = () => currentColumns.some(c => c.isShown);
116-
117-
const dropdownItems = [
118-
<DropdownItem key="select-all" onClick={() => handleSelectAll(true)}>Select all</DropdownItem>,
119-
<DropdownItem key="deselect-all" onClick={() => handleSelectAll(false)}>Select none</DropdownItem>
120-
];
121-
122101
return (
123102
<>
124103
<Title headingLevel="h3">{title}</Title>
125104
{description && <div style={{ paddingBottom: '1rem' }}><p>{description}</p></div>}
126105
<div style={{ paddingBottom: '1rem' }}>
127-
<Dropdown
128-
onSelect={() => setIsDropdownOpen(false)}
129-
toggle={(toggleRef) => (
130-
<MenuToggle
131-
ref={toggleRef}
132-
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
133-
isExpanded={isDropdownOpen}
134-
>
135-
<div>
136-
<Checkbox
137-
aria-label="Select all"
138-
tabIndex={-1}
139-
isChecked={isAllSelected() ? true : isSomeSelected() ? null : false}
140-
id={`${ouiaId}-select-all-checkbox`}
141-
/>
142-
</div>
143-
</MenuToggle>
144-
)}
145-
isOpen={isDropdownOpen}
146-
>
147-
<DropdownList>{dropdownItems}</DropdownList>
148-
</Dropdown>
106+
<BulkSelect
107+
canSelectAll
108+
isDataPaginated={false}
109+
selectedCount={currentColumns.filter(({ isShown }) => isShown).length}
110+
totalCount={currentColumns.length}
111+
onSelect={handleBulkSelect}
112+
pageSelected={currentColumns.every((item) => item.isShown)}
113+
pagePartiallySelected={
114+
currentColumns.some((item) => item.isShown) && !currentColumns.every((item) => item.isShown)
115+
}
116+
/>
149117
</div>
150-
<DragDrop onDrop={onDrag}>
151-
<Droppable droppableId="draggable-datalist">
152-
<DataList aria-label="Selected columns" isCompact data-ouia-component-id={`${ouiaId}-column-list`}>
153-
{currentColumns.map((column, index) =>
154-
<Draggable key={column.key} id={column.key}>
155-
<DataListItem key={column.key} data-testid={`column-item-${column.key}`}>
156-
<DataListItemRow>
157-
<DataListControl>
158-
<DataListDragButton
159-
aria-label="Reorder"
160-
aria-labelledby={`${ouiaId}-column-${index}-label`}
161-
/>
162-
</DataListControl>
163-
<DataListCheck
164-
data-testid={`column-check-${column.key}`}
165-
isChecked={column.isShown}
166-
onChange={() => handleChange(index)}
167-
isDisabled={column.isUntoggleable}
168-
aria-labelledby={`${ouiaId}-column-${index}-label`}
169-
ouiaId={`${ouiaId}-column-${index}-checkbox`}
170-
id={`${ouiaId}-column-${index}-checkbox`}
171-
/>
172-
<DataListItemCells
173-
dataListCells={[
174-
<DataListCell key={column.key} data-ouia-component-id={`${ouiaId}-column-${index}-label`}>
175-
<label htmlFor={`${ouiaId}-column-${index}-checkbox`} id={`${ouiaId}-column-${index}-label`}>
176-
{column.title}
177-
</label>
178-
</DataListCell>
179-
]}
180-
/>
181-
</DataListItemRow>
182-
</DataListItem>
183-
</Draggable>
184-
)}
185-
</DataList>
186-
</Droppable>
187-
</DragDrop>
188-
<div style={{ display: 'flex', justifyContent: 'normal', paddingTop: '1rem' }}>
118+
<DragDropSort
119+
variant="DataList"
120+
items={currentColumns.map((column, index) => ({ id: column.key, content:
121+
<DataListItemRow>
122+
<DataListCheck
123+
data-testid={`column-check-${column.key}`}
124+
isChecked={column.isShown}
125+
onChange={() => handleChange(index)}
126+
isDisabled={column.isUntoggleable}
127+
aria-labelledby={`${ouiaId}-column-${index}-label`}
128+
ouiaId={`${ouiaId}-column-${index}-checkbox`}
129+
id={`${ouiaId}-column-${index}-checkbox`}
130+
/>
131+
<DataListItemCells
132+
dataListCells={[
133+
<DataListCell key={column.key} data-ouia-component-id={`${ouiaId}-column-${index}-label`}>
134+
<label htmlFor={`${ouiaId}-column-${index}-checkbox`} id={`${ouiaId}-column-${index}-label`}>
135+
{column.title}
136+
</label>
137+
</DataListCell>
138+
]}
139+
/>
140+
</DataListItemRow>
141+
}))}
142+
onDrop={onDrag}
143+
overlayProps={{ isCompact: true }}
144+
>
145+
<Droppable
146+
items={currentColumns.map((column) =>
147+
// eslint-disable-next-line no-console
148+
({ id: column.key, content: column.title })
149+
)}
150+
wrapper={<DataList aria-label="Selected columns" isCompact data-ouia-component-id={`${ouiaId}-column-list`}/>}
151+
/>
152+
</DragDropSort> <div style={{ display: 'flex', justifyContent: 'normal', paddingTop: '1rem' }}>
189153
<Button key="save" variant={ButtonVariant.primary} onClick={handleSave} ouiaId={`${ouiaId}-save-button`}>
190154
Save
191155
</Button>

0 commit comments

Comments
 (0)