From d51a6f636997f897512dcccb6bb80d3e12a78c53 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Tue, 11 Jul 2023 18:10:43 +0200 Subject: [PATCH 1/5] Add RecordRepresentation component --- docs/RecordRepresentation.md | 65 ++++++++++ docs/Reference.md | 1 + .../record/RecordRepresentation.spec.tsx | 37 ++++++ .../record/RecordRepresentation.stories.tsx | 117 ++++++++++++++++++ .../record/RecordRepresentation.tsx | 21 ++++ .../ra-core/src/controller/record/index.ts | 1 + 6 files changed, 242 insertions(+) create mode 100644 docs/RecordRepresentation.md create mode 100644 packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx create mode 100644 packages/ra-core/src/controller/record/RecordRepresentation.stories.tsx create mode 100644 packages/ra-core/src/controller/record/RecordRepresentation.tsx diff --git a/docs/RecordRepresentation.md b/docs/RecordRepresentation.md new file mode 100644 index 00000000000..04f8c5ffacb --- /dev/null +++ b/docs/RecordRepresentation.md @@ -0,0 +1,65 @@ +--- +layout: default +title: "The RecordRepresentation Component" +--- + +Render the record representation, leveraging the [`recordRepresentation`](./Resource.md#recordrepresentation) prop of the parent `` component. + +## Usage + +```tsx +// in src/posts/PostBreadcrumbs.tsx +import * as React from 'react'; +import { Breadcrumbs, Typography } from '@mui/material'; +import { Link, RecordRepresentation } from 'react-admin'; + +export const PostBreadcrumbs = () => { + return ( +
+ + + Home + + + Posts + + + + + +
+ ); +} + +// in src/posts/PostEdit.tsx +import { Edit, SimpleForm, TextInput } from 'react-admin'; +import { PostBreadcrumbs } from './PostBreadcrumbs'; + +const PostEdit = () => ( + <> + + + + + + + +) +``` + +## Props + +Here are all the props you can set on the `` component: + +| Prop | Required | Type | Default | Description | +| ---------- | -------- | ---------- | ------------------------------------------ | ----------------------| +| `record` | Optional | `RaRecord` | Record from the parent `RecordContext` | The record to display | +| `resource` | Optional | `string` | Resource from the parent `ResourceContext` | The record's resource | + +## `record` + +The record to display. Defaults to the record from the parent `RecordContext`. + +## `resource` + +The record's resource. Defaults to the resource from the parent `ResourceContext`. diff --git a/docs/Reference.md b/docs/Reference.md index 3378dfa8be8..b8d2c90496b 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -127,6 +127,7 @@ title: "Index" **- R -** * [``](./RadioButtonGroupInput.md) +* [``](./RecordRepresentation.md) * [``](./ReferenceArrayField.md) * [``](./ReferenceArrayInput.md) * [``](./ReferenceField.md) diff --git a/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx b/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx new file mode 100644 index 00000000000..6c7f42692c7 --- /dev/null +++ b/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { + ComponentRecordRepresentation, + FunctionRecordRepresentation, + NoRecordRepresentation, + StringRecordRepresentation, +} from './RecordRepresentation.stories'; + +describe('RecordRepresentation', () => { + it('should render the record id when not provided on its parent ', async () => { + render(); + await screen.findByText('#1'); + }); + it('should render the record id when provided as a field name on its parent ', async () => { + render(); + await screen.findByText("The Hitchhiker's Guide to the Galaxy"); + }); + it('should render the record id when provided as a function on its parent ', async () => { + render(); + await screen.findByText( + "The Hitchhiker's Guide to the Galaxy by Douglas Adams" + ); + }); + it('should render the record id when provided as a component on its parent ', async () => { + render(); + await screen.findByText( + (content, element) => { + return ( + element?.textContent === + "The Hitchhiker's Guide to the Galaxy (by Douglas Adams) - 1979" + ); + }, + { selector: 'p' } + ); + }); +}); diff --git a/packages/ra-core/src/controller/record/RecordRepresentation.stories.tsx b/packages/ra-core/src/controller/record/RecordRepresentation.stories.tsx new file mode 100644 index 00000000000..9ecbdac95de --- /dev/null +++ b/packages/ra-core/src/controller/record/RecordRepresentation.stories.tsx @@ -0,0 +1,117 @@ +import * as React from 'react'; +import { + ResourceContextProvider, + ResourceDefinitionContextProvider, +} from '../../core'; +import { RecordContextProvider } from './RecordContext'; +import { RecordRepresentation } from './RecordRepresentation'; +import { useRecordContext } from './useRecordContext'; +export default { + title: 'ra-core/controller/record/RecordRepresentation', +}; + +const Book = { + id: 1, + title: "The Hitchhiker's Guide to the Galaxy", + author: 'Douglas Adams', + publishedAt: '1979-10-12', +}; + +export const NoRecordRepresentation = () => ( + + + + + + + +); + +export const StringRecordRepresentation = () => ( + + + + + + + +); + +export const FunctionRecordRepresentation = () => ( + + + `${record.title} by ${record.author}`, + }, + }} + > + + + + + +); + +const BookRepresentation = () => { + const record = useRecordContext(); + + if (!record) return null; + + return ( +

+ {record.title}{' '} + + (by {record.author}) -{' '} + {new Date(record.publishedAt).getFullYear()} + +

+ ); +}; +export const ComponentRecordRepresentation = () => ( + + , + }, + }} + > + + + + + +); diff --git a/packages/ra-core/src/controller/record/RecordRepresentation.tsx b/packages/ra-core/src/controller/record/RecordRepresentation.tsx new file mode 100644 index 00000000000..850888dabb8 --- /dev/null +++ b/packages/ra-core/src/controller/record/RecordRepresentation.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { useGetRecordRepresentation, useResourceContext } from '../../core'; +import { RaRecord } from '../../types'; +import { useRecordContext } from './useRecordContext'; + +/** + * Render the record representation as specified on its parent . + * @param props The component props + * @param {string} props.resource The resource name + * @param {RaRecord} props.record The record to render + */ +export const RecordRepresentation = (props: { + record?: RaRecord; + resource?: string; +}) => { + const record = useRecordContext(props); + const resource = useResourceContext(props); + const getRecordRepresentation = useGetRecordRepresentation(resource); + + return <>{getRecordRepresentation(record)}; +}; diff --git a/packages/ra-core/src/controller/record/index.ts b/packages/ra-core/src/controller/record/index.ts index 3acfc956e41..b7eba2ef0ec 100644 --- a/packages/ra-core/src/controller/record/index.ts +++ b/packages/ra-core/src/controller/record/index.ts @@ -2,3 +2,4 @@ export * from './RecordContext'; export * from './useRecordContext'; export * from './WithRecord'; export * from './OptionalRecordContextProvider'; +export * from './RecordRepresentation'; From 154a579ee54bf125ff7d7b67b8e7e9097d27af07 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:35:01 +0200 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Jean-Baptiste Kaiser --- .../src/controller/record/RecordRepresentation.spec.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx b/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx index 6c7f42692c7..6cf25f50f8f 100644 --- a/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx +++ b/packages/ra-core/src/controller/record/RecordRepresentation.spec.tsx @@ -12,17 +12,17 @@ describe('RecordRepresentation', () => { render(); await screen.findByText('#1'); }); - it('should render the record id when provided as a field name on its parent ', async () => { + it('should render the record representation when provided as a field name on its parent ', async () => { render(); await screen.findByText("The Hitchhiker's Guide to the Galaxy"); }); - it('should render the record id when provided as a function on its parent ', async () => { + it('should render the record representation when provided as a function on its parent ', async () => { render(); await screen.findByText( "The Hitchhiker's Guide to the Galaxy by Douglas Adams" ); }); - it('should render the record id when provided as a component on its parent ', async () => { + it('should render the record representation when provided as a component on its parent ', async () => { render(); await screen.findByText( (content, element) => { From f7a88de8a2c69816f679235f2db2b893d50c9836 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:50:16 +0200 Subject: [PATCH 3/5] Fix documentation --- docs/RecordRepresentation.md | 30 +++++++------- docs/Resource.md | 2 + docs/navigation.html | 2 + docs/useGetRecordRepresentation.md | 65 ++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 docs/useGetRecordRepresentation.md diff --git a/docs/RecordRepresentation.md b/docs/RecordRepresentation.md index 04f8c5ffacb..1ad6715994f 100644 --- a/docs/RecordRepresentation.md +++ b/docs/RecordRepresentation.md @@ -5,6 +5,8 @@ title: "The RecordRepresentation Component" Render the record representation, leveraging the [`recordRepresentation`](./Resource.md#recordrepresentation) prop of the parent `` component. +You can also uses its hook version: [``](./useGetRecordRepresentation.md). + ## Usage ```tsx @@ -17,33 +19,33 @@ export const PostBreadcrumbs = () => { return (
- - Home - - - Posts - - - - + + Home + + + Posts + + + +
); } // in src/posts/PostEdit.tsx -import { Edit, SimpleForm, TextInput } from 'react-admin'; +import { EditBase, EditView, SimpleForm, TextInput } from 'react-admin'; import { PostBreadcrumbs } from './PostBreadcrumbs'; const PostEdit = () => ( - <> + - + - - + + ) ``` diff --git a/docs/Resource.md b/docs/Resource.md index e9ad2db5c86..b72456d3f5c 100644 --- a/docs/Resource.md +++ b/docs/Resource.md @@ -226,6 +226,8 @@ For instance, to change the default representation of "users" records to render - a function (e.g. `(record) => record.title`) to specify a custom string representation - a React component (e.g. ``). In such components, use [`useRecordContext`](./useRecordContext.md) to access the record. +If you want to display this record representation somewhere, you can leverage the [`useGetRecordRepresentation`](./useGetRecordRepresentation.md) hook or the [``](./RecordRepresentation.md) component. + ## `hasCreate`, `hasEdit`, `hasShow` Some components, like [``](./CreateDialog.md), [``](./EditDialog.md) or [``](./ShowDialog.md) need to declare the CRUD components outside of the `` component. In such cases, you can use the `hasCreate`, `hasEdit` and `hasShow` props to tell react-admin which CRUD components are available for a given resource. diff --git a/docs/navigation.html b/docs/navigation.html index 8ff5e4af250..1211d870d0e 100644 --- a/docs/navigation.html +++ b/docs/navigation.html @@ -243,6 +243,8 @@
  • <Confirm>
  • Buttons
  • <UpdateButton>
  • +
  • <RecordRepresentation>
  • +
  • useGetRecordRepresentation
    • Realtime
      diff --git a/docs/useGetRecordRepresentation.md b/docs/useGetRecordRepresentation.md new file mode 100644 index 00000000000..3dfbe40e71c --- /dev/null +++ b/docs/useGetRecordRepresentation.md @@ -0,0 +1,65 @@ +--- +layout: default +title: "The useGetRecordRepresentation Component" +--- + +Returns a function that get the record representation, leveraging the [`recordRepresentation`](./Resource.md#recordrepresentation) prop of the parent `` component. + +You can also uses its component version: [``](./RecordRepresentation.md). + +## Usage + +```tsx +// in src/posts/PostBreadcrumbs.tsx +import * as React from 'react'; +import { Breadcrumbs, Typography } from '@mui/material'; +import { Link, useGetRecordRepresentation, useRecordContext, useResourceContext } from 'react-admin'; + +export const PostBreadcrumbs = () => { + const record = useRecordContext(props); + const resource = useResourceContext(props); + const getRecordRepresentation = useGetRecordRepresentation(resource); + return ( +
      + + + Home + + + Posts + + + {getRecordRepresentation(record)} + + +
      + ); +} + +// in src/posts/PostEdit.tsx +import { EditBase, EditView, SimpleForm, TextInput } from 'react-admin'; +import { PostBreadcrumbs } from './PostBreadcrumbs'; + +const PostEdit = () => ( + + + + + + + + +) +``` + +## Options + +Here are all the options you can set on the `useGetRecordRepresentation` hook: + +| Prop | Required | Type | Default | Description | +| ---------- | -------- | ---------- | ------- | ----------------------| +| `resource` | Required | `string` | | The record's resource | + +## `resource` + +The record's resource. From 2d338a1f94c6da357b7ea29b6f5fc88ef4a1e6d9 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:06:35 +0200 Subject: [PATCH 4/5] Update docs/useGetRecordRepresentation.md Co-authored-by: Jean-Baptiste Kaiser --- docs/useGetRecordRepresentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/useGetRecordRepresentation.md b/docs/useGetRecordRepresentation.md index 3dfbe40e71c..70009a89637 100644 --- a/docs/useGetRecordRepresentation.md +++ b/docs/useGetRecordRepresentation.md @@ -16,8 +16,8 @@ import { Breadcrumbs, Typography } from '@mui/material'; import { Link, useGetRecordRepresentation, useRecordContext, useResourceContext } from 'react-admin'; export const PostBreadcrumbs = () => { - const record = useRecordContext(props); - const resource = useResourceContext(props); + const record = useRecordContext(); + const resource = useResourceContext(); const getRecordRepresentation = useGetRecordRepresentation(resource); return (
      From 9dd4c4152dbf8fd1cebeb37bd3d5bc3727f719a0 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:13:40 +0200 Subject: [PATCH 5/5] Fix Reference documentation --- docs/Reference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Reference.md b/docs/Reference.md index b8d2c90496b..d1bea9f7b88 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -276,6 +276,7 @@ title: "Index" **- R -** * [`useRecordContext`](./useRecordContext.md) +* [`useRecordRepresentation`](./useRecordRepresentation.md) * [`useRedirect`](./useRedirect.md) * [`useReference`](./useGetOne.md#aggregating-getone-calls) * [`useRefresh`](./useRefresh.md)