From 656fb840f76ad5830784d24420ceedebf8928944 Mon Sep 17 00:00:00 2001
From: Gildas Garcia <1122076+djhi@users.noreply.github.com>
Date: Tue, 17 Jan 2023 12:50:26 +0100
Subject: [PATCH 1/3] Make List children responsible of the empty state
---
docs/Datagrid.md | 5 +-
docs/SimpleList.md | 20 +++++
.../src/list/ListNoResults.tsx | 16 ++++
.../src/list/SimpleList/SimpleList.spec.tsx | 16 ++++
.../src/list/SimpleList/SimpleList.tsx | 17 ++++
.../src/list/datagrid/Datagrid.spec.tsx | 9 +++
.../src/list/datagrid/Datagrid.tsx | 7 +-
packages/ra-ui-materialui/src/list/index.ts | 1 +
.../src/list/pagination/Pagination.spec.tsx | 80 -------------------
.../src/list/pagination/Pagination.tsx | 11 ++-
.../src/list/pagination/PaginationLimit.tsx | 3 +
11 files changed, 97 insertions(+), 88 deletions(-)
create mode 100644 packages/ra-ui-materialui/src/list/ListNoResults.tsx
diff --git a/docs/Datagrid.md b/docs/Datagrid.md
index ebd87eb010a..ed2f1ae6627 100644
--- a/docs/Datagrid.md
+++ b/docs/Datagrid.md
@@ -368,8 +368,9 @@ const CustomResetViewsButton = () => {
## `empty`
-It's possible that a Datagrid will have no records to display. If the Datagrid's parent component handles the loading state, the Datagrid will return `null` and render nothing.
-Passing through a component to the `empty` prop will cause the Datagrid to render the `empty` component instead of `null`.
+It's possible that a Datagrid will have no records to display. If the Datagrid's parent component does not handle the loading state, the Datagrid will display a message indicating there are no results. This message is translatable and its key is `ra.navigation.no_results`.
+
+You can customize the empty state by passing a component to the `empty` prop:
```jsx
const CustomEmpty = () =>
No books found
;
diff --git a/docs/SimpleList.md b/docs/SimpleList.md
index 541a62bfdda..ef476622e0c 100644
--- a/docs/SimpleList.md
+++ b/docs/SimpleList.md
@@ -44,6 +44,7 @@ It accepts the following props:
* [`rightAvatar`](#rightavatar)
* [`rightIcon`](#righticon)
* [`rowStyle`](#rowstyle)
+* [`empty`](#empty)
## `leftAvatar`
@@ -179,6 +180,25 @@ See [`primaryText`](#primarytext)
See [`primaryText`](#primarytext)
+## `empty`
+
+It's possible that a SimpleList will have no records to display. If the SimpleList's parent component does not handle the loading state, the SimpleList will display a message indicating there are no results. This message is translatable and its key is `ra.navigation.no_results`.
+
+You can customize the empty state by passing a component to the `empty` prop:
+
+```jsx
+const CustomEmpty = () => No books found
;
+
+const PostList = () => (
+
+ record.title}
+ empty={}
+ />
+
+);
+```
+
## Using `` On Small Screens
To use `` on small screens and a `` on larger screens, use MUI's `useMediaQuery` hook:
diff --git a/packages/ra-ui-materialui/src/list/ListNoResults.tsx b/packages/ra-ui-materialui/src/list/ListNoResults.tsx
new file mode 100644
index 00000000000..d1abe0435ca
--- /dev/null
+++ b/packages/ra-ui-materialui/src/list/ListNoResults.tsx
@@ -0,0 +1,16 @@
+import * as React from 'react';
+import { memo } from 'react';
+import CardContent from '@mui/material/CardContent';
+import Typography from '@mui/material/Typography';
+import { useTranslate } from 'ra-core';
+
+export const ListNoResults = memo(() => {
+ const translate = useTranslate();
+ return (
+
+
+ {translate('ra.navigation.no_results')}
+
+
+ );
+});
diff --git a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx
index d87b7305f04..b1664d11584 100644
--- a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx
+++ b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx
@@ -130,4 +130,20 @@ describe('', () => {
expect(screen.getByText('2').closest('a')).toBeNull();
});
});
+
+ it('should display a message when there is no result', () => {
+ render(
+
+
+
+ );
+ expect(screen.queryByText('ra.navigation.no_results')).not.toBeNull();
+ });
});
diff --git a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.tsx b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.tsx
index da1195ce6a5..575bc06c1b4 100644
--- a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.tsx
+++ b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.tsx
@@ -27,6 +27,7 @@ import {
} from 'ra-core';
import { SimpleListLoading } from './SimpleListLoading';
+import { ListNoResults } from '../ListNoResults';
/**
* The component renders a list of records as a MUI .
@@ -67,6 +68,7 @@ export const SimpleList = (
) => {
const {
className,
+ empty = DefaultEmpty,
hasBulkActions,
leftAvatar,
leftIcon,
@@ -95,6 +97,18 @@ export const SimpleList = (
);
}
+ /**
+ * Once loaded, the data for the list may be empty. Instead of
+ * displaying the table header with zero data rows,
+ * the SimpleList the empty component.
+ */
+ if (data == null || data.length === 0 || total === 0) {
+ if (empty) {
+ return empty;
+ }
+
+ return null;
+ }
const renderAvatar = (
record: RecordType,
avatarCallback: FunctionToElement
@@ -245,6 +259,7 @@ export type FunctionToElement = (
export interface SimpleListProps
extends Omit {
className?: string;
+ empty?: ReactElement;
hasBulkActions?: boolean;
leftAvatar?: FunctionToElement;
leftIcon?: FunctionToElement;
@@ -321,3 +336,5 @@ const Root = styled(List, {
})({
[`& .${SimpleListClasses.tertiary}`]: { float: 'right', opacity: 0.541176 },
});
+
+const DefaultEmpty = ;
diff --git a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.spec.tsx b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.spec.tsx
index 9b140899ab4..6259abd5cdd 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.spec.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.spec.tsx
@@ -268,4 +268,13 @@ describe('', () => {
expect(contextValue.onSelect).toHaveBeenCalledTimes(1);
});
});
+
+ it('should display a message when there is no result', () => {
+ render(
+
+
+
+ );
+ expect(screen.queryByText('ra.navigation.no_results')).not.toBeNull();
+ });
});
diff --git a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx
index 37b8bad5de0..aad55a90ff5 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx
@@ -32,6 +32,7 @@ import DatagridContextProvider from './DatagridContextProvider';
import { DatagridClasses, DatagridRoot } from './useDatagridStyles';
import { BulkActionsToolbar } from '../BulkActionsToolbar';
import { BulkDeleteButton } from '../../button';
+import { ListNoResults } from '../ListNoResults';
const defaultBulkActionButtons = ;
@@ -120,7 +121,7 @@ export const Datagrid: FC = React.forwardRef((props, ref) => {
header = DatagridHeader,
children,
className,
- empty,
+ empty = DefaultEmpty,
expand,
bulkActionButtons = defaultBulkActionButtons,
hover,
@@ -210,7 +211,7 @@ export const Datagrid: FC = React.forwardRef((props, ref) => {
/**
* Once loaded, the data for the list may be empty. Instead of
* displaying the table header with zero data rows,
- * the datagrid displays nothing or a custom empty component.
+ * the Datagrid the empty component.
*/
if (data == null || data.length === 0 || total === 0) {
if (empty) {
@@ -369,3 +370,5 @@ const sanitizeRestProps = props =>
.reduce((acc, key) => ({ ...acc, [key]: props[key] }), {});
Datagrid.displayName = 'Datagrid';
+
+const DefaultEmpty = ;
diff --git a/packages/ra-ui-materialui/src/list/index.ts b/packages/ra-ui-materialui/src/list/index.ts
index fba6aa18b7f..b18134056e1 100644
--- a/packages/ra-ui-materialui/src/list/index.ts
+++ b/packages/ra-ui-materialui/src/list/index.ts
@@ -8,6 +8,7 @@ export * from './List';
export * from './ListActions';
export * from './listFieldTypes';
export * from './ListGuesser';
+export * from './ListNoResults';
export * from './ListToolbar';
export * from './ListView';
export * from './pagination';
diff --git a/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx b/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx
index ff64c111a03..644316f2567 100644
--- a/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx
+++ b/packages/ra-ui-materialui/src/list/pagination/Pagination.spec.tsx
@@ -21,86 +21,6 @@ describe('', () => {
hasPreviousPage: undefined,
};
- describe('no results mention', () => {
- it('should display a pagination limit when there is no result', () => {
- render(
-
-
-
-
-
- );
- expect(
- screen.queryByText('ra.navigation.no_results')
- ).not.toBeNull();
- });
-
- it('should not display a pagination limit when there are results', () => {
- render(
-
-
-
-
-
- );
- expect(screen.queryByText('ra.navigation.no_results')).toBeNull();
- });
-
- it('should display a pagination limit on an out of bounds page (more than total pages)', async () => {
- jest.spyOn(console, 'error').mockImplementationOnce(() => {});
- const setPage = jest.fn().mockReturnValue(null);
- render(
-
-
-
-
-
- );
- // mui TablePagination displays no more a warning in that case
- // Then useEffect fallbacks on a valid page
- expect(
- screen.queryByText('ra.navigation.no_results')
- ).not.toBeNull();
- });
-
- it('should display a pagination limit on an out of bounds page (less than 0)', async () => {
- jest.spyOn(console, 'error').mockImplementationOnce(() => {});
- const setPage = jest.fn().mockReturnValue(null);
- render(
-
-
-
-
-
- );
- // mui TablePagination displays no more a warning in that case
- // Then useEffect fallbacks on a valid page
- expect(
- screen.queryByText('ra.navigation.no_results')
- ).not.toBeNull();
- });
- });
-
describe('Total pagination', () => {
it('should display a next button when there are more results', () => {
render(
diff --git a/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx b/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx
index 4a4f4bda1b5..c8e6637d93c 100644
--- a/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx
+++ b/packages/ra-ui-materialui/src/list/pagination/Pagination.tsx
@@ -17,13 +17,12 @@ import {
} from 'ra-core';
import { PaginationActions } from './PaginationActions';
-import { PaginationLimit } from './PaginationLimit';
export const Pagination: FC = memo(props => {
const {
rowsPerPageOptions = DefaultRowsPerPageOptions,
actions,
- limit = DefaultLimit,
+ limit = null,
...rest
} = props;
const {
@@ -97,7 +96,12 @@ export const Pagination: FC = memo(props => {
// Avoid rendering TablePagination if "page" value is invalid
if (total === 0 || page < 1 || (total != null && page > totalPages)) {
- return limit;
+ if (limit != null && process.env.NODE_ENV === 'development') {
+ console.warn(
+ 'The Pagination limit prop is deprecated. Empty state should be handled by the component displaying data (Datagrid, SimpleList).'
+ );
+ }
+ return null;
}
if (isSmall) {
@@ -149,7 +153,6 @@ Pagination.propTypes = {
rowsPerPageOptions: PropTypes.arrayOf(PropTypes.number),
};
-const DefaultLimit = ;
const DefaultRowsPerPageOptions = [5, 10, 25, 50];
const emptyArray = [];
diff --git a/packages/ra-ui-materialui/src/list/pagination/PaginationLimit.tsx b/packages/ra-ui-materialui/src/list/pagination/PaginationLimit.tsx
index 747c7206ead..cc8b9cf9fa3 100644
--- a/packages/ra-ui-materialui/src/list/pagination/PaginationLimit.tsx
+++ b/packages/ra-ui-materialui/src/list/pagination/PaginationLimit.tsx
@@ -4,6 +4,9 @@ import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import { useTranslate } from 'ra-core';
+/**
+ * @deprecated Empty state should be handled by the component displaying data (Datagrid, SimpleList).
+ */
export const PaginationLimit = memo(() => {
const translate = useTranslate();
return (
From b25f6df67c891ef8c5bf5404a650cc3e06fd9226 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Kaiser
Date: Fri, 10 Feb 2023 15:40:57 +0100
Subject: [PATCH 2/3] code review
---
docs/Datagrid.md | 2 +-
docs/SimpleList.md | 2 +-
packages/ra-ui-materialui/src/list/ListNoResults.tsx | 5 +++--
packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx | 2 +-
4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/docs/Datagrid.md b/docs/Datagrid.md
index ed2f1ae6627..d0653cedf1a 100644
--- a/docs/Datagrid.md
+++ b/docs/Datagrid.md
@@ -368,7 +368,7 @@ const CustomResetViewsButton = () => {
## `empty`
-It's possible that a Datagrid will have no records to display. If the Datagrid's parent component does not handle the loading state, the Datagrid will display a message indicating there are no results. This message is translatable and its key is `ra.navigation.no_results`.
+It's possible that a Datagrid will have no records to display. If the Datagrid's parent component does not handle the empty state, the Datagrid will display a message indicating there are no results. This message is translatable and its key is `ra.navigation.no_results`.
You can customize the empty state by passing a component to the `empty` prop:
diff --git a/docs/SimpleList.md b/docs/SimpleList.md
index ef476622e0c..684d2485b97 100644
--- a/docs/SimpleList.md
+++ b/docs/SimpleList.md
@@ -182,7 +182,7 @@ See [`primaryText`](#primarytext)
## `empty`
-It's possible that a SimpleList will have no records to display. If the SimpleList's parent component does not handle the loading state, the SimpleList will display a message indicating there are no results. This message is translatable and its key is `ra.navigation.no_results`.
+It's possible that a SimpleList will have no records to display. If the SimpleList's parent component does not handle the empty state, the SimpleList will display a message indicating there are no results. This message is translatable and its key is `ra.navigation.no_results`.
You can customize the empty state by passing a component to the `empty` prop:
diff --git a/packages/ra-ui-materialui/src/list/ListNoResults.tsx b/packages/ra-ui-materialui/src/list/ListNoResults.tsx
index d1abe0435ca..1f2f8acd97a 100644
--- a/packages/ra-ui-materialui/src/list/ListNoResults.tsx
+++ b/packages/ra-ui-materialui/src/list/ListNoResults.tsx
@@ -2,14 +2,15 @@ import * as React from 'react';
import { memo } from 'react';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
-import { useTranslate } from 'ra-core';
+import { useResourceContext, useTranslate } from 'ra-core';
export const ListNoResults = memo(() => {
const translate = useTranslate();
+ const resource = useResourceContext();
return (
- {translate('ra.navigation.no_results')}
+ {translate('ra.navigation.no_results', { resource })}
);
diff --git a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx
index aad55a90ff5..0c55fa3abab 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx
@@ -211,7 +211,7 @@ export const Datagrid: FC = React.forwardRef((props, ref) => {
/**
* Once loaded, the data for the list may be empty. Instead of
* displaying the table header with zero data rows,
- * the Datagrid the empty component.
+ * the Datagrid displays the empty component.
*/
if (data == null || data.length === 0 || total === 0) {
if (empty) {
From 04746969daa4a583258037f518eb46f07621fa5f Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Kaiser
Date: Fri, 10 Feb 2023 15:41:19 +0100
Subject: [PATCH 3/3] fix linter warning
---
packages/ra-ui-materialui/src/list/ListView.tsx | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/packages/ra-ui-materialui/src/list/ListView.tsx b/packages/ra-ui-materialui/src/list/ListView.tsx
index 9931925b5be..c4f0c98d182 100644
--- a/packages/ra-ui-materialui/src/list/ListView.tsx
+++ b/packages/ra-ui-materialui/src/list/ListView.tsx
@@ -1,12 +1,6 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
-import {
- Children,
- cloneElement,
- ReactElement,
- ReactNode,
- ElementType,
-} from 'react';
+import { cloneElement, ReactElement, ReactNode, ElementType } from 'react';
import PropTypes from 'prop-types';
import { SxProps } from '@mui/system';
import Card from '@mui/material/Card';