Skip to content

Commit 8861843

Browse files
committed
All/selected row switch
1 parent 2b82628 commit 8861843

File tree

5 files changed

+181
-4
lines changed

5 files changed

+181
-4
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import { mount } from 'cypress/react';
3+
import { BasicExample } from '../../packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/AllSelectedExample';
4+
5+
describe('BasicExample Component', () => {
6+
beforeEach(() => {
7+
mount(<BasicExample />);
8+
});
9+
10+
it('displays all rows by default', () => {
11+
cy.get('table tbody tr').should('have.length', 5);
12+
});
13+
14+
it('filters to selected rows when toggled', () => {
15+
cy.get('table tbody tr')
16+
.first()
17+
.find('input[type="checkbox"]')
18+
.check();
19+
20+
cy.get('#selected-row-switch').click();
21+
22+
cy.get('table tbody tr').should('have.length', 1);
23+
});
24+
25+
it('switches back to all when last selected row is unselected', () => {
26+
cy.get('table tbody tr')
27+
.first()
28+
.find('input[type="checkbox"]')
29+
.check();
30+
31+
cy.get('#selected-row-switch').click();
32+
33+
cy.get('table tbody tr').should('have.length', 1);
34+
35+
cy.get('table tbody tr')
36+
.first()
37+
.find('input[type="checkbox"]')
38+
.uncheck();
39+
40+
cy.get('table tbody tr').should('have.length', 5);
41+
});
42+
});
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React, { useEffect, useMemo, useState } from 'react';
2+
import { Pagination, ToggleGroup, ToggleGroupItem, Tooltip } from '@patternfly/react-core';
3+
import { useDataViewPagination, useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
4+
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
5+
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
6+
import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
7+
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups';
8+
9+
const TOGGLE_ALL = 'all';
10+
const TOGGLE_SELECTED = 'selected';
11+
12+
const perPageOptions = [
13+
{ title: '5', value: 5 },
14+
{ title: '10', value: 10 }
15+
];
16+
17+
interface Repository {
18+
name: string;
19+
branches: string | null;
20+
prs: string | null;
21+
workspaces: string;
22+
lastCommit: string;
23+
};
24+
25+
const repositories: Repository[] = [
26+
{ name: 'Repository one', branches: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: 'Timestamp one' },
27+
{ name: 'Repository two', branches: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: 'Timestamp two' },
28+
{ name: 'Repository three', branches: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: 'Timestamp three' },
29+
{ name: 'Repository four', branches: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: 'Timestamp four' },
30+
{ name: 'Repository five', branches: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: 'Timestamp five' },
31+
{ name: 'Repository six', branches: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: 'Timestamp six' }
32+
];
33+
34+
const rows = repositories.map(item => Object.values(item));
35+
36+
const columns = [ 'Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit' ];
37+
38+
const ouiaId = 'LayoutExample';
39+
40+
export const BasicExample: React.FunctionComponent = () => {
41+
const pagination = useDataViewPagination({ perPage: 5 });
42+
const selection = useDataViewSelection({ matchOption: (a, b) => a[0] === b[0] });
43+
const [ selectedToggle, setSelectedToggle ] = useState(TOGGLE_ALL);
44+
const { selected, onSelect } = selection;
45+
const { page, perPage, onSetPage } = pagination;
46+
47+
const pageRows = useMemo(() => (selectedToggle === TOGGLE_SELECTED ? selected : rows).slice((page - 1) * perPage, ((page - 1) * perPage) + perPage), [ page, perPage, selectedToggle, selected ]);
48+
49+
const handleBulkSelect = (value: BulkSelectValue) => {
50+
value === BulkSelectValue.none && onSelect(false);
51+
value === BulkSelectValue.all && onSelect(true, rows);
52+
};
53+
54+
const handleItemClick = (event: React.MouseEvent<any> | React.KeyboardEvent | MouseEvent, _isSelected: boolean) => {
55+
const id = event.currentTarget.id;
56+
57+
if (id === TOGGLE_SELECTED && selected.length === 0) {
58+
return;
59+
}
60+
61+
if (selectedToggle !== id) {
62+
setSelectedToggle(id);
63+
onSetPage(undefined, 1);
64+
}
65+
};
66+
67+
useEffect(() => {
68+
if (selected.length === 0) {
69+
setSelectedToggle(TOGGLE_ALL);
70+
}
71+
}, [ selected ]);
72+
73+
return (
74+
<DataView selection={selection}>
75+
<DataViewToolbar
76+
ouiaId='DataViewHeader'
77+
bulkSelect={
78+
<BulkSelect
79+
canSelectAll
80+
isDataPaginated={false}
81+
totalCount={repositories.length}
82+
selectedCount={selected.length}
83+
onSelect={handleBulkSelect}
84+
/>
85+
}
86+
toggleGroup={
87+
<ToggleGroup aria-label="Toggle group to switch between all / selected table rows">
88+
<ToggleGroupItem
89+
text="All"
90+
buttonId={TOGGLE_ALL}
91+
isSelected={selectedToggle === TOGGLE_ALL}
92+
onChange={handleItemClick}
93+
/>
94+
<ToggleGroupItem
95+
id="selected-row-switch"
96+
text="Selected"
97+
buttonId={TOGGLE_SELECTED}
98+
isSelected={selectedToggle === TOGGLE_SELECTED}
99+
onChange={handleItemClick}
100+
aria-disabled={selected.length === 0}
101+
/>
102+
</ToggleGroup>
103+
}
104+
pagination={<Pagination perPageOptions={perPageOptions} itemCount={repositories.length} {...pagination} />}
105+
/>
106+
107+
{selected.length === 0 && (<Tooltip
108+
id="selected-row-switch-tooltip"
109+
content="Select at least one row to enable this filter"
110+
triggerRef={() => document.getElementById('selected-row-switch') as HTMLButtonElement}
111+
/>)}
112+
113+
<DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={pageRows} />
114+
</DataView>
115+
);
116+
};

packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ sortValue: 2
1515
propComponents: ['DataViewToolbar', 'DataViewFilters', 'DataViewTextFilter', 'DataViewCheckboxFilter']
1616
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md
1717
---
18-
import { useMemo } from 'react';
18+
import { useMemo, useState, useEffect } from 'react';
1919
import { BrowserRouter, useSearchParams } from 'react-router-dom';
20+
import { Pagination, ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
2021
import { useDataViewPagination, useDataViewSelection, useDataViewFilters } from '@patternfly/react-data-view/dist/dynamic/Hooks';
2122
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
2223
import { BulkSelect, BulkSelectValue, ErrorState, ResponsiveAction, ResponsiveActions, SkeletonTableHead, SkeletonTableBody } from '@patternfly/react-component-groups';
@@ -140,3 +141,14 @@ This example demonstrates the setup and usage of filters within the data view. I
140141
```js file="./FiltersExample.tsx"
141142

142143
```
144+
145+
146+
## All/selected data switch
147+
All/selected data switch allows users to toggle between displaying the entire table (All) and only the rows they have selected (Selected). If the user deselects the last selected row, the filter automatically switches back to All, displaying all table rows again. Until at least one row is selected, a tooltip with guidance is displayed, and the Selected button does not perform any action. The Selected button is intentionally not disabled for accessibility reasons but instead has `aria-disabled` set.
148+
149+
### All/selected example
150+
This example demonstrates the setup and usage of All/selected row switch.
151+
152+
```js file="./AllSelectedExample.tsx"
153+
154+
```

packages/module/patternfly-docs/generated/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ module.exports = {
22
'/extensions/data-view/toolbar/react': {
33
id: "Toolbar",
44
title: "Data view toolbar",
5-
toc: [[{"text":"Toolbar example"}],{"text":"Toolbar actions"},[{"text":"Actions example"}],{"text":"Pagination"},[{"text":"Pagination state"},{"text":"Pagination example"}],{"text":"Selection"},[{"text":"Selection state"},{"text":"Selection example"}],{"text":"Filters"},[{"text":"Filters state"},{"text":"Filtering example"}]],
6-
examples: ["Toolbar example","Actions example","Pagination example","Selection example","Filtering example"],
5+
toc: [[{"text":"Toolbar example"}],{"text":"Toolbar actions"},[{"text":"Actions example"}],{"text":"Pagination"},[{"text":"Pagination state"},{"text":"Pagination example"}],{"text":"Selection"},[{"text":"Selection state"},{"text":"Selection example"}],{"text":"Filters"},[{"text":"Filters state"},{"text":"Filtering example"}],{"text":"All/selected data switch"},[{"text":"All/selected example"}]],
6+
examples: ["Toolbar example","Actions example","Pagination example","Selection example","Filtering example","All/selected example"],
77
section: "extensions",
88
subsection: "Data view",
99
source: "react",

packages/module/src/DataViewToolbar/DataViewToolbar.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ export interface DataViewToolbarProps extends Omit<PropsWithChildren<ToolbarProp
1313
pagination?: React.ReactNode;
1414
/** React node to display actions */
1515
actions?: React.ReactNode;
16+
/** React node to display toggle group */
17+
toggleGroup?: React.ReactNode;
1618
/** React node to display filters */
1719
filters?: React.ReactNode;
1820
/** React node to display custom filter labels */
1921
customLabelGroupContent?: React.ReactNode;
2022
}
2123

22-
export const DataViewToolbar: React.FC<DataViewToolbarProps> = ({ className, ouiaId = 'DataViewToolbar', bulkSelect, actions, pagination, filters, customLabelGroupContent, clearAllFilters, children, ...props }: DataViewToolbarProps) => {
24+
export const DataViewToolbar: React.FC<DataViewToolbarProps> = ({ className, ouiaId = 'DataViewToolbar', bulkSelect, actions, toggleGroup, pagination, filters, customLabelGroupContent, clearAllFilters, children, ...props }: DataViewToolbarProps) => {
2325
const defaultClearFilters = useRef(
2426
<ToolbarItem>
2527
<Button ouiaId={`${ouiaId}-clear-all-filters`} variant="link" onClick={clearAllFilters} isInline>
@@ -45,6 +47,11 @@ export const DataViewToolbar: React.FC<DataViewToolbarProps> = ({ className, oui
4547
{actions}
4648
</ToolbarItem>
4749
)}
50+
{toggleGroup && (
51+
<ToolbarItem>
52+
{toggleGroup}
53+
</ToolbarItem>
54+
)}
4855
{pagination && (
4956
<ToolbarItem variant={ToolbarItemVariant.pagination} data-ouia-component-id={`${ouiaId}-pagination`}>
5057
{pagination}

0 commit comments

Comments
 (0)