diff --git a/packages/ra-ui-materialui/src/list/List.spec.tsx b/packages/ra-ui-materialui/src/list/List.spec.tsx index 244381d1784..5e117560977 100644 --- a/packages/ra-ui-materialui/src/list/List.spec.tsx +++ b/packages/ra-ui-materialui/src/list/List.spec.tsx @@ -14,7 +14,13 @@ import { List } from './List'; import { Filter } from './filter'; import { TextInput } from '../input'; import { Notification } from '../layout'; -import { Basic, Title, TitleFalse, TitleElement } from './List.stories'; +import { + Basic, + Title, + TitleFalse, + TitleElement, + PartialPagination, +} from './List.stories'; const theme = createTheme(defaultTheme); @@ -104,99 +110,115 @@ describe('', () => { expect(screen.queryAllByText('Hello')).toHaveLength(1); }); - it('should render an invite when the list is empty', async () => { - const Dummy = () => { - const { isPending } = useListContext(); - return
{isPending ? 'loading' : 'dummy'}
; - }; - const dataProvider = { - getList: jest.fn(() => Promise.resolve({ data: [], total: 0 })), - } as any; - render( - - - - - - - - ); - await waitFor(() => { - screen.getByText('resources.posts.empty'); - expect(screen.queryByText('dummy')).toBeNull(); + describe('empty', () => { + it('should render an invite when the list is empty', async () => { + const Dummy = () => { + const { isPending } = useListContext(); + return
{isPending ? 'loading' : 'dummy'}
; + }; + const dataProvider = { + getList: jest.fn(() => Promise.resolve({ data: [], total: 0 })), + } as any; + render( + + + + + + + + ); + await waitFor(() => { + screen.getByText('resources.posts.empty'); + expect(screen.queryByText('dummy')).toBeNull(); + }); }); - }); - it('should not render an invite when the list is empty with an empty prop set to false', async () => { - const Dummy = () => { - const { isPending } = useListContext(); - return
{isPending ? 'loading' : 'dummy'}
; - }; - const dataProvider = { - getList: jest.fn(() => Promise.resolve({ data: [], total: 0 })), - } as any; - render( - - - - - - - - ); - await waitFor(() => { - expect(screen.queryByText('resources.posts.empty')).toBeNull(); - screen.getByText('dummy'); + it('should not render an invite when the list is empty with an empty prop set to false', async () => { + const Dummy = () => { + const { isPending } = useListContext(); + return
{isPending ? 'loading' : 'dummy'}
; + }; + const dataProvider = { + getList: jest.fn(() => Promise.resolve({ data: [], total: 0 })), + } as any; + render( + + + + + + + + ); + await waitFor(() => { + expect(screen.queryByText('resources.posts.empty')).toBeNull(); + screen.getByText('dummy'); + }); }); - }); - it('should render custom empty component when data is empty', async () => { - const Dummy = () => null; - const CustomEmpty = () =>
Custom Empty
; + it('should not render an empty component when using partial pagination and the list is not empty', async () => { + render(); + await waitFor(() => { + expect(screen.queryByText('resources.posts.empty')).toBeNull(); + screen.getByText('John Doe'); + }); + }); - const dataProvider = { - getList: jest.fn(() => - Promise.resolve({ - data: [], - pageInfo: { hasNextPage: false, hasPreviousPage: false }, - }) - ), - } as any; - render( - - - }> - - - - - ); - await waitFor(() => { - expect(screen.queryByText('resources.posts.empty')).toBeNull(); - screen.getByText('Custom Empty'); + it('should render custom empty component when data is empty', async () => { + const Dummy = () => null; + const CustomEmpty = () =>
Custom Empty
; + + const dataProvider = { + getList: jest.fn(() => + Promise.resolve({ + data: [], + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }) + ), + } as any; + render( + + + }> + + + + + ); + await waitFor(() => { + expect(screen.queryByText('resources.posts.empty')).toBeNull(); + screen.getByText('Custom Empty'); + }); }); - }); - it('should not render an invite when a filter is active', async () => { - const Dummy = () => { - const { isPending } = useListContext(); - return
{isPending ? 'loading' : 'dummy'}
; - }; - const dataProvider = { - getList: jest.fn(() => Promise.resolve({ data: [], total: 0 })), - } as any; - render( - - - - - - - - ); - await waitFor(() => { - expect(screen.queryByText('resources.posts.empty')).toBeNull(); - screen.getByText('dummy'); + it('should not render an invite when a filter is active', async () => { + const Dummy = () => { + const { isPending } = useListContext(); + return
{isPending ? 'loading' : 'dummy'}
; + }; + const dataProvider = { + getList: jest.fn(() => Promise.resolve({ data: [], total: 0 })), + } as any; + render( + + + + + + + + ); + await waitFor(() => { + expect(screen.queryByText('resources.posts.empty')).toBeNull(); + screen.getByText('dummy'); + }); }); }); diff --git a/packages/ra-ui-materialui/src/list/List.stories.tsx b/packages/ra-ui-materialui/src/list/List.stories.tsx index 38a13aa0f96..37b99a26627 100644 --- a/packages/ra-ui-materialui/src/list/List.stories.tsx +++ b/packages/ra-ui-materialui/src/list/List.stories.tsx @@ -5,6 +5,7 @@ import { Resource, useListContext, TestMemoryRouter, + DataProvider, } from 'ra-core'; import fakeRestDataProvider from 'ra-data-fakerest'; import { Box, Card, Typography, Button, Link as MuiLink } from '@mui/material'; @@ -336,6 +337,39 @@ export const Component = () => ( ); +export const PartialPagination = () => ( + + ({ + data: [ + { + id: 1, + name: 'John Doe', + }, + ], + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }), + } as DataProvider + } + > + ( + + + + )} + create={() => } + /> + + +); + export const Empty = () => ( @@ -352,6 +386,34 @@ export const Empty = () => ( ); +export const EmptyPartialPagination = () => ( + + ({ + data: [], + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }), + } as unknown as DataProvider + } + > + ( + + + + )} + create={() => } + /> + + +); + export const SX = () => ( diff --git a/packages/ra-ui-materialui/src/list/ListView.tsx b/packages/ra-ui-materialui/src/list/ListView.tsx index 1c0a221c6da..f3aa291fe23 100644 --- a/packages/ra-ui-materialui/src/list/ListView.tsx +++ b/packages/ra-ui-materialui/src/list/ListView.tsx @@ -67,13 +67,16 @@ export const ListView = ( empty !== false &&
{empty}
; const shouldRenderEmptyPage = + !error && // the list is not loading data for the first time !isPending && // the API returned no data (using either normal or partial pagination) (total === 0 || (total == null && hasPreviousPage === false && - hasNextPage === false)) && + hasNextPage === false && + // @ts-ignore FIXME total may be undefined when using partial pagination but the ListControllerResult type is wrong about it + data.length === 0)) && // the user didn't set any filters !Object.keys(filterValues).length && // there is an empty page component