Skip to content

Commit

Permalink
[MTV-1969] Add bulk select dropdown for selectable tables
Browse files Browse the repository at this point in the history
Signed-off-by: Jeff Puzzo <jpuzzo@redhat.com>
  • Loading branch information
jpuzz0 committed Jan 30, 2025
1 parent e763870 commit b6e322a
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"{{total}} VM": "{{total}} VM",
"{{total}} VM_plural": "{{total}} VMs",
"{{total}} VMs": "{{total}} VMs",
"{{vmCount}} VMs selected": "{{vmCount}} VMs selected",
"{children}": "{children}",
"24 hours": "24 hours",
"31 days": "31 days",
Expand Down
1 change: 1 addition & 0 deletions packages/forklift-console-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@kubev2v/types": "0.0.20",
"@openshift-console/dynamic-plugin-sdk": "1.6.0",
"@patternfly/react-charts": "7.4.1",
"@patternfly/react-component-groups": "^5.5.8",
"@patternfly/react-core": "^5.1.1",
"@patternfly/react-icons": "^5.1.1",
"@patternfly/react-table": "^5.1.1",
Expand Down
102 changes: 60 additions & 42 deletions packages/forklift-console-plugin/src/components/page/StandardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ import {
ValueMatcher,
withTr,
} from '@kubev2v/common';
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups';
import {
Level,
LevelItem,
PageSection,
Pagination,
Split,
Title,
Toolbar,
ToolbarContent,
Expand Down Expand Up @@ -224,11 +226,6 @@ export interface StandardPageProps<T> {
* Expanded ids
*/
expandedIds?: string[];

/**
* Label to show count of selected items
*/
selectedCountLabel?: (selectedIdCount: number) => string;
}

/**
Expand Down Expand Up @@ -286,7 +283,7 @@ export function StandardPage<T>({
expandedIds,
className,
selectedIds,
selectedCountLabel,
onSelect,
}: StandardPageProps<T>) {
const { t } = useForkliftTranslation();
const [sortedData, setSortedData] = useState([]);
Expand Down Expand Up @@ -340,6 +337,8 @@ export function StandardPage<T>({
const errorFetchingData = error;
const noResults = loaded && !error && sortedData.length == 0;
const noMatchingResults = loaded && !error && filteredData.length === 0 && sortedData.length > 0;
const pageDataIds = pageData.map(toId);
const filteredDataIds = filteredData.map(toId);

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

const onBulkSelect = (value: BulkSelectValue) => {
value === BulkSelectValue.none && onSelect([]);
value === BulkSelectValue.all && onSelect(filteredDataIds);
value === BulkSelectValue.nonePage &&
onSelect(selectedIds.filter((item) => !pageDataIds.includes(item)));
value === BulkSelectValue.page &&
onSelect(Array.from(new Set([...selectedIds, ...pageDataIds])));
};

return (
<span className={className}>
{title && (
Expand All @@ -382,49 +390,59 @@ export function StandardPage<T>({
<PageSection variant="light">
<Toolbar clearAllFilters={clearAllFilters} clearFiltersButtonText={t('Clear all filters')}>
<ToolbarContent>
<ToolbarToggleGroup toggleIcon={<FilterIcon />} breakpoint="xl">
{primaryFilters.length > 0 && (
<FilterGroup
fieldFilters={primaryFilters}
onFilterUpdate={setSelectedFilters}
selectedFilters={selectedFilters}
supportedFilterTypes={supportedFilters}
/>
)}
<AttributeValueFilter
fieldFilters={fields
.filter(({ filter }) => filter && !filter.primary && !filter.standalone)
.map(toFieldFilter(flatData))}
onFilterUpdate={setSelectedFilters}
selectedFilters={selectedFilters}
supportedFilterTypes={supportedFilters}
<Split hasGutter>
<BulkSelect
canSelectAll
selectedCount={selectedIds.length}
pageCount={pageDataIds.length}
totalCount={filteredDataIds.length}
onSelect={onBulkSelect}
{...(pageDataIds.length && {
pageSelected: pageDataIds.every((item) => selectedIds.includes(item)),
pagePartiallySelected:
pageDataIds.some((item) => selectedIds.includes(item)) &&
!pageDataIds.every((item) => selectedIds.includes(item)),
})}
/>
{!!fields.find((field) => field.filter?.standalone) && (
<FilterGroup

<ToolbarToggleGroup toggleIcon={<FilterIcon />} breakpoint="xl">
{primaryFilters.length > 0 && (
<FilterGroup
fieldFilters={primaryFilters}
onFilterUpdate={setSelectedFilters}
selectedFilters={selectedFilters}
supportedFilterTypes={supportedFilters}
/>
)}
<AttributeValueFilter
fieldFilters={fields
.filter((field) => field.filter?.standalone)
.filter(({ filter }) => filter && !filter.primary && !filter.standalone)
.map(toFieldFilter(flatData))}
onFilterUpdate={setSelectedFilters}
selectedFilters={selectedFilters}
supportedFilterTypes={supportedFilters}
/>
)}
<ManageColumnsToolbar
resourceFields={fields}
defaultColumns={fieldsMetadata}
setColumns={setFields}
/>
{GlobalActionToolbarItems?.length > 0 &&
GlobalActionToolbarItems.map((Action, index) => (
<Action key={index} dataOnScreen={showPagination ? pageData : filteredData} />
))}
</ToolbarToggleGroup>

{selectedCountLabel && (
<ToolbarItem className="forklift-page__toolbar-item__selected-count">
{selectedCountLabel(selectedIds.length ?? 0)}
</ToolbarItem>
)}
{!!fields.find((field) => field.filter?.standalone) && (
<FilterGroup
fieldFilters={fields
.filter((field) => field.filter?.standalone)
.map(toFieldFilter(flatData))}
onFilterUpdate={setSelectedFilters}
selectedFilters={selectedFilters}
supportedFilterTypes={supportedFilters}
/>
)}
<ManageColumnsToolbar
resourceFields={fields}
defaultColumns={fieldsMetadata}
setColumns={setFields}
/>
{GlobalActionToolbarItems?.length > 0 &&
GlobalActionToolbarItems.map((Action, index) => (
<Action key={index} dataOnScreen={showPagination ? pageData : filteredData} />
))}
</ToolbarToggleGroup>
</Split>

{showPagination && (
<ToolbarItem variant="pagination">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,11 @@ export function withRowSelection<T>({
return Enhanced;
}

export function withHeaderSelection<T>({
HeaderMapper,
isSelected,
isExpanded,
toggleSelectFor,
canSelect,
}) {
export function withHeaderSelection<T>({ HeaderMapper, isExpanded }) {
const Enhanced = ({ dataOnScreen, ...other }: TableViewHeaderProps<T>) => {
const selectableItems = dataOnScreen.filter(canSelect);
const allSelected = selectableItems.every((it) => isSelected(it));
return (
<>
{isExpanded && <Th />}
{isSelected && (
<Th
select={{
onSelect: () => toggleSelectFor(selectableItems),
isSelected: allSelected,
isHeaderSelectDisabled: !selectableItems?.length, // Disable if no selectable items
}}
/>
)}
<HeaderMapper {...{ ...other, dataOnScreen }} />
</>
);
Expand Down Expand Up @@ -181,17 +164,15 @@ export function withIdBasedSelection<T>({

const HeaderMapper = withHeaderSelection({
HeaderMapper: props.HeaderMapper ?? DefaultHeader,
canSelect,
isSelected,
isExpanded,
toggleSelectFor,
});

return (
<StandardPage
{...rest}
expandedIds={expandedIds}
selectedIds={selectedIds}
onSelect={setSelectedIds}
toId={toId}
RowMapper={RowMapper}
HeaderMapper={HeaderMapper}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,7 @@ export const ProviderVirtualMachinesList: React.FC<{
initialSelectedIds?: string[];
showActions: boolean;
className?: string;
selectedCountLabel?: (selectedIdCount: number) => string;
}> = ({
title,
name,
namespace,
onSelect,
initialSelectedIds,
showActions,
className,
selectedCountLabel,
}) => {
}> = ({ title, name, namespace, onSelect, initialSelectedIds, showActions, className }) => {
const [provider, providerLoaded, providerLoadError] = useK8sWatchResource<V1beta1Provider>({
groupVersionKind: ProviderModelGroupVersionKind,
namespaced: true,
Expand All @@ -47,7 +37,6 @@ export const ProviderVirtualMachinesList: React.FC<{
initialSelectedIds={initialSelectedIds}
showActions={showActions}
className={className}
selectedCountLabel={selectedCountLabel}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export interface ProviderVirtualMachinesListProps {
onSelect: (selectedVms: VmData[]) => void;
initialSelectedIds: string[];
showActions: boolean;
selectedCountLabel?: (selectedIdCount: number) => string;
}

export const MemoizedProviderVirtualMachinesList = memo(
Expand All @@ -21,7 +20,6 @@ export const MemoizedProviderVirtualMachinesList = memo(
onSelect,
initialSelectedIds,
showActions,
selectedCountLabel,
}: ProviderVirtualMachinesListProps) => {
return (
<ProviderVirtualMachinesList
Expand All @@ -31,7 +29,6 @@ export const MemoizedProviderVirtualMachinesList = memo(
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
showActions={showActions}
selectedCountLabel={selectedCountLabel}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,6 @@ export const SelectSourceProvider: React.FC<{
}
initialSelectedIds={filterState.selectedVMs.map((vm) => vm.vm.id)}
showActions={false}
selectedCountLabel={(selectedIdCount) =>
t('{{vmCount}} VMs selected', { vmCount: selectedIdCount })
}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export interface ProviderVirtualMachinesProps {
initialSelectedIds?: string[];
showActions: boolean;
className?: string;
selectedCountLabel?: (selectedIdCount: number) => string;
}

export const ProviderVirtualMachines: React.FC<{ name: string; namespace: string }> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export interface ProviderVirtualMachinesListProps {
initialSelectedIds?: string[];
showActions: boolean;
className?: string;
selectedCountLabel?: (selectedIdCount: number) => string;
}

export const toId = (item: VmData) => item.vm.id;
Expand All @@ -47,7 +46,6 @@ export const ProviderVirtualMachinesList: FC<ProviderVirtualMachinesListProps> =
initialSelectedIds,
showActions,
className,
selectedCountLabel,
}) => {
const { t } = useForkliftTranslation();

Expand Down Expand Up @@ -98,7 +96,6 @@ export const ProviderVirtualMachinesList: FC<ProviderVirtualMachinesListProps> =
page={1}
expandedIds={initialExpandedIds_}
ExpandedComponent={ConcernsTable}
selectedCountLabel={selectedCountLabel}
/>
);
};
Expand Down

0 comments on commit b6e322a

Please sign in to comment.