Skip to content

Commit b6e322a

Browse files
committed
[MTV-1969] Add bulk select dropdown for selectable tables
Signed-off-by: Jeff Puzzo <[email protected]>
1 parent e763870 commit b6e322a

File tree

9 files changed

+64
-86
lines changed

9 files changed

+64
-86
lines changed

packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
"{{total}} VM": "{{total}} VM",
1515
"{{total}} VM_plural": "{{total}} VMs",
1616
"{{total}} VMs": "{{total}} VMs",
17-
"{{vmCount}} VMs selected": "{{vmCount}} VMs selected",
1817
"{children}": "{children}",
1918
"24 hours": "24 hours",
2019
"31 days": "31 days",

packages/forklift-console-plugin/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@kubev2v/types": "0.0.20",
3333
"@openshift-console/dynamic-plugin-sdk": "1.6.0",
3434
"@patternfly/react-charts": "7.4.1",
35+
"@patternfly/react-component-groups": "^5.5.8",
3536
"@patternfly/react-core": "^5.1.1",
3637
"@patternfly/react-icons": "^5.1.1",
3738
"@patternfly/react-table": "^5.1.1",

packages/forklift-console-plugin/src/components/page/StandardPage.tsx

+60-42
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ import {
2929
ValueMatcher,
3030
withTr,
3131
} from '@kubev2v/common';
32+
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups';
3233
import {
3334
Level,
3435
LevelItem,
3536
PageSection,
3637
Pagination,
38+
Split,
3739
Title,
3840
Toolbar,
3941
ToolbarContent,
@@ -224,11 +226,6 @@ export interface StandardPageProps<T> {
224226
* Expanded ids
225227
*/
226228
expandedIds?: string[];
227-
228-
/**
229-
* Label to show count of selected items
230-
*/
231-
selectedCountLabel?: (selectedIdCount: number) => string;
232229
}
233230

234231
/**
@@ -286,7 +283,7 @@ export function StandardPage<T>({
286283
expandedIds,
287284
className,
288285
selectedIds,
289-
selectedCountLabel,
286+
onSelect,
290287
}: StandardPageProps<T>) {
291288
const { t } = useForkliftTranslation();
292289
const [sortedData, setSortedData] = useState([]);
@@ -340,6 +337,8 @@ export function StandardPage<T>({
340337
const errorFetchingData = error;
341338
const noResults = loaded && !error && sortedData.length == 0;
342339
const noMatchingResults = loaded && !error && filteredData.length === 0 && sortedData.length > 0;
340+
const pageDataIds = pageData.map(toId);
341+
const filteredDataIds = filteredData.map(toId);
343342

344343
const primaryFilters = fields
345344
.filter((field) => field.filter?.primary)
@@ -366,6 +365,15 @@ export function StandardPage<T>({
366365
setPage(page);
367366
};
368367

368+
const onBulkSelect = (value: BulkSelectValue) => {
369+
value === BulkSelectValue.none && onSelect([]);
370+
value === BulkSelectValue.all && onSelect(filteredDataIds);
371+
value === BulkSelectValue.nonePage &&
372+
onSelect(selectedIds.filter((item) => !pageDataIds.includes(item)));
373+
value === BulkSelectValue.page &&
374+
onSelect(Array.from(new Set([...selectedIds, ...pageDataIds])));
375+
};
376+
369377
return (
370378
<span className={className}>
371379
{title && (
@@ -382,49 +390,59 @@ export function StandardPage<T>({
382390
<PageSection variant="light">
383391
<Toolbar clearAllFilters={clearAllFilters} clearFiltersButtonText={t('Clear all filters')}>
384392
<ToolbarContent>
385-
<ToolbarToggleGroup toggleIcon={<FilterIcon />} breakpoint="xl">
386-
{primaryFilters.length > 0 && (
387-
<FilterGroup
388-
fieldFilters={primaryFilters}
389-
onFilterUpdate={setSelectedFilters}
390-
selectedFilters={selectedFilters}
391-
supportedFilterTypes={supportedFilters}
392-
/>
393-
)}
394-
<AttributeValueFilter
395-
fieldFilters={fields
396-
.filter(({ filter }) => filter && !filter.primary && !filter.standalone)
397-
.map(toFieldFilter(flatData))}
398-
onFilterUpdate={setSelectedFilters}
399-
selectedFilters={selectedFilters}
400-
supportedFilterTypes={supportedFilters}
393+
<Split hasGutter>
394+
<BulkSelect
395+
canSelectAll
396+
selectedCount={selectedIds.length}
397+
pageCount={pageDataIds.length}
398+
totalCount={filteredDataIds.length}
399+
onSelect={onBulkSelect}
400+
{...(pageDataIds.length && {
401+
pageSelected: pageDataIds.every((item) => selectedIds.includes(item)),
402+
pagePartiallySelected:
403+
pageDataIds.some((item) => selectedIds.includes(item)) &&
404+
!pageDataIds.every((item) => selectedIds.includes(item)),
405+
})}
401406
/>
402-
{!!fields.find((field) => field.filter?.standalone) && (
403-
<FilterGroup
407+
408+
<ToolbarToggleGroup toggleIcon={<FilterIcon />} breakpoint="xl">
409+
{primaryFilters.length > 0 && (
410+
<FilterGroup
411+
fieldFilters={primaryFilters}
412+
onFilterUpdate={setSelectedFilters}
413+
selectedFilters={selectedFilters}
414+
supportedFilterTypes={supportedFilters}
415+
/>
416+
)}
417+
<AttributeValueFilter
404418
fieldFilters={fields
405-
.filter((field) => field.filter?.standalone)
419+
.filter(({ filter }) => filter && !filter.primary && !filter.standalone)
406420
.map(toFieldFilter(flatData))}
407421
onFilterUpdate={setSelectedFilters}
408422
selectedFilters={selectedFilters}
409423
supportedFilterTypes={supportedFilters}
410424
/>
411-
)}
412-
<ManageColumnsToolbar
413-
resourceFields={fields}
414-
defaultColumns={fieldsMetadata}
415-
setColumns={setFields}
416-
/>
417-
{GlobalActionToolbarItems?.length > 0 &&
418-
GlobalActionToolbarItems.map((Action, index) => (
419-
<Action key={index} dataOnScreen={showPagination ? pageData : filteredData} />
420-
))}
421-
</ToolbarToggleGroup>
422-
423-
{selectedCountLabel && (
424-
<ToolbarItem className="forklift-page__toolbar-item__selected-count">
425-
{selectedCountLabel(selectedIds.length ?? 0)}
426-
</ToolbarItem>
427-
)}
425+
{!!fields.find((field) => field.filter?.standalone) && (
426+
<FilterGroup
427+
fieldFilters={fields
428+
.filter((field) => field.filter?.standalone)
429+
.map(toFieldFilter(flatData))}
430+
onFilterUpdate={setSelectedFilters}
431+
selectedFilters={selectedFilters}
432+
supportedFilterTypes={supportedFilters}
433+
/>
434+
)}
435+
<ManageColumnsToolbar
436+
resourceFields={fields}
437+
defaultColumns={fieldsMetadata}
438+
setColumns={setFields}
439+
/>
440+
{GlobalActionToolbarItems?.length > 0 &&
441+
GlobalActionToolbarItems.map((Action, index) => (
442+
<Action key={index} dataOnScreen={showPagination ? pageData : filteredData} />
443+
))}
444+
</ToolbarToggleGroup>
445+
</Split>
428446

429447
{showPagination && (
430448
<ToolbarItem variant="pagination">

packages/forklift-console-plugin/src/components/page/StandardPageWithSelection.tsx

+2-21
Original file line numberDiff line numberDiff line change
@@ -47,28 +47,11 @@ export function withRowSelection<T>({
4747
return Enhanced;
4848
}
4949

50-
export function withHeaderSelection<T>({
51-
HeaderMapper,
52-
isSelected,
53-
isExpanded,
54-
toggleSelectFor,
55-
canSelect,
56-
}) {
50+
export function withHeaderSelection<T>({ HeaderMapper, isExpanded }) {
5751
const Enhanced = ({ dataOnScreen, ...other }: TableViewHeaderProps<T>) => {
58-
const selectableItems = dataOnScreen.filter(canSelect);
59-
const allSelected = selectableItems.every((it) => isSelected(it));
6052
return (
6153
<>
6254
{isExpanded && <Th />}
63-
{isSelected && (
64-
<Th
65-
select={{
66-
onSelect: () => toggleSelectFor(selectableItems),
67-
isSelected: allSelected,
68-
isHeaderSelectDisabled: !selectableItems?.length, // Disable if no selectable items
69-
}}
70-
/>
71-
)}
7255
<HeaderMapper {...{ ...other, dataOnScreen }} />
7356
</>
7457
);
@@ -181,17 +164,15 @@ export function withIdBasedSelection<T>({
181164

182165
const HeaderMapper = withHeaderSelection({
183166
HeaderMapper: props.HeaderMapper ?? DefaultHeader,
184-
canSelect,
185-
isSelected,
186167
isExpanded,
187-
toggleSelectFor,
188168
});
189169

190170
return (
191171
<StandardPage
192172
{...rest}
193173
expandedIds={expandedIds}
194174
selectedIds={selectedIds}
175+
onSelect={setSelectedIds}
195176
toId={toId}
196177
RowMapper={RowMapper}
197178
HeaderMapper={HeaderMapper}

packages/forklift-console-plugin/src/modules/Plans/views/create/components/ProvidersVirtualMachinesList.tsx

+1-12
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,7 @@ export const ProviderVirtualMachinesList: React.FC<{
1616
initialSelectedIds?: string[];
1717
showActions: boolean;
1818
className?: string;
19-
selectedCountLabel?: (selectedIdCount: number) => string;
20-
}> = ({
21-
title,
22-
name,
23-
namespace,
24-
onSelect,
25-
initialSelectedIds,
26-
showActions,
27-
className,
28-
selectedCountLabel,
29-
}) => {
19+
}> = ({ title, name, namespace, onSelect, initialSelectedIds, showActions, className }) => {
3020
const [provider, providerLoaded, providerLoadError] = useK8sWatchResource<V1beta1Provider>({
3121
groupVersionKind: ProviderModelGroupVersionKind,
3222
namespaced: true,
@@ -47,7 +37,6 @@ export const ProviderVirtualMachinesList: React.FC<{
4737
initialSelectedIds={initialSelectedIds}
4838
showActions={showActions}
4939
className={className}
50-
selectedCountLabel={selectedCountLabel}
5140
/>
5241
);
5342
};

packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/MemoizedProviderVirtualMachinesList.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export interface ProviderVirtualMachinesListProps {
1010
onSelect: (selectedVms: VmData[]) => void;
1111
initialSelectedIds: string[];
1212
showActions: boolean;
13-
selectedCountLabel?: (selectedIdCount: number) => string;
1413
}
1514

1615
export const MemoizedProviderVirtualMachinesList = memo(
@@ -21,7 +20,6 @@ export const MemoizedProviderVirtualMachinesList = memo(
2120
onSelect,
2221
initialSelectedIds,
2322
showActions,
24-
selectedCountLabel,
2523
}: ProviderVirtualMachinesListProps) => {
2624
return (
2725
<ProviderVirtualMachinesList
@@ -31,7 +29,6 @@ export const MemoizedProviderVirtualMachinesList = memo(
3129
onSelect={onSelect}
3230
initialSelectedIds={initialSelectedIds}
3331
showActions={showActions}
34-
selectedCountLabel={selectedCountLabel}
3532
/>
3633
);
3734
},

packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/SelectSourceProvider.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,6 @@ export const SelectSourceProvider: React.FC<{
7171
}
7272
initialSelectedIds={filterState.selectedVMs.map((vm) => vm.vm.id)}
7373
showActions={false}
74-
selectedCountLabel={(selectedIdCount) =>
75-
t('{{vmCount}} VMs selected', { vmCount: selectedIdCount })
76-
}
7774
/>
7875
</>
7976
)}

packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/ProviderVirtualMachines.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export interface ProviderVirtualMachinesProps {
2424
initialSelectedIds?: string[];
2525
showActions: boolean;
2626
className?: string;
27-
selectedCountLabel?: (selectedIdCount: number) => string;
2827
}
2928

3029
export const ProviderVirtualMachines: React.FC<{ name: string; namespace: string }> = ({

packages/forklift-console-plugin/src/modules/Providers/views/details/tabs/VirtualMachines/components/ProviderVirtualMachinesList.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export interface ProviderVirtualMachinesListProps {
3232
initialSelectedIds?: string[];
3333
showActions: boolean;
3434
className?: string;
35-
selectedCountLabel?: (selectedIdCount: number) => string;
3635
}
3736

3837
export const toId = (item: VmData) => item.vm.id;
@@ -47,7 +46,6 @@ export const ProviderVirtualMachinesList: FC<ProviderVirtualMachinesListProps> =
4746
initialSelectedIds,
4847
showActions,
4948
className,
50-
selectedCountLabel,
5149
}) => {
5250
const { t } = useForkliftTranslation();
5351

@@ -98,7 +96,6 @@ export const ProviderVirtualMachinesList: FC<ProviderVirtualMachinesListProps> =
9896
page={1}
9997
expandedIds={initialExpandedIds_}
10098
ExpandedComponent={ConcernsTable}
101-
selectedCountLabel={selectedCountLabel}
10299
/>
103100
);
104101
};

0 commit comments

Comments
 (0)