Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ source: react
# If you use typescript, the name of the interface to display props for
# These are found through the sourceProps function provided in patternfly-docs.source.js
sortValue: 2
propComponents: ['DataViewToolbar', 'DataViewFilters', 'DataViewTextFilter', 'DataViewCheckboxFilter']
propComponents: ['DataViewToolbar', 'DataViewFilters', 'DataViewTextFilter', 'DataViewCheckboxFilter', 'DataViewTreeFilter']
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md
---
import { useMemo, useState, useEffect } from 'react';
Expand All @@ -26,6 +26,7 @@ import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataView
import { DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters';
import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter';
import { DataViewCheckboxFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewCheckboxFilter';
import { DataViewTreeFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTreeFilter';

The **data view toolbar** component renders a default opinionated data view toolbar above or below the data section.

Expand Down Expand Up @@ -143,6 +144,12 @@ This example demonstrates the setup and usage of filters within the data view. I

```

### Tree filter example
This example demonstrates the usage of a tree filter with hierarchical data. The tree filter allows users to select items from a nested structure, making it ideal for categorized or grouped filtering options.

```js file="./TreeFilterExample.tsx"

```

## All/selected data switch
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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import React, { useMemo } from 'react';
import { Pagination } from '@patternfly/react-core';
import { BrowserRouter, useSearchParams } from 'react-router-dom';
import { TreeViewDataItem } from '@patternfly/react-core';
import { useDataViewFilters, useDataViewPagination } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
import { DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters';
import { DataViewTreeFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTreeFilter';

const perPageOptions = [
{ title: '5', value: 5 },
{ title: '10', value: 10 }
];

interface Repository {
name: string;
workspace: string;
tags: string[];
os: string;
lastSeen: string;
}

interface RepositoryFilters {
name: string;
workspace: string[];
os: string[];
tags: string[];
}

const repositories: Repository[] = [
{ name: 'Server-001', workspace: 'Development Workspace', tags: ['web', 'frontend'], os: 'Ubuntu 22.04', lastSeen: '2 hours ago' },
{ name: 'Server-002', workspace: 'Development Workspace', tags: ['api', 'backend'], os: 'RHEL 9', lastSeen: '5 hours ago' },
{ name: 'Server-003', workspace: 'Development Workspace', tags: ['database'], os: 'Windows Server 2022', lastSeen: '1 day ago' },
{ name: 'Server-004', workspace: 'Production Workspace', tags: ['web', 'frontend'], os: 'Ubuntu 22.04', lastSeen: '30 minutes ago' },
{ name: 'Server-005', workspace: 'Production Workspace', tags: ['api', 'backend'], os: 'Debian 12', lastSeen: '1 hour ago' },
{ name: 'Server-006', workspace: 'Production Workspace', tags: ['monitoring'], os: 'macOS Ventura', lastSeen: '3 hours ago' },
{ name: 'Server-007', workspace: 'Production Workspace', tags: ['cache'], os: 'macOS Sonoma', lastSeen: '2 days ago' },
{ name: 'Server-008', workspace: 'Testing Workspace', tags: ['test', 'frontend'], os: 'CentOS 8', lastSeen: '6 hours ago' },
{ name: 'Server-009', workspace: 'Testing Workspace', tags: ['test', 'backend'], os: 'Fedora 38', lastSeen: '4 hours ago' }
];

const treeOptions: TreeViewDataItem[] = [
{
name: 'Development Workspace',
id: 'workspace-dev',
checkProps: { 'aria-label': 'dev-workspace-check', checked: false }
},
{
name: 'Production Workspace',
id: 'workspace-prod',
checkProps: { 'aria-label': 'prod-workspace-check', checked: false }
},
{
name: 'Testing Workspace',
id: 'workspace-test',
checkProps: { 'aria-label': 'test-workspace-check', checked: false }
}
];

const osOptions: TreeViewDataItem[] = [
{
name: 'Linux',
id: 'os-linux',
checkProps: { 'aria-label': 'linux-check', checked: false },
children: [
{
name: 'Ubuntu 22.04',
id: 'os-ubuntu',
checkProps: { checked: false }
},
{
name: 'RHEL 9',
id: 'os-rhel',
checkProps: { checked: false }
},
{
name: 'Debian 12',
id: 'os-debian',
checkProps: { checked: false }
},
{
name: 'CentOS 8',
id: 'os-centos',
checkProps: { checked: false }
},
{
name: 'Fedora 38',
id: 'os-fedora',
checkProps: { checked: false }
}
],
defaultExpanded: true
},
{
name: 'Windows',
id: 'os-windows',
checkProps: { 'aria-label': 'windows-check', checked: false },
children: [
{
name: 'Windows Server 2022',
id: 'os-windows-2022',
checkProps: { checked: false }
}
]
},
{
name: 'macOS',
id: 'os-macos',
checkProps: { 'aria-label': 'macos-check', checked: false },
children: [
{
name: 'macOS Ventura',
id: 'os-macos-ventura',
checkProps: { checked: false }
},
{
name: 'macOS Sonoma',
id: 'os-macos-sonoma',
checkProps: { checked: false }
}
]
}
];

const tagOptions: TreeViewDataItem[] = [
{
name: 'Environment',
id: 'tags-env',
checkProps: { 'aria-label': 'env-check', checked: false },
children: [
{
name: 'web',
id: 'tag-web',
checkProps: { checked: false }
},
{
name: 'api',
id: 'tag-api',
checkProps: { checked: false }
},
{
name: 'database',
id: 'tag-database',
checkProps: { checked: false }
}
],
defaultExpanded: true
},
{
name: 'Layer',
id: 'tags-layer',
checkProps: { 'aria-label': 'layer-check', checked: false },
children: [
{
name: 'frontend',
id: 'tag-frontend',
checkProps: { checked: false }
},
{
name: 'backend',
id: 'tag-backend',
checkProps: { checked: false }
}
]
},
{
name: 'Other',
id: 'tags-other',
checkProps: { 'aria-label': 'other-check', checked: false },
children: [
{
name: 'monitoring',
id: 'tag-monitoring',
checkProps: { checked: false }
},
{
name: 'cache',
id: 'tag-cache',
checkProps: { checked: false }
},
{
name: 'test',
id: 'tag-test',
checkProps: { checked: false }
}
]
}
];

const columns = ['Name', 'Workspace', 'Tags', 'OS', 'Last seen'];

const ouiaId = 'TreeFilterExample';

const MyTable: React.FunctionComponent = () => {
const [searchParams, setSearchParams] = useSearchParams();
const { filters, onSetFilters, clearAllFilters } = useDataViewFilters<RepositoryFilters>({
initialFilters: { name: '', workspace: [], os: [], tags: [] },
searchParams,
setSearchParams
});
const pagination = useDataViewPagination({ perPage: 5 });
const { page, perPage } = pagination;

const filteredData = useMemo(
() =>
repositories.filter(
(item) =>
(!filters.name || item.name?.toLocaleLowerCase().includes(filters.name?.toLocaleLowerCase())) &&
(!filters.workspace || filters.workspace.length === 0 || filters.workspace.includes(item.workspace)) &&
(!filters.os || filters.os.length === 0 || filters.os.includes(item.os)) &&
(!filters.tags || filters.tags.length === 0 || filters.tags.some(tag => item.tags.includes(tag)))
),
[filters]
);

const pageRows = useMemo(
() =>
filteredData
.slice((page - 1) * perPage, (page - 1) * perPage + perPage)
.map((item) => [item.name, item.workspace, item.tags.join(', '), item.os, item.lastSeen]),
[page, perPage, filteredData]
);

return (
<DataView>
<DataViewToolbar
ouiaId="TreeFilterExampleHeader"
clearAllFilters={clearAllFilters}
pagination={<Pagination perPageOptions={perPageOptions} itemCount={filteredData.length} {...pagination} />}
filters={
<DataViewFilters onChange={(_e, values) => onSetFilters(values)} values={filters}>
<DataViewTreeFilter
filterId="workspace"
title="Workspace"
items={treeOptions}
/>
<DataViewTreeFilter
filterId="os"
title="Operating System"
items={osOptions}
/>
<DataViewTreeFilter
filterId="tags"
title="Tags"
items={tagOptions}
/>
</DataViewFilters>
}
/>
<DataViewTable aria-label="Repositories table" ouiaId={ouiaId} columns={columns} rows={pageRows} />
<DataViewToolbar
ouiaId="TreeFilterExampleFooter"
pagination={
<Pagination isCompact perPageOptions={perPageOptions} itemCount={filteredData.length} {...pagination} />
}
/>
</DataView>
);
};

export const TreeFilterExample: React.FunctionComponent = () => (
<BrowserRouter>
<MyTable />
</BrowserRouter>
);
4 changes: 2 additions & 2 deletions packages/module/patternfly-docs/generated/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module.exports = {
'/extensions/data-view/toolbar/react': {
id: "Toolbar",
title: "Data view toolbar",
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"}]],
examples: ["Toolbar example","Actions example","Pagination example","Selection example","Filtering example","All/selected example"],
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":"Tree filter example"}],{"text":"All/selected data switch"},[{"text":"All/selected example"}]],
examples: ["Toolbar example","Actions example","Pagination example","Selection example","Filtering example","Tree filter example","All/selected example"],
section: "extensions",
subsection: "Data view",
source: "react",
Expand Down
2 changes: 1 addition & 1 deletion packages/module/src/DataViewFilters/DataViewFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const DataViewFilters = <T extends object>({

useEffect(() => {
filterItems.length > 0 && setActiveAttributeMenu(filterItems[0].title);
}, [ filterItems ]);
}, [ filterItems.length ]);

const handleClickOutside = (event: MouseEvent) =>
isAttributeMenuOpen &&
Expand Down
Loading