Skip to content

Commit 64a9d6c

Browse files
authored
Merge pull request #8640 from marmelab/force-resource-routes
Add ability to override available routes for a `<Resource>`
2 parents e76e3eb + 3a04b59 commit 64a9d6c

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed

docs/Resource.md

+34
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,40 @@ For instance, to change the default representation of "users" records to render
145145
- a function (e.g. `(record) => record.title`) to specify a custom string representation
146146
- a React component (e.g. `<MyCustomRecordRepresentation />`). In such components, use [`useRecordContext`](./useRecordContext.md) to access the record.
147147

148+
## `hasCreate`, `hasEdit`, `hasShow`
149+
150+
Some components, like [`<CreateDialog>`](./CreateDialog.md), [`<EditDialog>`](./EditDialog.md) or [`<ShowDialog>`](./ShowDialog.md) need to declare the CRUD components outside of the `<Resource>` 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.
151+
152+
This is useful, for instance, to have the `<ReferenceField>` component display a link to the edit or show view of the referenced record.
153+
154+
```jsx
155+
// in src/App.js
156+
import { Admin, Resource } from 'react-admin';
157+
import { dataProvider } from './dataProvider';
158+
159+
import { PostList } from './posts';
160+
import { CommentEdit } from './commentEdit';
161+
162+
const App = () => (
163+
<Admin dataProvider={dataProvider}>
164+
<Resource name="posts" list={PostList} hasEdit />
165+
<Resource name="comment" edit={CommentEdit} />
166+
</Admin>
167+
);
168+
169+
// in src/commentEdit.js
170+
import { Edit, SimpleForm, ReferenceField } from 'react-admin';
171+
172+
const CommentEdit = () => (
173+
<Edit>
174+
<SimpleForm>
175+
{/* renders a link to the edit view only because `hasEdit` has been set on `<Resource>` */}
176+
<ReferenceField source="post_id" reference="posts" />
177+
</SimpleForm>
178+
</Edit>
179+
);
180+
```
181+
148182
## Resource Context
149183

150184
`<Resource>` also creates a `ResourceContext`, that gives access to the current resource name to all descendants of the main page components (`list`, `create`, `edit`, `show`).

packages/ra-core/src/core/Resource.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,16 @@ Resource.registerResource = ({
5252
options,
5353
show,
5454
recordRepresentation,
55+
hasCreate,
56+
hasEdit,
57+
hasShow,
5558
}: ResourceProps) => ({
5659
name,
5760
options,
5861
hasList: !!list,
59-
hasCreate: !!create,
60-
hasEdit: !!edit,
61-
hasShow: !!show,
62+
hasCreate: !!create || !!hasCreate,
63+
hasEdit: !!edit || !!hasEdit,
64+
hasShow: !!show || !!hasShow,
6265
icon,
6366
recordRepresentation,
6467
});

packages/ra-core/src/core/useConfigureAdminRouterFromChildren.spec.tsx

+41
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,30 @@ const TestedComponentWithOnlyLazyCustomRoutes = ({ history }) => {
219219
);
220220
};
221221

222+
const TestedComponentWithForcedRoutes = () => {
223+
const history = createMemoryHistory();
224+
225+
return (
226+
<CoreAdminContext history={history}>
227+
<CoreAdminRoutes
228+
layout={MyLayout}
229+
catchAll={CatchAll}
230+
loading={Loading}
231+
>
232+
<Resource
233+
name="posts"
234+
list={<div />}
235+
hasCreate
236+
hasEdit
237+
hasShow
238+
/>
239+
<Resource name="comments" list={<div />} />
240+
{() => [<Resource name="user" list={<div />} hasEdit />]}
241+
</CoreAdminRoutes>
242+
</CoreAdminContext>
243+
);
244+
};
245+
222246
const expectResource = (resource: string) =>
223247
expect(screen.queryByText(`"name":"${resource}"`, { exact: false }));
224248

@@ -324,4 +348,21 @@ describe('useConfigureAdminRouterFromChildren', () => {
324348
history.push('/foo');
325349
expect(screen.queryByText('Foo')).not.toBeNull();
326350
});
351+
it('should support forcing hasEdit hasCreate or hasShow', async () => {
352+
render(<TestedComponentWithForcedRoutes />);
353+
await waitFor(() => expect(screen.queryByText('Loading')).toBeNull());
354+
355+
expectResourceView('posts', 'list').not.toBeNull();
356+
expectResourceView('posts', 'create').not.toBeNull();
357+
expectResourceView('posts', 'edit').not.toBeNull();
358+
expectResourceView('posts', 'show').not.toBeNull();
359+
expectResourceView('comments', 'list').not.toBeNull();
360+
expectResourceView('comments', 'create').toBeNull();
361+
expectResourceView('comments', 'edit').toBeNull();
362+
expectResourceView('comments', 'show').toBeNull();
363+
expectResourceView('user', 'list').not.toBeNull();
364+
expectResourceView('user', 'create').toBeNull();
365+
expectResourceView('user', 'edit').not.toBeNull();
366+
expectResourceView('user', 'show').toBeNull();
367+
});
327368
});

packages/ra-core/src/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,9 @@ export interface ResourceProps {
346346
create?: ComponentType<any> | ReactElement;
347347
edit?: ComponentType<any> | ReactElement;
348348
show?: ComponentType<any> | ReactElement;
349+
hasCreate?: boolean;
350+
hasEdit?: boolean;
351+
hasShow?: boolean;
349352
icon?: ComponentType<any>;
350353
recordRepresentation?: ReactElement | RecordToStringFunction | string;
351354
options?: ResourceOptions;

0 commit comments

Comments
 (0)