diff --git a/docs/Datagrid.md b/docs/Datagrid.md index f8bf8838018..f280ef78588 100644 --- a/docs/Datagrid.md +++ b/docs/Datagrid.md @@ -38,24 +38,24 @@ The `` is an **iterator** component: it gets an array of records from ## Props -Here are all the props accepted by the component: - -* [`body`](#body) -* [`bulkActionButtons`](#bulkactionbuttons) -* [`children`](#children) -* [`empty`](#empty) -* [`expand`](#expand) -* [`expandSingle`](#expandsingle) -* [`header`](#header) -* [`hover`](#hover) -* [`isRowExpandable`](#isrowexpandable) -* [`isRowSelectable`](#isrowselectable) -* [`optimized`](#optimized-better-performance-for-large-tables) -* [`rowStyle`](#rowstyle) -* [`rowSx`](#rowsx) -* [`rowClick`](#rowclick) -* [`size`](#size) -* [`sx`](#sx-css-api) +| Prop | Required | Type | Default | Description | +| --- | --- | --- | --- | --- | +| `children` | Required | Element | n/a | The list of `` components to render as columns. | +| `body` | Optional | Element | `` | The component used to render the body of the table. | +| `bulkActionButtons` | Optional | Element | `` | The component used to render the bulk action buttons. | +| `empty` | Optional | Element | `` | The component used to render the empty table. | +| `expand` | Optional | Element | | The component used to render the expand panel for each row. | +| `expandSingle` | Optional | Boolean | `false` | Whether to allow only one expanded row at a time. | +| `header` | Optional | Element | `` | The component used to render the table header. | +| `hover` | Optional | Boolean | `true` | Whether to highlight the row under the mouse. | +| `isRowExpandable` | Optional | Function | `() => true` | A function that returns whether a row is expandable. | +| `isRowSelectable` | Optional | Function | `() => true` | A function that returns whether a row is selectable. | +| `optimized` | Optional | Boolean | `false` | Whether to optimize the rendering of the table. | +| `rowClick` | Optional | mixed | | The action to trigger when the user clicks on a row. | +| `rowStyle` | Optional | Function | | A function that returns the style to apply to a row. | +| `rowSx` | Optional | Function | | A function that returns the sx prop to apply to a row. | +| `size` | Optional | `'small'` or `'medium'` | `'small'` | The size of the table. | +| `sx` | Optional | Object | | The sx prop passed down to the Material UI `` element. | Additional props are passed down to [the Material UI `
` element](https://mui.com/material-ui/api/table/). @@ -107,39 +107,6 @@ const PostList = () => ( export default PostList; ``` -## `children` - -`` accepts a list of Field components as children. It inspects each child's `source` and/or `label` props to determine the name of the column. - -What's a Field component? Simply a component that reads the record (via `useRecordContext`) and renders a value. React-admin includes many Field components that you can use as children of `` (``, ``, ``, ``, and many more). Check [the Fields documentation](./Fields.md) for more information. - -You can even create your own field components. - -```jsx -// in src/posts.js -import * as React from 'react'; -import { useRecordContext, List, Datagrid, TextField, DateField } from 'react-admin'; - -const FullNameField = () => { - const record = useRecordContext(); - return {record.firstName} {record.lastName}; -} - -export const UserList = () => ( - - - - - - - -); -``` - -`` also inspects its children for `headerClassName` and `cellClassName` props, and gives the class names to the headers and the cells of that column. - -Finally, `` inspects children for props that indicate how it should be sorted (see [the Customizing The Sort Order For Columns section](#customizing-column-sort)) below. - ## `bulkActionButtons` ` element) based on the record, thanks to the `rowStyle` prop, which expects a function. React-admin calls this function for each row, passing the current record and index as arguments. The function should return a style object, which react-admin uses as a `` prop. +## `rowClick` -For instance, this allows to apply a custom background to the entire row if one value of the record - like its number of views - passes a certain threshold. +You can catch clicks on rows to redirect to the show or edit view by setting the `rowClick` prop: ```jsx import { List, Datagrid } from 'react-admin'; -const postRowStyle = (record, index) => ({ - backgroundColor: record.nb_views >= 500 ? '#efe' : 'white', -}); export const PostList = () => ( - + ... ); ``` -## `rowSx` +`rowClick` accepts the following values: -You can customize the styles of rows and cells in `` (applied to the `` element) based on the record, thanks to the `rowSx` prop, which expects a function. React-admin calls this function for each row, passing the current record and index as arguments. The function should return a Material UI [`sx`](https://mui.com/system/getting-started/the-sx-prop/), which react-admin uses as a `` prop. +* "edit" to redirect to the edition view +* "show" to redirect to the show view +* "expand" to open the `expand` panel +* "toggleSelection" to trigger the `onToggleItem` function +* `false` to do nothing +* a function `(id, resource, record) => path` that may return any of the above values or a custom path + +**Tip**: If you pass a function, it can return `'edit'`, `'show'`, `false` or a router path. This allows to redirect to either the Edit or Show view after checking a condition on the record. For example: + +```js +const postRowClick = (id, resource, record) => record.editable ? 'edit' : 'show'; +``` + +**Tip**: If you pass a function, it can also return a promise allowing you to check an external API before returning a path. For example: + +```js +import fetchUserRights from './fetchUserRights'; + +const getPermissions = useGetPermissions(); +const postRowClick = (id, resource, record) => + useGetPermissions() + .then(permissions => permissions === 'admin' ? 'edit' : 'show'); +``` + +## `rowStyle` + +*Deprecated - use [`rowSx`](#rowsx) instead.* + +You can customize the `` row style (applied to the `` element) based on the record, thanks to the `rowStyle` prop, which expects a function. React-admin calls this function for each row, passing the current record and index as arguments. The function should return a style object, which react-admin uses as a `` prop. For instance, this allows to apply a custom background to the entire row if one value of the record - like its number of views - passes a certain threshold. ```jsx import { List, Datagrid } from 'react-admin'; -const postRowSx = (record, index) => ({ +const postRowStyle = (record, index) => ({ backgroundColor: record.nb_views >= 500 ? '#efe' : 'white', }); export const PostList = () => ( - + ... ); ``` -## `rowClick` +## `rowSx` -You can catch clicks on rows to redirect to the show or edit view by setting the `rowClick` prop: +You can customize the styles of rows and cells in `` (applied to the `` element) based on the record, thanks to the `rowSx` prop, which expects a function. React-admin calls this function for each row, passing the current record and index as arguments. The function should return a Material UI [`sx`](https://mui.com/system/getting-started/the-sx-prop/), which react-admin uses as a `` prop. + +For instance, this allows to apply a custom background to the entire row if one value of the record - like its number of views - passes a certain threshold. ```jsx import { List, Datagrid } from 'react-admin'; +const postRowSx = (record, index) => ({ + backgroundColor: record.nb_views >= 500 ? '#efe' : 'white', +}); export const PostList = () => ( - + ... ); ``` -`rowClick` accepts the following values: - -* "edit" to redirect to the edition vue -* "show" to redirect to the show vue -* "expand" to open the `expand` panel -* "toggleSelection" to trigger the `onToggleItem` function -* `false` to do nothing -* a function `(id, resource, record) => path` that may return any of the above values or a custom path - -**Tip**: If you pass a function, it can return `'edit'`, `'show'`, `false` or a router path. This allows to redirect to either the Edit or Show view after checking a condition on the record. For example: - -```js -const postRowClick = (id, resource, record) => record.editable ? 'edit' : 'show'; -``` - -**Tip**: If you pass a function, it can also return a promise allowing you to check an external API before returning a path. For example: - -```js -import fetchUserRights from './fetchUserRights'; - -const getPermissions = useGetPermissions(); -const postRowClick = (id, resource, record) => - useGetPermissions() - .then(permissions => permissions === 'admin' ? 'edit' : 'show'); -``` - ## `size` The `` is designed for a high density of content, so the row padding is low. If you want to add more margin to each cell, set the `size` prop to `medium`. diff --git a/docs/SimpleList.md b/docs/SimpleList.md index 82a37334a78..2bf275370cf 100644 --- a/docs/SimpleList.md +++ b/docs/SimpleList.md @@ -37,19 +37,40 @@ export const PostList = () => ( `` executes the functions passed as `primaryText`, `secondaryText`, and `tertiaryText` on render, passing the current `record` as parameter. It uses the result to render each List item. -It accepts the following props: - -* [`primaryText`](#primarytext) (required) -* [`secondaryText`](#secondarytext) -* [`tertiaryText`](#tertiarytext) -* [`linkType`](#linktype) -* [`leftAvatar`](#leftavatar) -* [`leftIcon`](#lefticon) -* [`rightAvatar`](#rightavatar) -* [`rightIcon`](#righticon) -* [`rowStyle`](#rowstyle) -* [`rowSx`](#rowsx) -* [`empty`](#empty) +## Props + +| Prop | Required | Type | Default | Description | +| --- | --- | --- | --- | --- | +| `primaryText` | Optional | mixed | record representation | The primary text to display. | +| `secondaryText` | Optional | mixed | | The secondary text to display. | +| `tertiaryText` | Optional | mixed | | The tertiary text to display. | +| `linkType` | Optional |mixed | `"edit"` | The target of each item click. | +| `leftAvatar` | Optional | function | | A function returning an `` component to display before the primary text. | +| `leftIcon` | Optional | function | | A function returning an `` component to display before the primary text. | +| `rightAvatar` | Optional | function | | A function returning an `` component to display after the primary text. | +| `rightIcon` | Optional | function | | A function returning an `` component to display after the primary text. | +| `rowStyle` | Optional | function | | A function returning a style object to apply to each row. | +| `rowSx` | Optional | function | | A function returning a sx object to apply to each row. | +| `empty` | Optional | ReactElement | | A ReactElement to display instead of the list when the data is empty. | + +## `empty` + +It's possible that a `` will have no records to display. If the ``'s parent component does not handle the empty state, the `` will display a message indicating there are no results. This message is translatable with the key `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={} + /> + +); +``` ## `leftAvatar` @@ -78,22 +99,20 @@ export const PostList = () => ( ); ``` - `linkType` accepts the following values: * `linkType="edit"`: links to the edit page. This is the default behavior. * `linkType="show"`: links to the show page. * `linkType={false}`: does not create any link. - - ## `primaryText` -The `primaryText`, `secondaryText` and `tertiaryText` props can accept 3 types of values: +The `primaryText`, `secondaryText` and `tertiaryText` props can accept 4 types of values: 1. a function returning a string, 2. a string, 3. a React element. +4. `undefined` (the default) If it's a **function**, react-admin passes the current record as parameter: @@ -151,6 +170,18 @@ export const PostList = () => ( `` creates a `RecordContext` for each list item. This allows Field components to grab the current record using [`useRecordContext`](./useRecordContext.md). +If it's **undefined**, react-admin uses the [`recordRepresentation`](./Resource.md#recordrepresentation) for the current Resource. This is the default value. + +```jsx +import { List, SimpleList } from 'react-admin'; + +export const PostList = () => ( + + + +); +``` + ## `rightAvatar` This prop should be a function returning an `` component. When present, the `` renders a `` after the `` @@ -161,6 +192,8 @@ This prop should be a function returning an `` component. When present, th ## `rowStyle` +*Deprecated - use [`rowSx`](#rowsx) instead.* + This optional prop should be a function, which gets called for each row. It receives the current record and index as arguments, and should return a style object. The style object is applied to the `` styles prop. ```jsx @@ -203,24 +236,6 @@ 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 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: - -```jsx -const CustomEmpty = () =>
No books found
; - -const PostList = () => ( - - record.title} - empty={} - /> - -); -``` ## Using `` On Small Screens 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 b1664d11584..fbf5dcef8b6 100644 --- a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx +++ b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.spec.tsx @@ -5,6 +5,7 @@ import { ListContext, ResourceContextProvider } from 'ra-core'; import { AdminContext } from '../../AdminContext'; import { SimpleList } from './SimpleList'; import { TextField } from '../../field'; +import { NoPrimaryText } from './SimpleList.stories'; const Wrapper = ({ children }: any) => ( @@ -146,4 +147,9 @@ describe('', () => { ); expect(screen.queryByText('ra.navigation.no_results')).not.toBeNull(); }); + + it('should fall back to record representation when no primaryText is provided', async () => { + render(); + await screen.findByText('War and Peace'); + }); }); diff --git a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.stories.tsx b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.stories.tsx index 2b3d1148665..b5b5028f00d 100644 --- a/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.stories.tsx +++ b/packages/ra-ui-materialui/src/list/SimpleList/SimpleList.stories.tsx @@ -131,6 +131,26 @@ export const FullApp = () => ( ); +export const NoPrimaryText = () => ( + defaultMessages, 'en')} + > + + ( + + + + )} + edit={EditGuesser} + /> + + +); + export const ErrorInFetch = () => ( ( } = props; const { data, isLoading, total } = useListContext(props); const resource = useResourceContext(props); + const getRecordRepresentation = useGetRecordRepresentation(resource); const translate = useTranslate(); if (isLoading === true) { @@ -158,14 +160,16 @@ export const SimpleList = ( - {typeof primaryText === 'string' - ? translate(primaryText, { - ...record, - _: primaryText, - }) - : isElement(primaryText) - ? primaryText - : primaryText(record, record.id)} + {primaryText + ? typeof primaryText === 'string' + ? translate(primaryText, { + ...record, + _: primaryText, + }) + : isElement(primaryText) + ? primaryText + : primaryText(record, record.id) + : getRecordRepresentation(record)} {!!tertiaryText && (isValidElement(tertiaryText) ? ( @@ -280,6 +284,9 @@ export interface SimpleListProps secondaryText?: FunctionToElement | ReactElement | string; tertiaryText?: FunctionToElement | ReactElement | string; rowSx?: (record: RecordType, index: number) => SxProps; + /** + * @deprecated Use rowSx instead + */ rowStyle?: (record: RecordType, index: number) => any; // can be injected when using the component without context resource?: string; diff --git a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx index bdd22c7da91..7ffe3810373 100644 --- a/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx +++ b/packages/ra-ui-materialui/src/list/datagrid/Datagrid.tsx @@ -52,6 +52,7 @@ const defaultBulkActionButtons = ; * - isRowSelectable * - optimized * - rowClick + * - rowSx * - size * - sx * @@ -349,6 +350,9 @@ export interface DatagridProps optimized?: boolean; rowClick?: string | RowClickFunction | false; rowSx?: (record: RecordType, index: number) => SxProps; + /** + * @deprecated use rowStyle instead + */ rowStyle?: (record: RecordType, index: number) => any; size?: 'medium' | 'small'; // can be injected when using the component without context