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