-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from fhlavac/cg
Add conditional text filtering to DataView
- Loading branch information
Showing
26 changed files
with
1,189 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import React from 'react'; | ||
import { useDataViewFilters } from '@patternfly/react-data-view/dist/dynamic/Hooks'; | ||
import { DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters'; | ||
import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter'; | ||
import { DataViewToolbar } from '@patternfly/react-data-view/dist/esm/DataViewToolbar'; | ||
import { FilterIcon } from '@patternfly/react-icons'; | ||
|
||
const filtersProps = { | ||
ouiaId: 'DataViewFilters', | ||
toggleIcon: <FilterIcon />, | ||
values: { name: '', branch: '' } | ||
}; | ||
|
||
interface RepositoryFilters { | ||
name: string, | ||
branch: string | ||
}; | ||
|
||
const DataViewToolbarWithState = (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any | ||
const { filters, onSetFilters, clearAllFilters } = useDataViewFilters<RepositoryFilters>({ initialFilters: { name: '', branch: '' } }); | ||
|
||
return ( | ||
<DataViewToolbar | ||
ouiaId='FiltersExampleHeader' | ||
clearAllFilters = {clearAllFilters} | ||
filters={ | ||
<DataViewFilters {...filtersProps} onChange={(_e, values) => onSetFilters(values)} values={filters} {...props}> | ||
<DataViewTextFilter filterId="name" title='Name' placeholder='Filter by name' /> | ||
<DataViewTextFilter filterId="branch" title='Branch' placeholder='Filter by branch' /> | ||
</DataViewFilters> | ||
} | ||
/> | ||
); | ||
}; | ||
|
||
describe('DataViewFilters', () => { | ||
it('renders DataViewFilters with menu and filter items', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewFilters"]').should('exist'); | ||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
|
||
cy.contains('Name').should('exist'); | ||
cy.contains('Branch').should('exist'); | ||
}); | ||
|
||
it('can select a filter option', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewFilters"]').should('contain.text', 'Name'); | ||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Branch').click(); | ||
|
||
cy.get('[data-ouia-component-id="DataViewFilters"]').should('contain.text', 'Branch'); | ||
}); | ||
|
||
it('responds to input and clears the filters', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Name').click(); | ||
|
||
cy.get('input[placeholder="Filter by name"]').type('Repository one'); | ||
cy.get('.pf-v5-c-chip__text').should('have.length', 1); | ||
cy.get('input[placeholder="Filter by name"]').clear(); | ||
cy.get('.pf-v5-c-chip__text').should('have.length', 0); | ||
}); | ||
|
||
it('displays chips for selected filters', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Name').click(); | ||
cy.get('input[placeholder="Filter by name"]').type('Repository one'); | ||
|
||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Branch').click(); | ||
cy.get('input[placeholder="Filter by branch"]').type('Main branch'); | ||
|
||
cy.get('.pf-v5-c-chip__text').should('have.length', 2); | ||
cy.get('.pf-v5-c-chip__text').eq(0).should('contain.text', 'Repository one'); | ||
cy.get('.pf-v5-c-chip__text').eq(1).should('contain.text', 'Main branch'); | ||
}); | ||
|
||
it('removes filters by clicking individual chips', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Name').click(); | ||
cy.get('input[placeholder="Filter by name"]').type('Repository one'); | ||
|
||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Branch').click(); | ||
cy.get('input[placeholder="Filter by branch"]').type('Main branch'); | ||
|
||
cy.get('[data-ouia-component-id="close"]').should('have.length', 2); | ||
|
||
cy.get('[data-ouia-component-id="close"]').first().click(); | ||
cy.get('[data-ouia-component-id="close"]').last().click(); | ||
|
||
cy.get('[data-ouia-component-id="close"]').should('have.length', 0); | ||
}); | ||
|
||
it('clears all filters using the clear-all button', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Name').click(); | ||
cy.get('input[placeholder="Filter by name"]').type('Repository one'); | ||
|
||
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v5-c-menu-toggle').click(); | ||
cy.contains('Branch').click(); | ||
cy.get('input[placeholder="Filter by branch"]').type('Main branch'); | ||
|
||
cy.get('[data-ouia-component-id="FiltersExampleHeader-clear-all-filters"]').should('exist').click(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React, { useState } from 'react'; | ||
import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter'; | ||
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; | ||
|
||
const defaultProps = { | ||
filterId: 'name', | ||
title: 'Name', | ||
value: '', | ||
ouiaId: 'DataViewTextFilter', | ||
placeholder: 'Filter by name' | ||
}; | ||
|
||
const DataViewToolbarWithState = (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any | ||
const [ value, setValue ] = useState('Repository one'); | ||
|
||
return ( | ||
<DataViewToolbar clearAllFilters={() => setValue('')}> | ||
<DataViewTextFilter {...defaultProps} value={value} onChange={() => setValue('')} {...props} /> | ||
</DataViewToolbar> | ||
); | ||
}; | ||
|
||
describe('DataViewTextFilter', () => { | ||
|
||
it('renders DataViewTextFilter with correct initial values', () => { | ||
cy.mount(<DataViewToolbarWithState value="" />); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter"]').should('exist'); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input') | ||
.should('have.attr', 'placeholder', 'Filter by name') | ||
.and('have.value', ''); | ||
}); | ||
|
||
it('accepts input when passed', () => { | ||
cy.mount(<DataViewToolbarWithState value="" />); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input') | ||
.type('Repository one') | ||
.should('have.value', 'Repository one'); | ||
}); | ||
|
||
it('displays a chip when value is present and removes it on delete', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', 'Repository one'); | ||
|
||
cy.get('.pf-v5-c-chip__text').contains('Repository one'); | ||
cy.get('.pf-m-chip-group button.pf-v5-c-button.pf-m-plain').click(); | ||
|
||
cy.get('.pf-v5-c-chip__text').should('not.exist'); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', ''); | ||
}); | ||
|
||
it('clears input when the clear button is clicked', () => { | ||
cy.mount(<DataViewToolbarWithState />); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', 'Repository one'); | ||
|
||
cy.get('[data-ouia-component-id="DataViewToolbar-clear-all-filters"]').click(); | ||
|
||
cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', ''); | ||
}); | ||
|
||
it('hides or shows the toolbar item based on showToolbarItem prop', () => { | ||
cy.mount( | ||
<DataViewToolbar> | ||
<DataViewTextFilter {...defaultProps} showToolbarItem={false} /> | ||
</DataViewToolbar> | ||
); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter"]').should('not.exist'); | ||
|
||
cy.mount( | ||
<DataViewToolbar> | ||
<DataViewTextFilter {...defaultProps} showToolbarItem /> | ||
</DataViewToolbar> | ||
); | ||
cy.get('[data-ouia-component-id="DataViewTextFilter"]').should('exist'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
...le/patternfly-docs/content/extensions/data-view/examples/Functionality/FiltersExample.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import React, { useMemo } from 'react'; | ||
import { Pagination } from '@patternfly/react-core'; | ||
import { BrowserRouter, useSearchParams } from 'react-router-dom'; | ||
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 { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter'; | ||
|
||
const perPageOptions = [ | ||
{ title: '5', value: 5 }, | ||
{ title: '10', value: 10 } | ||
]; | ||
|
||
interface Repository { | ||
name: string; | ||
branch: string | null; | ||
prs: string | null; | ||
workspaces: string; | ||
lastCommit: string; | ||
} | ||
|
||
interface RepositoryFilters { | ||
name: string, | ||
branch: string | ||
} | ||
|
||
const repositories: Repository[] = [ | ||
{ name: 'Repository one', branch: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: 'Timestamp one' }, | ||
{ name: 'Repository two', branch: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: 'Timestamp two' }, | ||
{ name: 'Repository three', branch: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: 'Timestamp three' }, | ||
{ name: 'Repository four', branch: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: 'Timestamp four' }, | ||
{ name: 'Repository five', branch: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: 'Timestamp five' }, | ||
{ name: 'Repository six', branch: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: 'Timestamp six' } | ||
]; | ||
|
||
const columns = [ 'Name', 'Branch', 'Pull requests', 'Workspaces', 'Last commit' ]; | ||
|
||
const ouiaId = 'LayoutExample'; | ||
|
||
const MyTable: React.FunctionComponent = () => { | ||
const [ searchParams, setSearchParams ] = useSearchParams(); | ||
const pagination = useDataViewPagination({ perPage: 5 }); | ||
const { page, perPage } = pagination; | ||
const { filters, onSetFilters, clearAllFilters } = useDataViewFilters<RepositoryFilters>({ initialFilters: { name: '', branch: '' }, searchParams, setSearchParams }); | ||
|
||
const pageRows = useMemo(() => repositories | ||
.filter(item => (!filters.name || item.name?.toLocaleLowerCase().includes(filters.name?.toLocaleLowerCase())) && (!filters.branch || item.branch?.toLocaleLowerCase().includes(filters.branch?.toLocaleLowerCase()))) | ||
.slice((page - 1) * perPage, ((page - 1) * perPage) + perPage) | ||
.map(item => Object.values(item)), [ page, perPage, filters ]); | ||
|
||
return ( | ||
<DataView> | ||
<DataViewToolbar | ||
ouiaId='LayoutExampleHeader' | ||
clearAllFilters = {clearAllFilters} | ||
pagination={ | ||
<Pagination | ||
perPageOptions={perPageOptions} | ||
itemCount={repositories.length} | ||
{...pagination} | ||
/> | ||
} | ||
filters={ | ||
<DataViewFilters onChange={(_e, values) => onSetFilters(values)} values={filters}> | ||
<DataViewTextFilter filterId="name" title='Name' placeholder='Filter by name' /> | ||
<DataViewTextFilter filterId="branch" title='Branch' placeholder='Filter by branch' /> | ||
</DataViewFilters> | ||
} | ||
/> | ||
<DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={pageRows} /> | ||
<DataViewToolbar | ||
ouiaId='LayoutExampleFooter' | ||
pagination={ | ||
<Pagination | ||
isCompact | ||
perPageOptions={perPageOptions} | ||
itemCount={repositories.length} | ||
{...pagination} | ||
/> | ||
} | ||
/> | ||
</DataView> | ||
); | ||
} | ||
|
||
export const BasicExample: React.FunctionComponent = () => ( | ||
<BrowserRouter> | ||
<MyTable/> | ||
</BrowserRouter> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
packages/module/src/DataViewFilters/DataViewFilters.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import DataViewFilters from './DataViewFilters'; | ||
import DataViewToolbar from '../DataViewToolbar'; | ||
import DataViewTextFilter from '../DataViewTextFilter'; | ||
|
||
describe('DataViewFilters component', () => { | ||
const mockOnChange = jest.fn(); | ||
|
||
it('should render correctly', () => { | ||
const { container } = render(<DataViewToolbar | ||
filters={ | ||
<DataViewFilters onChange={mockOnChange} values={{}}> | ||
<DataViewTextFilter filterId="one" title="One" /> | ||
<DataViewTextFilter filterId="two" title="Two" /> | ||
</DataViewFilters> | ||
} | ||
/>); | ||
expect(container).toMatchSnapshot(); | ||
}); | ||
}); |
Oops, something went wrong.