From 11288a58e631c81337c3deb31f8e2b51151794ff Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Wed, 22 Nov 2023 11:38:04 +0100 Subject: [PATCH 1/4] change defaultRowClick --- .../src/list/datagrid/DatagridRow.spec.tsx | 126 ++++++++++++++++-- .../src/list/datagrid/DatagridRow.tsx | 10 +- 2 files changed, 127 insertions(+), 9 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx index 684c7bc1067..660b49e10d4 100644 --- a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx +++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx @@ -5,11 +5,12 @@ import { screen, } from '@testing-library/react'; import { useLocation } from 'react-router-dom'; -import { useRecordContext } from 'ra-core'; +import { ResourceDefinitionContextProvider, useRecordContext } from 'ra-core'; import { AdminContext } from '../../AdminContext'; import DatagridRow from './DatagridRow'; import DatagridContextProvider from './DatagridContextProvider'; +import { createMemoryHistory } from 'history'; const TitleField = (): JSX.Element => { const record = useRecordContext(); @@ -21,13 +22,16 @@ const ExpandPanel = () => expanded; // remove validateDomNesting warnings by react-testing-library const render = element => baseRender(element, { - wrapper: ({ children }) => ( - - - {children} -
-
- ), + wrapper: ({ children }) => { + const history = createMemoryHistory(); + return ( + + + {children} +
+
+ ); + }, }); describe('', () => { @@ -194,6 +198,21 @@ describe('', () => { ); }); + it('should not call push if onRowClick is false', () => { + let spy = jest.fn(); + render( + + + + + + ); + fireEvent.click(screen.getByText('hello')); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/' }) + ); + }); + it('should not call push if onRowClick is falsy', () => { let spy = jest.fn(); render( @@ -208,5 +227,96 @@ describe('', () => { expect.objectContaining({ pathname: '/' }) ); }); + + it("should default to 'edit' if the resource has an edit page", () => { + let spy = jest.fn(); + render( + + + + + + + + ); + fireEvent.click(screen.getByText('hello')); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/posts/15' }) + ); + }); + + it("should default to 'show' if the resource has a show page", () => { + let spy = jest.fn(); + render( + + + + + + + + ); + fireEvent.click(screen.getByText('hello')); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/posts/15/show' }) + ); + }); + + it("should default to 'show' if the resource has both a show and an edit page", () => { + let spy = jest.fn(); + render( + + + + + + + + ); + fireEvent.click(screen.getByText('hello')); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/posts/15/show' }) + ); + }); + + it('should default to false if the resource has no show nor edit page', () => { + let spy = jest.fn(); + render( + + + + + + + + ); + fireEvent.click(screen.getByText('hello')); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/' }) + ); + }); }); }); diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx index 90e859c30bb..81b9b750370 100644 --- a/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx +++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx @@ -23,6 +23,7 @@ import { useTranslate, useCreatePath, useRecordContext, + useResourceDefinition, } from 'ra-core'; import { useNavigate } from 'react-router-dom'; @@ -39,6 +40,13 @@ const computeNbColumns = (expand, children, hasBulkActions) => : 0; // we don't need to compute columns if there is no expand panel; const DatagridRow: FC = React.forwardRef((props, ref) => { + const definition = useResourceDefinition(props); + const defaultRowClick = definition?.hasShow + ? 'show' + : definition?.hasEdit + ? 'edit' + : false; + const { children, className, @@ -48,7 +56,7 @@ const DatagridRow: FC = React.forwardRef((props, ref) => { id, onToggleItem, record: recordOverride, - rowClick, + rowClick = defaultRowClick, selected = false, style, selectable = true, From e9ad7e41a1c18778bc573ccc1c32f934e0439ea4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Wed, 22 Nov 2023 12:04:34 +0100 Subject: [PATCH 2/4] update doc --- docs/Breadcrumb.md | 2 +- docs/CustomRoutes.md | 2 +- docs/Datagrid.md | 16 +++++++++------- docs/Features.md | 2 +- docs/List.md | 2 +- docs/ListTutorial.md | 2 +- docs/RealtimeDataProvider.md | 2 +- docs/Resource.md | 4 ++-- docs/Tutorial.md | 34 +++++++++++----------------------- 9 files changed, 28 insertions(+), 38 deletions(-) diff --git a/docs/Breadcrumb.md b/docs/Breadcrumb.md index 39170a77707..2827642c8f0 100644 --- a/docs/Breadcrumb.md +++ b/docs/Breadcrumb.md @@ -763,7 +763,7 @@ export const SongList = () => { useDefineAppLocation('music.songs'); return ( - + diff --git a/docs/CustomRoutes.md b/docs/CustomRoutes.md index 9197f82a2b6..645093dfed5 100644 --- a/docs/CustomRoutes.md +++ b/docs/CustomRoutes.md @@ -259,7 +259,7 @@ const BookList = () => { const { authorId } = useParams(); return ( - + diff --git a/docs/Datagrid.md b/docs/Datagrid.md index 247f98b5683..4d49985ef94 100644 --- a/docs/Datagrid.md +++ b/docs/Datagrid.md @@ -61,7 +61,7 @@ Both are [Enterprise Edition](https://marmelab.com/ra-enterprise) components. | `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. | +| `rowClick` | Optional | mixed | `'show'` or `'edit'` | 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. | @@ -409,7 +409,7 @@ const FullNameField = () => { export const UserList = () => ( - + @@ -676,7 +676,9 @@ const PostList = () => ( ## `rowClick` -You can catch clicks on rows to redirect to the show or edit view by setting the `rowClick` prop: +By default, `` will look at the current [resource definition](https://marmelab.com/react-admin/Resource.html) to determine what to do when the user clicks on a row. If the resource has a `show` page, it will redirect to the Show view. If the resource has an `edit` page, it will redirect to the Edit view. Otherwise, the row will not be clickable. + +You can choose what happens when the user clicks on a row by setting the `rowClick` prop. For instance, set the `rowClick` prop to `"edit"` to redirect to the Edit view: ```tsx import { List, Datagrid } from 'react-admin'; @@ -692,10 +694,10 @@ export const PostList = () => ( `rowClick` accepts the following values: -* "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 +* `"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 diff --git a/docs/Features.md b/docs/Features.md index d4e22da4e8c..238ae3a5f07 100644 --- a/docs/Features.md +++ b/docs/Features.md @@ -214,7 +214,7 @@ const BookList = () => ( , ]}> - + diff --git a/docs/List.md b/docs/List.md index 855abcbd21f..933175fc792 100644 --- a/docs/List.md +++ b/docs/List.md @@ -300,7 +300,7 @@ export const PostList = () => { tertiaryText={record => new Date(record.published_at).toLocaleDateString()} /> ) : ( - + diff --git a/docs/ListTutorial.md b/docs/ListTutorial.md index a59b81c369e..77b8c77f740 100644 --- a/docs/ListTutorial.md +++ b/docs/ListTutorial.md @@ -541,7 +541,7 @@ export const PostList = () => { tertiaryText={record => new Date(record.published_at).toLocaleDateString()} /> ) : ( - + diff --git a/docs/RealtimeDataProvider.md b/docs/RealtimeDataProvider.md index 70f514d72b4..00b73d8281e 100644 --- a/docs/RealtimeDataProvider.md +++ b/docs/RealtimeDataProvider.md @@ -66,7 +66,7 @@ export const App = () => ( const SaleList = () => ( - + diff --git a/docs/Resource.md b/docs/Resource.md index 2fc1e42ef0e..fbfa3768983 100644 --- a/docs/Resource.md +++ b/docs/Resource.md @@ -130,7 +130,7 @@ export const BookList = () => { const { authorId } = useParams(); return ( - + @@ -347,7 +347,7 @@ export const SongList = () => { const { id } = useParams(); return ( - + diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 203050484ab..e0f9e14c75c 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -166,7 +166,7 @@ import { List, Datagrid, TextField, EmailField } from "react-admin"; export const UserList = () => ( - + @@ -208,7 +208,7 @@ Let's take a moment to analyze the code of the `` component: ```tsx export const UserList = () => ( - + @@ -290,7 +290,7 @@ export const UserList = () => { tertiaryText={(record) => record.email} /> ) : ( - + @@ -325,7 +325,7 @@ Let's get back to ``. It reads the data fetched by ``, then rend ```diff // in src/users.tsx - + - @@ -352,7 +352,7 @@ For instance, the `website` field looks like a URL. Instead of displaying it as -import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; +import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin"; // ... - + @@ -396,7 +396,7 @@ You can use the `` component in ``, instead of react-admin +import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; +import MyUrlField from './MyUrlField'; // ... - + @@ -484,7 +484,7 @@ import { List, Datagrid, TextField, ReferenceField } from "react-admin"; export const PostList = () => ( - + @@ -532,7 +532,7 @@ The `` component fetches the reference data, creates a `RecordCo **Tip**: Look at the network tab of your browser again: react-admin deduplicates requests for users, and aggregates them in order to make only *one* HTTP request to the `/users` endpoint for the whole Datagrid. That's one of many optimizations that keep the UI fast and responsive. -To finish the post list, place the post `id` field as first column, and remove the `body` field. From a UX point of view, fields containing large chunks of text should not appear in a Datagrid, only in detail views. Also, to make the Edit action stand out, let's replace the `rowClick` action by an explicit action button: +To finish the post list, place the post `id` field as first column, and remove the `body` field. From a UX point of view, fields containing large chunks of text should not appear in a Datagrid, only in detail views. Also, to make the Edit action stand out, let's replace the default `rowClick` action by an explicit action button: ```diff // in src/posts.tsx @@ -541,8 +541,8 @@ To finish the post list, place the post `id` field as first column, and remove t export const PostList = () => ( -- -+ +- ++ + - @@ -577,18 +577,6 @@ export const App = () => ( ); ``` -You will need to modify the user list view so that a click on a datagrid row links to the show view: - -```diff -// in src/users.tsx -export const UserList = () => { - // ... -- -+ - // ... -}; -``` - Now you can click on a user in the list to see its details: