@patternfly/react-data-view 6.1.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/DataViewToolbar/DataViewToolbar.d.ts +2 -0
- package/dist/cjs/DataViewToolbar/DataViewToolbar.js +2 -1
- package/dist/cjs/Hooks/filters.js +1 -1
- package/dist/cjs/Hooks/filters.test.js +25 -0
- package/dist/esm/DataViewToolbar/DataViewToolbar.d.ts +2 -0
- package/dist/esm/DataViewToolbar/DataViewToolbar.js +2 -1
- package/dist/esm/Hooks/filters.js +1 -1
- package/dist/esm/Hooks/filters.test.js +25 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/patternfly-docs/content/extensions/data-view/examples/Toolbar/AllSelectedExample.tsx +116 -0
- package/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md +13 -1
- package/release.config.js +1 -1
- package/src/DataViewToolbar/DataViewToolbar.tsx +8 -1
- package/src/Hooks/filters.test.tsx +27 -0
- package/src/Hooks/filters.ts +1 -1
|
@@ -12,6 +12,8 @@ export interface DataViewToolbarProps extends Omit<PropsWithChildren<ToolbarProp
|
|
|
12
12
|
pagination?: React.ReactNode;
|
|
13
13
|
/** React node to display actions */
|
|
14
14
|
actions?: React.ReactNode;
|
|
15
|
+
/** React node to display toggle group */
|
|
16
|
+
toggleGroup?: React.ReactNode;
|
|
15
17
|
/** React node to display filters */
|
|
16
18
|
filters?: React.ReactNode;
|
|
17
19
|
/** React node to display custom filter labels */
|
|
@@ -38,7 +38,7 @@ exports.DataViewToolbar = void 0;
|
|
|
38
38
|
const react_1 = __importStar(require("react"));
|
|
39
39
|
const react_core_1 = require("@patternfly/react-core");
|
|
40
40
|
const DataViewToolbar = (_a) => {
|
|
41
|
-
var { className, ouiaId = 'DataViewToolbar', bulkSelect, actions, pagination, filters, customLabelGroupContent, clearAllFilters, children } = _a, props = __rest(_a, ["className", "ouiaId", "bulkSelect", "actions", "pagination", "filters", "customLabelGroupContent", "clearAllFilters", "children"]);
|
|
41
|
+
var { className, ouiaId = 'DataViewToolbar', bulkSelect, actions, toggleGroup, pagination, filters, customLabelGroupContent, clearAllFilters, children } = _a, props = __rest(_a, ["className", "ouiaId", "bulkSelect", "actions", "toggleGroup", "pagination", "filters", "customLabelGroupContent", "clearAllFilters", "children"]);
|
|
42
42
|
const defaultClearFilters = (0, react_1.useRef)(react_1.default.createElement(react_core_1.ToolbarItem, null,
|
|
43
43
|
react_1.default.createElement(react_core_1.Button, { ouiaId: `${ouiaId}-clear-all-filters`, variant: "link", onClick: clearAllFilters, isInline: true }, "Clear filters")));
|
|
44
44
|
return (react_1.default.createElement(react_core_1.Toolbar, Object.assign({ ouiaId: ouiaId, className: className, customLabelGroupContent: customLabelGroupContent !== null && customLabelGroupContent !== void 0 ? customLabelGroupContent : defaultClearFilters.current }, props),
|
|
@@ -46,6 +46,7 @@ const DataViewToolbar = (_a) => {
|
|
|
46
46
|
bulkSelect && (react_1.default.createElement(react_core_1.ToolbarItem, { "data-ouia-component-id": `${ouiaId}-bulk-select` }, bulkSelect)),
|
|
47
47
|
filters && (react_1.default.createElement(react_core_1.ToolbarItem, null, filters)),
|
|
48
48
|
actions && (react_1.default.createElement(react_core_1.ToolbarItem, null, actions)),
|
|
49
|
+
toggleGroup && (react_1.default.createElement(react_core_1.ToolbarItem, null, toggleGroup)),
|
|
49
50
|
pagination && (react_1.default.createElement(react_core_1.ToolbarItem, { variant: react_core_1.ToolbarItemVariant.pagination, "data-ouia-component-id": `${ouiaId}-pagination` }, pagination)),
|
|
50
51
|
children)));
|
|
51
52
|
};
|
|
@@ -7,8 +7,8 @@ const useDataViewFilters = ({ initialFilters = {}, searchParams, setSearchParams
|
|
|
7
7
|
const isUrlSyncEnabled = (0, react_1.useMemo)(() => searchParams && !!setSearchParams, [searchParams, setSearchParams]);
|
|
8
8
|
const getInitialFilters = (0, react_1.useCallback)(() => isUrlSyncEnabled
|
|
9
9
|
? Object.keys(initialFilters).reduce((loadedFilters, key) => {
|
|
10
|
-
const urlValue = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get(key);
|
|
11
10
|
const isArrayFilter = Array.isArray(initialFilters[key]);
|
|
11
|
+
const urlValue = isArrayFilter ? searchParams === null || searchParams === void 0 ? void 0 : searchParams.getAll(key) : searchParams === null || searchParams === void 0 ? void 0 : searchParams.get(key);
|
|
12
12
|
// eslint-disable-next-line no-nested-ternary
|
|
13
13
|
loadedFilters[key] = urlValue
|
|
14
14
|
? (isArrayFilter && !Array.isArray(urlValue) ? [urlValue] : urlValue)
|
|
@@ -42,6 +42,31 @@ describe('useDataViewFilters', () => {
|
|
|
42
42
|
(0, react_1.act)(() => result.current.onSetFilters({ search: 'new search' }));
|
|
43
43
|
expect(setSearchParams).toHaveBeenCalled();
|
|
44
44
|
});
|
|
45
|
+
it('should sync with URL search params with non array value', () => {
|
|
46
|
+
const searchParams = new URLSearchParams();
|
|
47
|
+
searchParams.set('test', 'foo');
|
|
48
|
+
const setSearchParams = jest.fn();
|
|
49
|
+
const props = {
|
|
50
|
+
initialFilters: { test: '' },
|
|
51
|
+
searchParams,
|
|
52
|
+
setSearchParams,
|
|
53
|
+
};
|
|
54
|
+
const { result } = (0, react_1.renderHook)(() => (0, filters_1.useDataViewFilters)(props));
|
|
55
|
+
expect(result.current.filters).toEqual({ test: 'foo' });
|
|
56
|
+
});
|
|
57
|
+
it('should sync with URL search params with array value', () => {
|
|
58
|
+
const searchParams = new URLSearchParams();
|
|
59
|
+
searchParams.append('test', 'foo');
|
|
60
|
+
searchParams.append('test', 'bar');
|
|
61
|
+
const setSearchParams = jest.fn();
|
|
62
|
+
const props = {
|
|
63
|
+
initialFilters: { test: [] },
|
|
64
|
+
searchParams,
|
|
65
|
+
setSearchParams,
|
|
66
|
+
};
|
|
67
|
+
const { result } = (0, react_1.renderHook)(() => (0, filters_1.useDataViewFilters)(props));
|
|
68
|
+
expect(result.current.filters).toEqual({ test: ['foo', 'bar'] });
|
|
69
|
+
});
|
|
45
70
|
it('should reset filters to default values when clearAllFilters is called', () => {
|
|
46
71
|
const { result } = (0, react_1.renderHook)(() => (0, filters_1.useDataViewFilters)({ initialFilters }));
|
|
47
72
|
(0, react_1.act)(() => result.current.clearAllFilters());
|
|
@@ -12,6 +12,8 @@ export interface DataViewToolbarProps extends Omit<PropsWithChildren<ToolbarProp
|
|
|
12
12
|
pagination?: React.ReactNode;
|
|
13
13
|
/** React node to display actions */
|
|
14
14
|
actions?: React.ReactNode;
|
|
15
|
+
/** React node to display toggle group */
|
|
16
|
+
toggleGroup?: React.ReactNode;
|
|
15
17
|
/** React node to display filters */
|
|
16
18
|
filters?: React.ReactNode;
|
|
17
19
|
/** React node to display custom filter labels */
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import React, { useRef } from 'react';
|
|
13
13
|
import { Button, Toolbar, ToolbarContent, ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core';
|
|
14
14
|
export const DataViewToolbar = (_a) => {
|
|
15
|
-
var { className, ouiaId = 'DataViewToolbar', bulkSelect, actions, pagination, filters, customLabelGroupContent, clearAllFilters, children } = _a, props = __rest(_a, ["className", "ouiaId", "bulkSelect", "actions", "pagination", "filters", "customLabelGroupContent", "clearAllFilters", "children"]);
|
|
15
|
+
var { className, ouiaId = 'DataViewToolbar', bulkSelect, actions, toggleGroup, pagination, filters, customLabelGroupContent, clearAllFilters, children } = _a, props = __rest(_a, ["className", "ouiaId", "bulkSelect", "actions", "toggleGroup", "pagination", "filters", "customLabelGroupContent", "clearAllFilters", "children"]);
|
|
16
16
|
const defaultClearFilters = useRef(React.createElement(ToolbarItem, null,
|
|
17
17
|
React.createElement(Button, { ouiaId: `${ouiaId}-clear-all-filters`, variant: "link", onClick: clearAllFilters, isInline: true }, "Clear filters")));
|
|
18
18
|
return (React.createElement(Toolbar, Object.assign({ ouiaId: ouiaId, className: className, customLabelGroupContent: customLabelGroupContent !== null && customLabelGroupContent !== void 0 ? customLabelGroupContent : defaultClearFilters.current }, props),
|
|
@@ -20,6 +20,7 @@ export const DataViewToolbar = (_a) => {
|
|
|
20
20
|
bulkSelect && (React.createElement(ToolbarItem, { "data-ouia-component-id": `${ouiaId}-bulk-select` }, bulkSelect)),
|
|
21
21
|
filters && (React.createElement(ToolbarItem, null, filters)),
|
|
22
22
|
actions && (React.createElement(ToolbarItem, null, actions)),
|
|
23
|
+
toggleGroup && (React.createElement(ToolbarItem, null, toggleGroup)),
|
|
23
24
|
pagination && (React.createElement(ToolbarItem, { variant: ToolbarItemVariant.pagination, "data-ouia-component-id": `${ouiaId}-pagination` }, pagination)),
|
|
24
25
|
children)));
|
|
25
26
|
};
|
|
@@ -4,8 +4,8 @@ export const useDataViewFilters = ({ initialFilters = {}, searchParams, setSearc
|
|
|
4
4
|
const isUrlSyncEnabled = useMemo(() => searchParams && !!setSearchParams, [searchParams, setSearchParams]);
|
|
5
5
|
const getInitialFilters = useCallback(() => isUrlSyncEnabled
|
|
6
6
|
? Object.keys(initialFilters).reduce((loadedFilters, key) => {
|
|
7
|
-
const urlValue = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get(key);
|
|
8
7
|
const isArrayFilter = Array.isArray(initialFilters[key]);
|
|
8
|
+
const urlValue = isArrayFilter ? searchParams === null || searchParams === void 0 ? void 0 : searchParams.getAll(key) : searchParams === null || searchParams === void 0 ? void 0 : searchParams.get(key);
|
|
9
9
|
// eslint-disable-next-line no-nested-ternary
|
|
10
10
|
loadedFilters[key] = urlValue
|
|
11
11
|
? (isArrayFilter && !Array.isArray(urlValue) ? [urlValue] : urlValue)
|
|
@@ -40,6 +40,31 @@ describe('useDataViewFilters', () => {
|
|
|
40
40
|
act(() => result.current.onSetFilters({ search: 'new search' }));
|
|
41
41
|
expect(setSearchParams).toHaveBeenCalled();
|
|
42
42
|
});
|
|
43
|
+
it('should sync with URL search params with non array value', () => {
|
|
44
|
+
const searchParams = new URLSearchParams();
|
|
45
|
+
searchParams.set('test', 'foo');
|
|
46
|
+
const setSearchParams = jest.fn();
|
|
47
|
+
const props = {
|
|
48
|
+
initialFilters: { test: '' },
|
|
49
|
+
searchParams,
|
|
50
|
+
setSearchParams,
|
|
51
|
+
};
|
|
52
|
+
const { result } = renderHook(() => useDataViewFilters(props));
|
|
53
|
+
expect(result.current.filters).toEqual({ test: 'foo' });
|
|
54
|
+
});
|
|
55
|
+
it('should sync with URL search params with array value', () => {
|
|
56
|
+
const searchParams = new URLSearchParams();
|
|
57
|
+
searchParams.append('test', 'foo');
|
|
58
|
+
searchParams.append('test', 'bar');
|
|
59
|
+
const setSearchParams = jest.fn();
|
|
60
|
+
const props = {
|
|
61
|
+
initialFilters: { test: [] },
|
|
62
|
+
searchParams,
|
|
63
|
+
setSearchParams,
|
|
64
|
+
};
|
|
65
|
+
const { result } = renderHook(() => useDataViewFilters(props));
|
|
66
|
+
expect(result.current.filters).toEqual({ test: ['foo', 'bar'] });
|
|
67
|
+
});
|
|
43
68
|
it('should reset filters to default values when clearAllFilters is called', () => {
|
|
44
69
|
const { result } = renderHook(() => useDataViewFilters({ initialFilters }));
|
|
45
70
|
act(() => result.current.clearAllFilters());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/
|
|
1
|
+
{"root":["../src/index.ts","../src/DataView/DataView.test.tsx","../src/DataView/DataView.tsx","../src/DataView/index.ts","../src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx","../src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx","../src/DataViewCheckboxFilter/index.ts","../src/DataViewEventsContext/DataViewEventsContext.test.tsx","../src/DataViewEventsContext/DataViewEventsContext.tsx","../src/DataViewEventsContext/index.ts","../src/DataViewFilters/DataViewFilters.test.tsx","../src/DataViewFilters/DataViewFilters.tsx","../src/DataViewFilters/index.tsx","../src/DataViewTable/DataViewTable.test.tsx","../src/DataViewTable/DataViewTable.tsx","../src/DataViewTable/index.ts","../src/DataViewTableBasic/DataViewTableBasic.test.tsx","../src/DataViewTableBasic/DataViewTableBasic.tsx","../src/DataViewTableBasic/index.ts","../src/DataViewTableHead/DataViewTableHead.test.tsx","../src/DataViewTableHead/DataViewTableHead.tsx","../src/DataViewTableHead/index.ts","../src/DataViewTableTree/DataViewTableTree.test.tsx","../src/DataViewTableTree/DataViewTableTree.tsx","../src/DataViewTableTree/index.ts","../src/DataViewTextFilter/DataViewTextFilter.test.tsx","../src/DataViewTextFilter/DataViewTextFilter.tsx","../src/DataViewTextFilter/index.ts","../src/DataViewToolbar/DataViewToolbar.test.tsx","../src/DataViewToolbar/DataViewToolbar.tsx","../src/DataViewToolbar/index.ts","../src/Hooks/filters.test.tsx","../src/Hooks/filters.ts","../src/Hooks/index.ts","../src/Hooks/pagination.test.tsx","../src/Hooks/pagination.ts","../src/Hooks/selection.test.tsx","../src/Hooks/selection.ts","../src/Hooks/sort.test.tsx","../src/Hooks/sort.ts","../src/InternalContext/InternalContext.test.tsx","../src/InternalContext/InternalContext.tsx","../src/InternalContext/index.ts"],"version":"5.6.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-data-view",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "Data view used for Red Hat projects.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -43,17 +43,17 @@
|
|
|
43
43
|
"react-dom": "^17 || ^18"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@patternfly/documentation-framework": "^6.5.
|
|
46
|
+
"@patternfly/documentation-framework": "^6.5.20",
|
|
47
47
|
"@patternfly/patternfly": "^6.0.0",
|
|
48
48
|
"@patternfly/react-code-editor": "^6.0.0",
|
|
49
|
-
"@patternfly/patternfly-a11y": "^5.
|
|
50
|
-
"@types/react": "^18.3.
|
|
49
|
+
"@patternfly/patternfly-a11y": "^5.1.0",
|
|
50
|
+
"@types/react": "^18.3.20",
|
|
51
51
|
"@types/react-dom": "^18.3.5",
|
|
52
52
|
"@types/react-router-dom": "^5.3.3",
|
|
53
53
|
"react": "^18.3.1",
|
|
54
54
|
"react-dom": "^18.3.1",
|
|
55
|
-
"react-router": "^6.
|
|
56
|
-
"react-router-dom": "^6.
|
|
55
|
+
"react-router": "^6.30.0",
|
|
56
|
+
"react-router-dom": "^6.30.0",
|
|
57
57
|
"rimraf": "^6.0.1",
|
|
58
58
|
"typescript": "^5.4.5"
|
|
59
59
|
}
|
|
@@ -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
|
+
};
|
|
@@ -15,8 +15,9 @@ sortValue: 2
|
|
|
15
15
|
propComponents: ['DataViewToolbar', 'DataViewFilters', 'DataViewTextFilter', 'DataViewCheckboxFilter']
|
|
16
16
|
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md
|
|
17
17
|
---
|
|
18
|
-
import { useMemo } from 'react';
|
|
18
|
+
import { useMemo, useState, useEffect } from 'react';
|
|
19
19
|
import { BrowserRouter, useSearchParams } from 'react-router-dom';
|
|
20
|
+
import { Pagination, ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
|
|
20
21
|
import { useDataViewPagination, useDataViewSelection, useDataViewFilters } from '@patternfly/react-data-view/dist/dynamic/Hooks';
|
|
21
22
|
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
|
|
22
23
|
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
|
|
|
140
141
|
```js file="./FiltersExample.tsx"
|
|
141
142
|
|
|
142
143
|
```
|
|
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
|
+
```
|
package/release.config.js
CHANGED
|
@@ -13,13 +13,15 @@ export interface DataViewToolbarProps extends Omit<PropsWithChildren<ToolbarProp
|
|
|
13
13
|
pagination?: React.ReactNode;
|
|
14
14
|
/** React node to display actions */
|
|
15
15
|
actions?: React.ReactNode;
|
|
16
|
+
/** React node to display toggle group */
|
|
17
|
+
toggleGroup?: React.ReactNode;
|
|
16
18
|
/** React node to display filters */
|
|
17
19
|
filters?: React.ReactNode;
|
|
18
20
|
/** React node to display custom filter labels */
|
|
19
21
|
customLabelGroupContent?: React.ReactNode;
|
|
20
22
|
}
|
|
21
23
|
|
|
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) => {
|
|
23
25
|
const defaultClearFilters = useRef(
|
|
24
26
|
<ToolbarItem>
|
|
25
27
|
<Button ouiaId={`${ouiaId}-clear-all-filters`} variant="link" onClick={clearAllFilters} isInline>
|
|
@@ -45,6 +47,11 @@ export const DataViewToolbar: React.FC<DataViewToolbarProps> = ({ className, oui
|
|
|
45
47
|
{actions}
|
|
46
48
|
</ToolbarItem>
|
|
47
49
|
)}
|
|
50
|
+
{toggleGroup && (
|
|
51
|
+
<ToolbarItem>
|
|
52
|
+
{toggleGroup}
|
|
53
|
+
</ToolbarItem>
|
|
54
|
+
)}
|
|
48
55
|
{pagination && (
|
|
49
56
|
<ToolbarItem variant={ToolbarItemVariant.pagination} data-ouia-component-id={`${ouiaId}-pagination`}>
|
|
50
57
|
{pagination}
|
|
@@ -53,6 +53,33 @@ describe('useDataViewFilters', () => {
|
|
|
53
53
|
expect(setSearchParams).toHaveBeenCalled();
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
it('should sync with URL search params with non array value', () => {
|
|
57
|
+
const searchParams = new URLSearchParams();
|
|
58
|
+
searchParams.set('test', 'foo');
|
|
59
|
+
const setSearchParams = jest.fn();
|
|
60
|
+
const props: UseDataViewFiltersProps<{ test: string }> = {
|
|
61
|
+
initialFilters: { test: '' },
|
|
62
|
+
searchParams,
|
|
63
|
+
setSearchParams,
|
|
64
|
+
};
|
|
65
|
+
const { result } = renderHook(() => useDataViewFilters(props));
|
|
66
|
+
expect(result.current.filters).toEqual({ test: 'foo' });
|
|
67
|
+
})
|
|
68
|
+
it('should sync with URL search params with array value', () => {
|
|
69
|
+
|
|
70
|
+
const searchParams = new URLSearchParams();
|
|
71
|
+
searchParams.append('test', 'foo');
|
|
72
|
+
searchParams.append('test', 'bar');
|
|
73
|
+
const setSearchParams = jest.fn();
|
|
74
|
+
const props: UseDataViewFiltersProps<{ test: string[] }> = {
|
|
75
|
+
initialFilters: { test: [] },
|
|
76
|
+
searchParams,
|
|
77
|
+
setSearchParams,
|
|
78
|
+
};
|
|
79
|
+
const { result } = renderHook(() => useDataViewFilters(props));
|
|
80
|
+
expect(result.current.filters).toEqual({ test: [ 'foo', 'bar' ] });
|
|
81
|
+
})
|
|
82
|
+
|
|
56
83
|
it('should reset filters to default values when clearAllFilters is called', () => {
|
|
57
84
|
const { result } = renderHook(() => useDataViewFilters({ initialFilters }));
|
|
58
85
|
act(() => result.current.clearAllFilters());
|
package/src/Hooks/filters.ts
CHANGED
|
@@ -18,8 +18,8 @@ export const useDataViewFilters = <T extends object>({
|
|
|
18
18
|
|
|
19
19
|
const getInitialFilters = useCallback((): T => isUrlSyncEnabled
|
|
20
20
|
? Object.keys(initialFilters).reduce((loadedFilters, key) => {
|
|
21
|
-
const urlValue = searchParams?.get(key);
|
|
22
21
|
const isArrayFilter = Array.isArray(initialFilters[key]);
|
|
22
|
+
const urlValue = isArrayFilter ? searchParams?.getAll(key) : searchParams?.get(key);
|
|
23
23
|
|
|
24
24
|
// eslint-disable-next-line no-nested-ternary
|
|
25
25
|
loadedFilters[key] = urlValue
|