diff --git a/docs/Resource.md b/docs/Resource.md index 7ceeff9b047..1ed0cc7bc25 100644 --- a/docs/Resource.md +++ b/docs/Resource.md @@ -145,6 +145,40 @@ 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. +## `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. + +This is useful, for instance, to have the `` component display a link to the edit or show view of the referenced record. + +```jsx +// in src/App.js +import { Admin, Resource } from 'react-admin'; +import { dataProvider } from './dataProvider'; + +import { PostList } from './posts'; +import { CommentEdit } from './commentEdit'; + +const App = () => ( + + + + +); + +// in src/commentEdit.js +import { Edit, SimpleForm, ReferenceField } from 'react-admin'; + +const CommentEdit = () => ( + + + {/* renders a link to the edit view only because `hasEdit` has been set on `` */} + + + +); +``` + ## Resource Context `` also creates a `ResourceContext`, that gives access to the current resource name to all descendants of the main page components (`list`, `create`, `edit`, `show`). diff --git a/packages/ra-core/src/core/Resource.tsx b/packages/ra-core/src/core/Resource.tsx index 8ba5ea27a71..3aae32cb33e 100644 --- a/packages/ra-core/src/core/Resource.tsx +++ b/packages/ra-core/src/core/Resource.tsx @@ -52,13 +52,16 @@ Resource.registerResource = ({ options, show, recordRepresentation, + hasCreate, + hasEdit, + hasShow, }: ResourceProps) => ({ name, options, hasList: !!list, - hasCreate: !!create, - hasEdit: !!edit, - hasShow: !!show, + hasCreate: !!create || !!hasCreate, + hasEdit: !!edit || !!hasEdit, + hasShow: !!show || !!hasShow, icon, recordRepresentation, }); diff --git a/packages/ra-core/src/core/useConfigureAdminRouterFromChildren.spec.tsx b/packages/ra-core/src/core/useConfigureAdminRouterFromChildren.spec.tsx index 61e2572f9df..c044598d58f 100644 --- a/packages/ra-core/src/core/useConfigureAdminRouterFromChildren.spec.tsx +++ b/packages/ra-core/src/core/useConfigureAdminRouterFromChildren.spec.tsx @@ -219,6 +219,30 @@ const TestedComponentWithOnlyLazyCustomRoutes = ({ history }) => { ); }; +const TestedComponentWithForcedRoutes = () => { + const history = createMemoryHistory(); + + return ( + + + } + hasCreate + hasEdit + hasShow + /> + } /> + {() => [} hasEdit />]} + + + ); +}; + const expectResource = (resource: string) => expect(screen.queryByText(`"name":"${resource}"`, { exact: false })); @@ -324,4 +348,21 @@ describe('useConfigureAdminRouterFromChildren', () => { history.push('/foo'); expect(screen.queryByText('Foo')).not.toBeNull(); }); + it('should support forcing hasEdit hasCreate or hasShow', async () => { + render(); + await waitFor(() => expect(screen.queryByText('Loading')).toBeNull()); + + expectResourceView('posts', 'list').not.toBeNull(); + expectResourceView('posts', 'create').not.toBeNull(); + expectResourceView('posts', 'edit').not.toBeNull(); + expectResourceView('posts', 'show').not.toBeNull(); + expectResourceView('comments', 'list').not.toBeNull(); + expectResourceView('comments', 'create').toBeNull(); + expectResourceView('comments', 'edit').toBeNull(); + expectResourceView('comments', 'show').toBeNull(); + expectResourceView('user', 'list').not.toBeNull(); + expectResourceView('user', 'create').toBeNull(); + expectResourceView('user', 'edit').not.toBeNull(); + expectResourceView('user', 'show').toBeNull(); + }); }); diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index 53ac89c2160..be2bcbe9dae 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -346,6 +346,9 @@ export interface ResourceProps { create?: ComponentType | ReactElement; edit?: ComponentType | ReactElement; show?: ComponentType | ReactElement; + hasCreate?: boolean; + hasEdit?: boolean; + hasShow?: boolean; icon?: ComponentType; recordRepresentation?: ReactElement | RecordToStringFunction | string; options?: ResourceOptions;