diff --git a/cypress/e2e/mobile.cy.js b/cypress/e2e/mobile.cy.js index 7203edebc30..7610a62441b 100644 --- a/cypress/e2e/mobile.cy.js +++ b/cypress/e2e/mobile.cy.js @@ -9,10 +9,10 @@ describe('Mobile UI', () => { }); describe('Infinite Scroll', () => { - it.only('should load more items when scrolling to the bottom of the page', () => { + it('should load more items when scrolling to the bottom of the page', () => { ListPagePosts.navigate(); + cy.contains('Fusce massa lorem').should('exist'); cy.contains('Sed quo et et fugiat modi').should('not.exist'); - cy.scrollTo('bottom'); cy.wait(500); cy.scrollTo('bottom'); cy.contains('Sed quo et et fugiat modi'); diff --git a/packages/ra-ui-materialui/src/list/filter/FilterButton.stories.tsx b/packages/ra-ui-materialui/src/list/filter/FilterButton.stories.tsx index 55a09d3fed5..4e62312de47 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterButton.stories.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterButton.stories.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { Chip } from '@mui/material'; import { ListBase, memoryStore } from 'ra-core'; import { Admin, @@ -13,6 +14,7 @@ import { TopToolbar, SearchInput, FilterButtonProps, + InputProps, } from 'react-admin'; import fakerestDataProvider from 'ra-data-fakerest'; import { @@ -383,6 +385,33 @@ export const WithAutoCompleteArrayInput = (args: { ); }; +const QuickFilter = ({ label }: InputProps) => ; + +export const WithComplexValueFilter = (args: { + disableSaveQuery?: boolean; +}) => { + const postFilters: React.ReactElement[] = [ + , + ]; + return ( + + + } + /> + + + ); +}; + export const Variant = () => { const postFilters: React.ReactElement[] = [ ', () => { }); }); - it('should allow to add and clear a filter with a complex object value', async () => { + it('should allow to add and clear a filter with a nested value', async () => { render(); const addFilterButton = await screen.findByText('Add filter'); @@ -230,6 +232,25 @@ describe('', () => { expect(screen.queryByLabelText('Nested')).toBeNull(); }); + it('should hide a removed filter with a complex object value', async () => { + render(); + + const addFilterButton = await screen.findByText('Add filter'); + fireEvent.click(addFilterButton); + fireEvent.click(await screen.findByText('Complex')); + await screen.findByText('1-7 of 7'); + await screen.findByText('Complex', { + selector: `.${chipClasses.root} *`, + }); + fireEvent.click(await screen.findByTitle('Remove this filter')); + await screen.findByText('1-10 of 13'); + expect( + screen.queryByText('Complex', { + selector: `.${chipClasses.root} *`, + }) + ).toBeNull(); + }); + it('should provide a FormGroupContext', async () => { render(); diff --git a/packages/ra-ui-materialui/src/list/filter/FilterForm.tsx b/packages/ra-ui-materialui/src/list/filter/FilterForm.tsx index 6751ad17b2b..1cf14ba1c6f 100644 --- a/packages/ra-ui-materialui/src/list/filter/FilterForm.tsx +++ b/packages/ra-ui-materialui/src/list/filter/FilterForm.tsx @@ -123,7 +123,7 @@ export const FilterFormBase = (props: FilterFormBaseProps) => { return ( filterElement.props.alwaysOn || displayedFilters[filterElement.props.source] || - (filterValue !== '' && typeof filterValue !== 'undefined') + !isEmptyValue(filterValue) ); }); }; @@ -301,3 +301,17 @@ const getInputValue = ( } return get(filterValues, key, ''); }; + +const isEmptyValue = (filterValue: unknown) => { + if (filterValue === '' || typeof filterValue === 'undefined') return true; + + // If one of the value leaf is not empty + // the value is considered not empty + if (typeof filterValue === 'object') { + return Object.keys(filterValue).every(key => + isEmptyValue(filterValue[key]) + ); + } + + return false; +};