From 423e886ef6f72517249486725472972e938ba10c Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:00:22 +0100 Subject: [PATCH 1/7] Allow to customize how `` applies filters ## Problem There is now easy way to implement cumulative filters (filtering by multiple tags for instance) without reimplementing ``. ### Solution Introduce two new props: - isSelected: accept the item value and the current filters and return a boolean - toggleFilter: accept the item value and the current filters and return new filters --- .../src/list/filter/FilterList.stories.tsx | 68 ++++++++++++++++ .../src/list/filter/FilterListItem.spec.tsx | 79 +++++++++++++++---- .../src/list/filter/FilterListItem.tsx | 69 ++++++++++------ 3 files changed, 174 insertions(+), 42 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx b/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx index 480628e0d7b..1a15ada60cd 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx @@ -77,6 +77,74 @@ export const Basic = () => { ); }; +export const Cumulative = () => { + const listContext = useList({ + data: [ + { id: 1, title: 'Article test', category: 'tests' }, + { id: 2, title: 'Article news', category: 'news' }, + { id: 3, title: 'Article deals', category: 'deals' }, + { id: 4, title: 'Article tutorials', category: 'tutorials' }, + ], + filter: { + category: ['tutorials', 'news'], + }, + }); + const isSelected = (value, filters) => { + const category = filters.category || []; + return category.includes(value.category); + }; + + const toggleFilter = (value, filters) => { + const category = filters.category || []; + return { + ...filters, + category: category.includes(value.category) + ? category.filter(v => v !== value.category) + : [...category, value.category], + }; + }; + return ( + + + + }> + + + + + + + + + + ); +}; + const FilterValue = () => { const { filterValues } = useListContext(); return ( diff --git a/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx b/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx index 85ddf678061..1b8897b3070 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import expect from 'expect'; -import { render, cleanup } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { ListContextProvider, ListControllerResult } from 'ra-core'; import { FilterListItem } from './FilterListItem'; +import { Cumulative } from './FilterList.stories'; const defaultListContext: ListControllerResult = { data: [], @@ -32,19 +33,17 @@ const defaultListContext: ListControllerResult = { }; describe('', () => { - afterEach(cleanup); - it("should display the item label when it's a string", () => { - const { queryByText } = render( + render( ); - expect(queryByText('Foo')).not.toBeNull(); + expect(screen.queryByText('Foo')).not.toBeNull(); }); it("should display the item label when it's an element", () => { - const { queryByTestId } = render( + render( Foo} @@ -52,42 +51,48 @@ describe('', () => { /> ); - expect(queryByTestId('123')).not.toBeNull(); + expect(screen.queryByTestId('123')).not.toBeNull(); }); it('should not appear selected if filterValues is empty', () => { - const { getByText } = render( + render( ); - expect(getByText('Foo').parentElement?.dataset.selected).toBe('false'); + expect(screen.getByText('Foo').parentElement?.dataset.selected).toBe( + 'false' + ); }); it('should not appear selected if filterValues does not contain value', () => { - const { getByText } = render( + render( ); - expect(getByText('Foo').parentElement?.dataset.selected).toBe('false'); + expect(screen.getByText('Foo').parentElement?.dataset.selected).toBe( + 'false' + ); }); it('should appear selected if filterValues is equal to value', () => { - const { getByText } = render( + render( ); - expect(getByText('Foo').parentElement?.dataset.selected).toBe('true'); + expect(screen.getByText('Foo').parentElement?.dataset.selected).toBe( + 'true' + ); }); it('should appear selected if filterValues is equal to value for nested filters', () => { - const { getByText } = render( + render( ', () => { /> ); - expect(getByText('Foo').parentElement?.dataset.selected).toBe('true'); + expect(screen.getByText('Foo').parentElement?.dataset.selected).toBe( + 'true' + ); }); it('should appear selected if filterValues contains value', () => { - const { getByText } = render( + render( ', () => { ); - expect(getByText('Foo').parentElement?.dataset.selected).toBe('true'); + expect(screen.getByText('Foo').parentElement?.dataset.selected).toBe( + 'true' + ); + }); + + it('should allow to customize isSelected and toggleFilter', () => { + const { container } = render(); + + expect( + Array.from( + container.querySelectorAll( + '[data-selected="true"]' + ) + ).map(item => item.textContent) + ).toEqual(['News', 'Tutorials']); + + screen.getByText(/"category":.*\[.*"tutorials",.*"news".*\]/); + + screen.getByText('News').click(); + + expect( + Array.from( + container.querySelectorAll( + '[data-selected="true"]' + ) + ).map(item => item.textContent) + ).toEqual(['Tutorials']); + screen.getByText(/"category":.*\[.*"tutorials".*\]/); + + screen.getByText('Tutorials').click(); + + expect( + Array.from( + container.querySelectorAll( + '[data-selected="true"]' + ) + ).map(item => item.textContent) + ).toEqual([]); + screen.getByText(/{}/); }); }); diff --git a/packages/ra-ui-materialui/src/list/filter/FilterListItem.tsx b/packages/ra-ui-materialui/src/list/filter/FilterListItem.tsx index 482ab6cc877..f5e9f6ff7bc 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterListItem.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterListItem.tsx @@ -10,7 +10,12 @@ import { ListItemSecondaryAction, } from '@mui/material'; import CancelIcon from '@mui/icons-material/CancelOutlined'; -import { useTranslate, useListFilterContext, shallowEqual } from 'ra-core'; +import { + useTranslate, + useListFilterContext, + shallowEqual, + useEvent, +} from 'ra-core'; import matches from 'lodash/matches'; import pickBy from 'lodash/pickBy'; @@ -141,36 +146,26 @@ const arePropsEqual = (prevProps, nextProps) => * ); */ export const FilterListItem = memo((props: FilterListItemProps) => { - const { label, value, ...rest } = props; + const { + label, + value, + isSelected: getIsSelected = DefaultIsSelected, + toggleFilter: userToggleFilter = DefaultToggleFilter, + ...rest + } = props; const { filterValues, setFilters } = useListFilterContext(); const translate = useTranslate(); + const toggleFilter = useEvent(userToggleFilter); - const isSelected = matches( - pickBy(value, val => typeof val !== 'undefined') - )(filterValues); + // We can't wrap this function with useEvent as it is called in the render phase + const isSelected = getIsSelected(value, filterValues); - const addFilter = () => { - setFilters({ ...filterValues, ...value }, null, false); - }; - - const removeFilter = () => { - const keysToRemove = Object.keys(value); - const filters = Object.keys(filterValues).reduce( - (acc, key) => - keysToRemove.includes(key) - ? acc - : { ...acc, [key]: filterValues[key] }, - {} - ); - - setFilters(filters, null, false); - }; - - const toggleFilter = () => (isSelected ? removeFilter() : addFilter()); + const handleClick = () => + setFilters(toggleFilter(value, filterValues), null, false); return ( { { event.stopPropagation(); - toggleFilter(); + handleClick(); }} > @@ -205,6 +200,28 @@ export const FilterListItem = memo((props: FilterListItemProps) => { ); }, arePropsEqual); +const DefaultIsSelected = (value, filters) => + matches(pickBy(value, val => typeof val !== 'undefined'))(filters); + +const DefaultToggleFilter = (value, filters) => { + const isSelected = matches( + pickBy(value, val => typeof val !== 'undefined') + )(filters); + + if (isSelected) { + const keysToRemove = Object.keys(value); + return Object.keys(filters).reduce( + (acc, key) => + keysToRemove.includes(key) + ? acc + : { ...acc, [key]: filters[key] }, + {} + ); + } + + return { ...filters, ...value }; +}; + const PREFIX = 'RaFilterListItem'; export const FilterListItemClasses = { @@ -228,4 +245,6 @@ const StyledListItem = styled(ListItem, { export interface FilterListItemProps extends Omit { label: string | ReactElement; value: any; + toggleFilter?: (value: any, filters: any) => any; + isSelected?: (value: any, filters: any) => boolean; } From e5774fe4fb27120f03cc2d1c2bc2bc20242b27ff Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:24:50 +0100 Subject: [PATCH 2/7] Add documentation --- docs/FilterList.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/docs/FilterList.md b/docs/FilterList.md index 4265462d6e7..a16124d2871 100644 --- a/docs/FilterList.md +++ b/docs/FilterList.md @@ -185,3 +185,65 @@ const CustomerList = props => ( {% endraw %} **Tip**: The `` Sidebar is not a good UI for small screens. You can choose to hide it on small screens (as in the previous example). A good tradeoff is to use `` on large screens, and the Filter Button/Form combo on Mobile. + +## Customize How Filters Are Applied + +Sometimes, you may want to customize how filters are applied. For instance, by allowing users to select multiple items such as selecting multiple categories. The `` component accepts two props for this purpose: + +- `isSelected`: accept a function that receive the item value and the currently applied filters. It must return a boolean. +- `toggleFilter`: accept a function that receive the item value and the currently applied filters. It is called when user toggle a filter and must return the new filters to apply. + +Here's how you can allow users to filter items having one of several categories: + +```jsx +import { FilterList, FilterListItem } from 'react-admin'; +import CategoryIcon from '@mui/icons-material/LocalOffer'; + +export const CategoriesFilter = () => { + const isSelected = (value, filters) => { + const category = filters.category || []; + return category.includes(value.category); + }; + + const toggleFilter = (value, filters) => { + const category = filters.category || []; + return { + ...filters, + category: category.includes(value.category) + // Remove the category if it was already present + ? category.filter(v => v !== value.category) + // Add the category if it wasn't already present + : [...category, value.category], + }; + }; + + return ( + }> + + + + + + ) +} +``` \ No newline at end of file From 39627b1bd749a8c3c591f3378239bc5c8d1f7715 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 23 Feb 2023 09:57:51 +0100 Subject: [PATCH 3/7] Add a screencast showing cumulative filters --- docs/FilterList.md | 4 +++- docs/img/filter-list-cumulative.gif | Bin 0 -> 51721 bytes 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 docs/img/filter-list-cumulative.gif diff --git a/docs/FilterList.md b/docs/FilterList.md index a16124d2871..88c93793530 100644 --- a/docs/FilterList.md +++ b/docs/FilterList.md @@ -246,4 +246,6 @@ export const CategoriesFilter = () => { ) } -``` \ No newline at end of file +``` + +![Cumulative filter list items](./img/filter-list-cumulative.gif) diff --git a/docs/img/filter-list-cumulative.gif b/docs/img/filter-list-cumulative.gif new file mode 100644 index 0000000000000000000000000000000000000000..16494f1b34f1b7d5ef0da476f8c0a91d5d29b442 GIT binary patch literal 51721 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 Y^j3n_#<+Dl@OQ-OAFl@g=f6<;AJ-M-djJ3c literal 0 HcmV?d00001 From 01cb25546b2edb49c0cb707438c67eaa39825d1f Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:18:04 +0100 Subject: [PATCH 4/7] Fix typos Co-authored-by: Jean-Baptiste Kaiser --- docs/FilterList.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/FilterList.md b/docs/FilterList.md index 88c93793530..7a80e770011 100644 --- a/docs/FilterList.md +++ b/docs/FilterList.md @@ -190,8 +190,8 @@ const CustomerList = props => ( Sometimes, you may want to customize how filters are applied. For instance, by allowing users to select multiple items such as selecting multiple categories. The `` component accepts two props for this purpose: -- `isSelected`: accept a function that receive the item value and the currently applied filters. It must return a boolean. -- `toggleFilter`: accept a function that receive the item value and the currently applied filters. It is called when user toggle a filter and must return the new filters to apply. +- `isSelected`: accepts a function that receives the item value and the currently applied filters. It must return a boolean. +- `toggleFilter`: accepts a function that receives the item value and the currently applied filters. It is called when user toggles a filter and must return the new filters to apply. Here's how you can allow users to filter items having one of several categories: From 835a128e92e8ead4296f14ad2eba07917044ae32 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:28:29 +0100 Subject: [PATCH 5/7] Improve tests --- .../src/list/filter/FilterList.stories.tsx | 3 +++ .../src/list/filter/FilterListItem.spec.tsx | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx b/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx index 1a15ada60cd..300278167ec 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterList.stories.tsx @@ -151,6 +151,9 @@ const FilterValue = () => { Filter values:
{JSON.stringify(filterValues, null, 2)}
+
+                {JSON.stringify(filterValues)}
+            
); }; diff --git a/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx b/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx index 1b8897b3070..fc59cadbb09 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx @@ -163,7 +163,7 @@ describe('', () => { ).map(item => item.textContent) ).toEqual(['News', 'Tutorials']); - screen.getByText(/"category":.*\[.*"tutorials",.*"news".*\]/); + screen.getByText(JSON.stringify({ category: ['tutorials', 'news'] })); screen.getByText('News').click(); @@ -174,7 +174,7 @@ describe('', () => { ) ).map(item => item.textContent) ).toEqual(['Tutorials']); - screen.getByText(/"category":.*\[.*"tutorials".*\]/); + screen.getByText(JSON.stringify({ category: ['tutorials'] })); screen.getByText('Tutorials').click(); @@ -185,6 +185,17 @@ describe('', () => { ) ).map(item => item.textContent) ).toEqual([]); - screen.getByText(/{}/); + expect(screen.getAllByText(JSON.stringify({})).length).toBe(2); + + screen.getByText('Tests').click(); + + expect( + Array.from( + container.querySelectorAll( + '[data-selected="true"]' + ) + ).map(item => item.textContent) + ).toEqual(['Tests']); + screen.getByText(JSON.stringify({ category: ['tests'] })); }); }); From cbc146640dcf4669cf81c1dcd78fd2c23e8e53a1 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:11:29 +0100 Subject: [PATCH 6/7] Refactor test --- .../src/list/filter/FilterListItem.spec.tsx | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx b/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx index fc59cadbb09..632008eb3e1 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterListItem.spec.tsx @@ -155,47 +155,30 @@ describe('', () => { it('should allow to customize isSelected and toggleFilter', () => { const { container } = render(); - expect( - Array.from( - container.querySelectorAll( - '[data-selected="true"]' - ) - ).map(item => item.textContent) - ).toEqual(['News', 'Tutorials']); - + expect(getSelectedItemsLabels(container)).toEqual([ + 'News', + 'Tutorials', + ]); screen.getByText(JSON.stringify({ category: ['tutorials', 'news'] })); screen.getByText('News').click(); - expect( - Array.from( - container.querySelectorAll( - '[data-selected="true"]' - ) - ).map(item => item.textContent) - ).toEqual(['Tutorials']); + expect(getSelectedItemsLabels(container)).toEqual(['Tutorials']); screen.getByText(JSON.stringify({ category: ['tutorials'] })); screen.getByText('Tutorials').click(); - expect( - Array.from( - container.querySelectorAll( - '[data-selected="true"]' - ) - ).map(item => item.textContent) - ).toEqual([]); + expect(getSelectedItemsLabels(container)).toEqual([]); expect(screen.getAllByText(JSON.stringify({})).length).toBe(2); screen.getByText('Tests').click(); - expect( - Array.from( - container.querySelectorAll( - '[data-selected="true"]' - ) - ).map(item => item.textContent) - ).toEqual(['Tests']); + expect(getSelectedItemsLabels(container)).toEqual(['Tests']); screen.getByText(JSON.stringify({ category: ['tests'] })); }); }); + +const getSelectedItemsLabels = (container: HTMLElement) => + Array.from( + container.querySelectorAll('[data-selected="true"]') + ).map(item => item.textContent); From 22844ae7ec8b3a71fdef95f5b71bd8f6da40deb3 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Fri, 24 Feb 2023 12:03:48 +0100 Subject: [PATCH 7/7] Update FilterList documentation --- docs/FilterList.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FilterList.md b/docs/FilterList.md index 7a80e770011..41aa665a84d 100644 --- a/docs/FilterList.md +++ b/docs/FilterList.md @@ -193,7 +193,7 @@ Sometimes, you may want to customize how filters are applied. For instance, by a - `isSelected`: accepts a function that receives the item value and the currently applied filters. It must return a boolean. - `toggleFilter`: accepts a function that receives the item value and the currently applied filters. It is called when user toggles a filter and must return the new filters to apply. -Here's how you can allow users to filter items having one of several categories: +Here's how you could implement cumulative filters, e.g. allowing users to filter items having one of several categories: ```jsx import { FilterList, FilterListItem } from 'react-admin';