Skip to content

Commit 6d8dfcd

Browse files
authored
Merge pull request #131 from fhlavac/checkbox
Add DataViewCheckbox component
2 parents e122f5f + d8e4684 commit 6d8dfcd

File tree

11 files changed

+540
-39
lines changed

11 files changed

+540
-39
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React from 'react';
2+
import { DataViewCheckboxFilter, DataViewCheckboxFilterProps } from '@patternfly/react-data-view/dist/dynamic/DataViewCheckboxFilter';
3+
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
4+
5+
describe('DataViewCheckboxFilter component', () => {
6+
const defaultProps: DataViewCheckboxFilterProps = {
7+
filterId: 'test-checkbox-filter',
8+
title: 'Test checkbox filter',
9+
value: [ 'workspace-one' ],
10+
options: [
11+
{ label: 'Workspace one', value: 'workspace-one' },
12+
{ label: 'Workspace two', value: 'workspace-two' },
13+
{ label: 'Workspace three', value: 'workspace-three' },
14+
],
15+
};
16+
17+
it('renders a checkbox filter with options', () => {
18+
const onChange = cy.stub().as('onChange');
19+
20+
cy.mount(
21+
<DataViewToolbar filters={<DataViewCheckboxFilter {...defaultProps} onChange={onChange} />} />
22+
);
23+
24+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-toggle"]')
25+
.contains('Test checkbox filter')
26+
.should('be.visible');
27+
28+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-badge"]')
29+
.should('exist')
30+
.contains('1');
31+
32+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-toggle"]').click();
33+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]').should('be.visible');
34+
35+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]')
36+
.find('li')
37+
.should('have.length', 3)
38+
.first()
39+
.contains('Workspace one');
40+
41+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]')
42+
.find('li')
43+
.first()
44+
.find('input[type="checkbox"]')
45+
.should('be.checked');
46+
47+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]')
48+
.find('li')
49+
.eq(1)
50+
.find('input[type="checkbox"]')
51+
.click();
52+
53+
cy.get('@onChange').should('have.been.calledWith', Cypress.sinon.match.object, [ 'workspace-two', 'workspace-one' ]);
54+
});
55+
56+
it('renders a checkbox filter with no options selected', () => {
57+
const emptyProps = { ...defaultProps, value: [] };
58+
59+
cy.mount(
60+
<DataViewToolbar filters={<DataViewCheckboxFilter {...emptyProps} />} />
61+
);
62+
63+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-toggle"]').contains('Test checkbox filter');
64+
cy.get('[data-ouia-component-id="DataViewCheckboxFilter-badge"]').should('not.exist');
65+
});
66+
});

packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/FiltersExample.tsx

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { useDataViewFilters, useDataViewPagination } from '@patternfly/react-dat
55
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
66
import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
77
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
8-
import { DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters';
8+
import { DataViewFilterOption, DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters';
99
import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter';
10+
import { DataViewCheckboxFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewCheckboxFilter';
1011

1112
const perPageOptions = [
1213
{ title: '5', value: 5 },
@@ -17,38 +18,51 @@ interface Repository {
1718
name: string;
1819
branch: string | null;
1920
prs: string | null;
20-
workspaces: string;
21+
workspace: string;
2122
lastCommit: string;
2223
}
2324

2425
interface RepositoryFilters {
2526
name: string,
26-
branch: string
27+
branch: string,
28+
workspace: string[]
2729
}
2830

2931
const repositories: Repository[] = [
30-
{ name: 'Repository one', branch: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: 'Timestamp one' },
31-
{ name: 'Repository two', branch: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: 'Timestamp two' },
32-
{ name: 'Repository three', branch: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: 'Timestamp three' },
33-
{ name: 'Repository four', branch: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: 'Timestamp four' },
34-
{ name: 'Repository five', branch: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: 'Timestamp five' },
35-
{ name: 'Repository six', branch: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: 'Timestamp six' }
32+
{ name: 'Repository one', branch: 'Branch one', prs: 'Pull request one', workspace: 'Workspace one', lastCommit: 'Timestamp one' },
33+
{ name: 'Repository two', branch: 'Branch two', prs: 'Pull request two', workspace: 'Workspace two', lastCommit: 'Timestamp two' },
34+
{ name: 'Repository three', branch: 'Branch three', prs: 'Pull request three', workspace: 'Workspace one', lastCommit: 'Timestamp three' },
35+
{ name: 'Repository four', branch: 'Branch four', prs: 'Pull request four', workspace: 'Workspace one', lastCommit: 'Timestamp four' },
36+
{ name: 'Repository five', branch: 'Branch five', prs: 'Pull request five', workspace: 'Workspace two', lastCommit: 'Timestamp five' },
37+
{ name: 'Repository six', branch: 'Branch six', prs: 'Pull request six', workspace: 'Workspace three', lastCommit: 'Timestamp six' }
3638
];
3739

38-
const columns = [ 'Name', 'Branch', 'Pull requests', 'Workspaces', 'Last commit' ];
40+
const filterOptions: DataViewFilterOption[] = [
41+
{ label: 'Workspace one', value: 'workspace-one' },
42+
{ label: 'Workspace two', value: 'workspace-two' },
43+
{ label: 'Workspace three', value: 'workspace-three' }
44+
];
45+
46+
const columns = [ 'Name', 'Branch', 'Pull requests', 'Workspace', 'Last commit' ];
3947

4048
const ouiaId = 'LayoutExample';
4149

4250
const MyTable: React.FunctionComponent = () => {
4351
const [ searchParams, setSearchParams ] = useSearchParams();
52+
const { filters, onSetFilters, clearAllFilters } = useDataViewFilters<RepositoryFilters>({ initialFilters: { name: '', branch: '', workspace: [] }, searchParams, setSearchParams });
4453
const pagination = useDataViewPagination({ perPage: 5 });
4554
const { page, perPage } = pagination;
46-
const { filters, onSetFilters, clearAllFilters } = useDataViewFilters<RepositoryFilters>({ initialFilters: { name: '', branch: '' }, searchParams, setSearchParams });
4755

48-
const pageRows = useMemo(() => repositories
49-
.filter(item => (!filters.name || item.name?.toLocaleLowerCase().includes(filters.name?.toLocaleLowerCase())) && (!filters.branch || item.branch?.toLocaleLowerCase().includes(filters.branch?.toLocaleLowerCase())))
56+
const filteredData = useMemo(() => repositories.filter(item =>
57+
(!filters.name || item.name?.toLocaleLowerCase().includes(filters.name?.toLocaleLowerCase())) &&
58+
(!filters.branch || item.branch?.toLocaleLowerCase().includes(filters.branch?.toLocaleLowerCase())) &&
59+
(!filters.workspace || filters.workspace.length === 0 || filters.workspace.includes(String(filterOptions.find(option => option.label === item.workspace)?.value)))
60+
), [ filters ]);
61+
62+
const pageRows = useMemo(() => filteredData
5063
.slice((page - 1) * perPage, ((page - 1) * perPage) + perPage)
51-
.map(item => Object.values(item)), [ page, perPage, filters ]);
64+
.map(item => Object.values(item)),
65+
[ page, perPage, filteredData ]);
5266

5367
return (
5468
<DataView>
@@ -58,14 +72,15 @@ const MyTable: React.FunctionComponent = () => {
5872
pagination={
5973
<Pagination
6074
perPageOptions={perPageOptions}
61-
itemCount={repositories.length}
75+
itemCount={filteredData.length}
6276
{...pagination}
6377
/>
6478
}
6579
filters={
6680
<DataViewFilters onChange={(_e, values) => onSetFilters(values)} values={filters}>
6781
<DataViewTextFilter filterId="name" title='Name' placeholder='Filter by name' />
6882
<DataViewTextFilter filterId="branch" title='Branch' placeholder='Filter by branch' />
83+
<DataViewCheckboxFilter filterId="workspace" title='Workspace' placeholder='Filter by workspace' options={filterOptions} />
6984
</DataViewFilters>
7085
}
7186
/>
@@ -76,7 +91,7 @@ const MyTable: React.FunctionComponent = () => {
7691
<Pagination
7792
isCompact
7893
perPageOptions={perPageOptions}
79-
itemCount={repositories.length}
94+
itemCount={filteredData.length}
8095
{...pagination}
8196
/>
8297
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ source: react
1111
# If you use typescript, the name of the interface to display props for
1212
# These are found through the sourceProps function provided in patternfly-docs.source.js
1313
sortValue: 3
14-
propComponents: ['DataViewFilters', 'DataViewTextFilter']
14+
propComponents: ['DataViewFilters', 'DataViewTextFilter', 'DataViewCheckboxFilter']
1515
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md
1616
---
1717
import { useMemo } from 'react';
@@ -23,6 +23,7 @@ import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataVi
2323
import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
2424
import { DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters';
2525
import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter';
26+
import { DataViewCheckboxFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewCheckboxFilter';
2627

2728
This is a list of functionality you can use to manage data displayed in the **data view**.
2829

@@ -92,7 +93,7 @@ The `useDataViewSelection` hook manages the selection state of the data view.
9293
Enables filtering of data records in the data view and displays the applied filter chips.
9394

9495
### Toolbar usage
95-
The data view toolbar can include a set of filters by passing a React node to the `filters` property. You can use predefined components `DataViewFilters` and `DataViewTextFilter` to customize and handle filtering directly in the toolbar. The `DataViewFilters` is a wrapper allowing conditional filtering using multiple attributes. If you need just a single filter, you can use `DataViewTextFilter` or a different filter component alone. Props of these filter components are listed at the bottom of this page.
96+
The data view toolbar can include a set of filters by passing a React node to the `filters` property. You can use predefined components `DataViewFilters`, `DataViewTextFilter` and `DataViewCheckboxFilter` to customize and handle filtering directly in the toolbar. The `DataViewFilters` is a wrapper allowing conditional filtering using multiple attributes. If you need just a single filter, you can use `DataViewTextFilter`, `DataViewCheckboxFilter` or a different filter component alone. Props of these filter components are listed at the bottom of this page.
9697

9798
You can decide between passing `value` and `onChange` event to every filter separately or pass `values` and `onChange` to the `DataViewFilters` wrapper which make them available to its children. Props directly passed to child filters have a higher priority than the "inherited" ones.
9899

@@ -101,7 +102,7 @@ You can decide between passing `value` and `onChange` event to every filter sepa
101102
The `useDataViewFilters` hook manages the filter state of the data view. It allows you to define default filter values, synchronize filter state with URL parameters, and handle filter changes efficiently.
102103

103104
**Initial values:**
104-
- `initialFilters` object with default filter values
105+
- `initialFilters` object with default filter values (if the filter param allows multiple values, pass an array)
105106
- optional `searchParams` object for managing URL-based filter state
106107
- optional `setSearchParams` function to update the URL when filters are modified
107108

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import DataViewCheckboxFilter, { DataViewCheckboxFilterProps } from './DataViewCheckboxFilter';
4+
import DataViewToolbar from '../DataViewToolbar';
5+
6+
describe('DataViewCheckboxFilter component', () => {
7+
const defaultProps: DataViewCheckboxFilterProps = {
8+
filterId: 'test-checkbox-filter',
9+
title: 'Test Checkbox Filter',
10+
value: [ 'workspace-one' ],
11+
options: [
12+
{ label: 'Workspace one', value: 'workspace-one' },
13+
{ label: 'Workspace two', value: 'workspace-two' },
14+
{ label: 'Workspace three', value: 'workspace-three' },
15+
],
16+
};
17+
18+
it('should render correctly', () => {
19+
const { container } = render(
20+
<DataViewToolbar filters={<DataViewCheckboxFilter {...defaultProps} />} />
21+
);
22+
expect(container).toMatchSnapshot();
23+
});
24+
});

0 commit comments

Comments
 (0)