|
1 |
| -# Customizing the Admin |
| 1 | +# Customizing the Guessers |
2 | 2 |
|
3 |
| -Customizing API Platform Admin is easy and idiomatic. The tool gives you the ability to customize everything, from the list of resource types that must be administrable to every single input or button. |
| 3 | +Using `<HydraAdmin>` or `<OpenApiAdmin>` directly is a great way to quickly get started with API Platform Admin. They will introspect your API's schema (using `@api-platform/api-doc-parser`) and automatically generate CRUD pages for all the resources it exposes. They will even [configure filtering, sorting, and real-time updates with Mercure](./schema-org.md) if your API supports it. |
4 | 4 |
|
5 |
| -To do so, you can use the React components provided by API Platform Admin itself, [React Admin](https://marmelab.com/react-admin/), [Material UI](https://material-ui.com/), [community libraries](https://github.com/brillout/awesome-react-components), or [write your own](https://reactjs.org/tutorial/tutorial.html). |
| 5 | +For some this may be enough, but you will often find yourself wanting to customize the generated pages further. For instance, you may want to: |
6 | 6 |
|
7 |
| -## Customizing the Admin's Main Page and the Resource List |
| 7 | +- Hide or reorder resources in the menu |
| 8 | +- Hide or reorder columns in the list view |
| 9 | +- Hide or reorder fields in the show, create and edit views |
| 10 | +- Customize the generated list, e.g. add a default sort order |
| 11 | +- Customize the generated create and edit views, e.g. to add a warning when there are unsaved changes |
| 12 | +- Customize the generated inputs, e.g. set a custom label or make a text input multiline |
8 | 13 |
|
9 |
| -By default, API Platform Admin automatically builds a tailored [Resource component](https://marmelab.com/react-admin/Resource.html) |
10 |
| -(and all its appropriate children) for each resource type exposed by a web API. |
11 |
| -Under the hood it uses the `@api-platform/api-doc-parser` library to parse the API documentation. |
12 |
| -The API documentation can use Hydra, OpenAPI and any other format supported by the library. |
13 |
| -Resources are listed in the order they appear in the machine-readable documentation. |
| 14 | +Such changes can't be achieved by modifying the Schema, they require customizing the React components generated by API Platform Admin. |
14 | 15 |
|
15 |
| -However, it's also possible to display only specific resources, and to order them, while still benefiting from all discovery features provided by API Platform Admin. |
16 |
| -To cherry-pick the resources to make available through the admin, pass a list of `<ResourceGuesser>` components as children of the root component: |
| 16 | +Fortunately, API Platform Admin has you covered! |
17 | 17 |
|
18 |
| -```javascript |
19 |
| -import { HydraAdmin, ResourceGuesser } from '@api-platform/admin'; |
| 18 | +## From `<AdminGuesser>` To `<ResourceGuesser>` |
20 | 19 |
|
21 |
| -export default () => ( |
22 |
| - <HydraAdmin entrypoint="https://demo.api-platform.com"> |
23 |
| - <ResourceGuesser name="books" /> |
24 |
| - <ResourceGuesser name="reviews" /> |
| 20 | +If you are using `<HydraAdmin>` or `<OpenApiAdmin>` directly, there is a simple way to start customizing the generated pages. |
25 | 21 |
|
26 |
| - {/* While deprecated resources are hidden by default, using an explicit ResourceGuesser component allows to add them back. */} |
27 |
| - <ResourceGuesser name="parchments" /> |
28 |
| - </HydraAdmin> |
| 22 | +Simply open your browser's developer tools and look at the console. You will see messages like this: |
| 23 | + |
| 24 | +``` |
| 25 | +If you want to override at least one resource, paste this content in the <AdminGuesser> component of your app: |
| 26 | +
|
| 27 | +<ResourceGuesser name="books" /> |
| 28 | +<ResourceGuesser name="greetings" /> |
| 29 | +<ResourceGuesser name="reviews" /> |
| 30 | +``` |
| 31 | + |
| 32 | +This message tells you which resources are exposed by your API and how to customize the generated pages for each of them. |
| 33 | + |
| 34 | +Let's say we'd like to hide the `greetings` resource from the menu. We can do this by replacing the `<AdminGuesser>` component (`<HydraAdmin>` in our case) children with a list of `<ResourceGuesser>`: |
| 35 | + |
| 36 | +```diff |
| 37 | +-import { HydraAdmin } from "@api-platform/admin"; |
| 38 | ++import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; |
| 39 | + |
| 40 | +const App = () => ( |
| 41 | +- <HydraAdmin entrypoint={...} /> |
| 42 | ++ <HydraAdmin entrypoint={...}> |
| 43 | ++ <ResourceGuesser name="books" /> |
| 44 | ++ <ResourceGuesser name="reviews" /> |
| 45 | ++ </HydraAdmin> |
29 | 46 | );
|
30 | 47 | ```
|
31 | 48 |
|
32 |
| -Instead of using the `<ResourceGuesser>` component provided by API Platform Admin, you can also pass custom React Admin's [Resource components](https://marmelab.com/react-admin/Resource.html), or any other React components that are supported by React Admin's [Admin](https://marmelab.com/react-admin/Admin.html). |
| 49 | +Now the `greetings` resource will no longer be displayed in the menu. |
33 | 50 |
|
34 |
| -## Customizing the List View |
| 51 | + |
35 | 52 |
|
36 |
| -The list view can be customized following the same pattern: |
| 53 | +`<ResourceGuesser>` also accepts all props react-admin's [`<Resource>`](https://marmelab.com/react-admin/Resource.html) component accepts. This means that, for instance, you can use the `list` prop to use your own list component, but keep using the create, edit and show components introspected by `<ResourceGuesser>`: |
37 | 54 |
|
38 |
| -```javascript |
39 |
| -import { |
40 |
| - HydraAdmin, |
41 |
| - ResourceGuesser, |
42 |
| - ListGuesser, |
43 |
| - FieldGuesser, |
44 |
| -} from '@api-platform/admin'; |
| 55 | +```diff |
| 56 | +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; |
| 57 | ++import { BookList } from "./BookList"; |
45 | 58 |
|
46 |
| -const ReviewsList = (props) => ( |
47 |
| - <ListGuesser {...props}> |
48 |
| - <FieldGuesser source="author" /> |
49 |
| - <FieldGuesser source="book" /> |
| 59 | +const App = () => ( |
| 60 | + <HydraAdmin entrypoint={...}> |
| 61 | +- <ResourceGuesser name="books" /> |
| 62 | ++ <ResourceGuesser name="books" list={BookList} /> |
| 63 | + <ResourceGuesser name="reviews" /> |
| 64 | + </HydraAdmin> |
| 65 | +); |
| 66 | +``` |
| 67 | + |
| 68 | +## Customizing the `<ListGuesser>` |
| 69 | + |
| 70 | +By default, `<ResourceGuesser>` will render a `<ListGuesser>` component as the list view for a resource. |
| 71 | + |
| 72 | +Again, this component will automatically introspect the API's schema and generate a list view with all the fields of the resource. |
| 73 | + |
| 74 | + |
50 | 75 |
|
51 |
| - {/* While deprecated fields are hidden by default, using an explicit FieldGuesser component allows to add them back. */} |
52 |
| - <FieldGuesser source="letter" /> |
53 |
| - </ListGuesser> |
| 76 | +This is already usable, but may not provide the best user experience yet. |
| 77 | + |
| 78 | +Here, too, to start customizing the list view, you can look at the DevTools console. You will see messages like this: |
| 79 | + |
| 80 | +``` |
| 81 | +If you want to override at least one field, create a BookList component with this content: |
| 82 | +
|
| 83 | +import { ListGuesser, FieldGuesser } from "@api-platform/admin"; |
| 84 | +
|
| 85 | +export const BookList = () => ( |
| 86 | + <ListGuesser> |
| 87 | + <FieldGuesser source="isbn" /> |
| 88 | + <FieldGuesser source="title" /> |
| 89 | + <FieldGuesser source="description" /> |
| 90 | + <FieldGuesser source="author" /> |
| 91 | + <FieldGuesser source="publicationDate" /> |
| 92 | + <FieldGuesser source="reviews" /> |
| 93 | + </ListGuesser> |
54 | 94 | );
|
55 | 95 |
|
56 |
| -export default () => ( |
57 |
| - <HydraAdmin entrypoint="https://demo.api-platform.com"> |
58 |
| - <ResourceGuesser name="reviews" list={ReviewsList} /> |
59 |
| - {/* ... */} |
60 |
| - </HydraAdmin> |
| 96 | +Then, update your main admin component: |
| 97 | +
|
| 98 | +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; |
| 99 | +import { BookList } from './BookList'; |
| 100 | +
|
| 101 | +const App = () => ( |
| 102 | + <HydraAdmin entrypoint={...}> |
| 103 | + <ResourceGuesser name="books" list={BookList} /> |
| 104 | + {/* ... */} |
| 105 | + </HydraAdmin> |
61 | 106 | );
|
62 | 107 | ```
|
63 | 108 |
|
64 |
| -In this example, only the fields `author`, `book` and `letter` (that is hidden by default because it is deprecated) will be displayed. The defined order will be respected. |
| 109 | +If you follow these instructions, you will end up with the same view as before, but now you can start customizing it. |
65 | 110 |
|
66 |
| -In addition to the `<FieldGuesser>` component, [all React Admin Fields components](https://marmelab.com/react-admin/Fields.html) can be passed as children of `<ListGuesser>`. |
| 111 | +For instance, we'll hide the 'Description' column as it takes too much space (we'll reserve that to the show view). And we will also add a default sort order to show the most recent books first. |
67 | 112 |
|
68 |
| -## Customizing the Show View |
| 113 | +Here's how to achieve this: |
69 | 114 |
|
70 |
| -For the show view: |
| 115 | +```diff |
| 116 | +export const BookList = () => ( |
| 117 | +- <ListGuesser> |
| 118 | ++ <ListGuesser sort={{ field: 'publicationDate', order: 'DESC' }}> |
| 119 | + <FieldGuesser source="isbn" /> |
| 120 | + <FieldGuesser source="title" /> |
| 121 | +- <FieldGuesser source="description" /> |
| 122 | + <FieldGuesser source="author" /> |
| 123 | + <FieldGuesser source="publicationDate" /> |
| 124 | + <FieldGuesser source="reviews" /> |
| 125 | + </ListGuesser> |
| 126 | +); |
| 127 | +``` |
71 | 128 |
|
72 |
| -```javascript |
73 |
| -import { |
74 |
| - HydraAdmin, |
75 |
| - ResourceGuesser, |
76 |
| - ShowGuesser, |
77 |
| - FieldGuesser, |
78 |
| -} from '@api-platform/admin'; |
| 129 | +And here is the result: |
79 | 130 |
|
80 |
| -const ReviewsShow = (props) => ( |
81 |
| - <ShowGuesser {...props}> |
82 |
| - <FieldGuesser source="author" /> |
83 |
| - <FieldGuesser source="book" /> |
84 |
| - <FieldGuesser source="rating" /> |
| 131 | + |
85 | 132 |
|
86 |
| - {/* While deprecated fields are hidden by default, using an explicit FieldGuesser component allows to add them back. */} |
87 |
| - <FieldGuesser source="letter" /> |
| 133 | +That's already better isn't it? 🙂 |
88 | 134 |
|
89 |
| - <FieldGuesser source="body" /> |
90 |
| - <FieldGuesser source="publicationDate" /> |
91 |
| - </ShowGuesser> |
92 |
| -); |
| 135 | +## Customizing the `<FieldGuesser>` |
93 | 136 |
|
94 |
| -export default () => ( |
95 |
| - <HydraAdmin entrypoint="https://demo.api-platform.com"> |
96 |
| - <ResourceGuesser name="reviews" show={ReviewsShow} /> |
97 |
| - {/* ... */} |
98 |
| - </HydraAdmin> |
| 137 | +Removing or reordering `<FieldGuesser>` components is not the only thing we can do. We can also customize them. |
| 138 | + |
| 139 | +Indeed, `<FieldGuesser>` will forward additional props to the underlying React Admin [Field component](https://marmelab.com/react-admin/Fields.html). |
| 140 | + |
| 141 | +This means we can use any [common field prop](https://marmelab.com/react-admin/Fields.html#common-field-props) on them. |
| 142 | + |
| 143 | +For instance, let's add a `label` prop to customize the label of the ISBN column to be all uppercase: |
| 144 | + |
| 145 | +```diff |
| 146 | +export const BookList = () => ( |
| 147 | + <ListGuesser sort={{ field: 'publicationDate', order: 'DESC' }}> |
| 148 | +- <FieldGuesser source="isbn" /> |
| 149 | ++ <FieldGuesser source="isbn" label="ISBN" /> |
| 150 | + <FieldGuesser source="title" /> |
| 151 | + <FieldGuesser source="author" /> |
| 152 | + <FieldGuesser source="publicationDate" /> |
| 153 | + <FieldGuesser source="reviews" /> |
| 154 | + </ListGuesser> |
99 | 155 | );
|
100 | 156 | ```
|
101 | 157 |
|
102 |
| -In addition to the `<FieldGuesser>` component, [all React Admin Fields components](https://marmelab.com/react-admin/Fields.html) can be passed as children of `<ShowGuesser>`. |
| 158 | +And here is the result: |
| 159 | + |
| 160 | + |
103 | 161 |
|
104 |
| -## Customizing the Create Form |
| 162 | +## Customizing the `<ShowGuesser>` |
105 | 163 |
|
106 |
| -Again, the same logic applies to forms. Here is how to customize the create form: |
| 164 | +Following the same principles (including looking at the DevTools console) we can customize the show view. |
107 | 165 |
|
108 |
| -```javascript |
| 166 | +In the following example, the show view for the `books` resource was customized to make the label of the `isbn` field uppercase: |
| 167 | + |
| 168 | +```tsx |
109 | 169 | import {
|
110 | 170 | HydraAdmin,
|
111 | 171 | ResourceGuesser,
|
112 |
| - CreateGuesser, |
113 |
| - InputGuesser, |
| 172 | + ShowGuesser, |
| 173 | + FieldGuesser, |
114 | 174 | } from '@api-platform/admin';
|
115 | 175 |
|
116 |
| -const ReviewsCreate = (props) => ( |
117 |
| - <CreateGuesser {...props}> |
118 |
| - <InputGuesser source="author" /> |
119 |
| - <InputGuesser source="book" /> |
120 |
| - <InputGuesser source="rating" /> |
121 |
| - |
122 |
| - {/* While deprecated fields are hidden by default, using an explicit InputGuesser component allows to add them back. */} |
123 |
| - <InputGuesser source="letter" /> |
124 |
| - |
125 |
| - <InputGuesser source="body" /> |
126 |
| - <InputGuesser source="publicationDate" /> |
127 |
| - </CreateGuesser> |
| 176 | +const BookShow = () => ( |
| 177 | + <ShowGuesser> |
| 178 | + <FieldGuesser source="isbn" label="ISBN" /> |
| 179 | + <FieldGuesser source="title" /> |
| 180 | + <FieldGuesser source="description" /> |
| 181 | + <FieldGuesser source="author" /> |
| 182 | + <FieldGuesser source="publicationDate" /> |
| 183 | + <FieldGuesser source="reviews" /> |
| 184 | + </ShowGuesser> |
128 | 185 | );
|
129 | 186 |
|
130 | 187 | export default () => (
|
131 | 188 | <HydraAdmin entrypoint="https://demo.api-platform.com">
|
132 |
| - <ResourceGuesser name="reviews" create={ReviewsCreate} /> |
133 |
| - {/* ... */} |
| 189 | + <ResourceGuesser name="books" show={BookShow} /> |
| 190 | + <ResourceGuesser name="reviews" /> |
134 | 191 | </HydraAdmin>
|
135 | 192 | );
|
136 | 193 | ```
|
137 | 194 |
|
138 |
| -In addition to the `<InputGuesser>` component, [all React Admin Input components](https://marmelab.com/react-admin/Inputs.html) can be passed as children of `<CreateGuesser>`. |
| 195 | +Here is the result: |
139 | 196 |
|
140 |
| -For instance, using an autocomplete input is straightforward, [check out the dedicated documentation entry](handling-relations.md#using-an-autocomplete-input-for-relations)! |
| 197 | + |
141 | 198 |
|
142 |
| -## Customizing the Edit Form |
| 199 | +## Customizing the `<EditGuesser>` and `<CreateGuesser>` |
143 | 200 |
|
144 |
| -Finally, you can customize the edit form the same way: |
| 201 | +Customizing the `<EditGuesser>` and `<CreateGuesser>` is very similar to customizing the `<ShowGuesser>`. |
145 | 202 |
|
146 |
| -```javascript |
147 |
| -import { |
148 |
| - HydraAdmin, |
149 |
| - ResourceGuesser, |
150 |
| - EditGuesser, |
151 |
| - InputGuesser, |
152 |
| -} from '@api-platform/admin'; |
| 203 | +Again, we can start by looking at the DevTools console to get the initial code of the components. |
153 | 204 |
|
154 |
| -const ReviewsEdit = (props) => ( |
155 |
| - <EditGuesser {...props}> |
156 |
| - <InputGuesser source="author" /> |
157 |
| - <InputGuesser source="book" /> |
158 |
| - <InputGuesser source="rating" /> |
| 205 | +``` |
| 206 | +If you want to override at least one input, create a ReviewEdit component with this content: |
| 207 | +
|
| 208 | +import { EditGuesser, InputGuesser } from "@api-platform/admin"; |
| 209 | +
|
| 210 | +export const ReviewEdit = () => ( |
| 211 | + <EditGuesser> |
| 212 | + <InputGuesser source="rating" /> |
| 213 | + <InputGuesser source="body" /> |
| 214 | + <InputGuesser source="author" /> |
| 215 | + <InputGuesser source="publicationDate" /> |
| 216 | + <InputGuesser source="book" /> |
| 217 | + </EditGuesser> |
| 218 | +); |
| 219 | +
|
| 220 | +Then, update your main admin component: |
159 | 221 |
|
160 |
| - {/* While deprecated fields are hidden by default, using an explicit InputGuesser component allows to add them back. */} |
161 |
| - <InputGuesser source="letter" /> |
| 222 | +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; |
| 223 | +import { ReviewEdit } from './ReviewEdit'; |
162 | 224 |
|
163 |
| - <InputGuesser source="body" /> |
164 |
| - <InputGuesser source="publicationDate" /> |
165 |
| - </EditGuesser> |
| 225 | +const App = () => ( |
| 226 | + <HydraAdmin entrypoint={...}> |
| 227 | + <ResourceGuesser name="reviews" edit={ReviewEdit} /> |
| 228 | + {/* ... */} |
| 229 | + </HydraAdmin> |
166 | 230 | );
|
| 231 | +``` |
167 | 232 |
|
168 |
| -export default () => ( |
169 |
| - <HydraAdmin entrypoint="https://demo.api-platform.com"> |
170 |
| - <ResourceGuesser edit={ReviewsEdit} /> |
171 |
| - {/* ... */} |
172 |
| - </HydraAdmin> |
| 233 | +Let's customize this `ReviewEdit` component to: |
| 234 | + |
| 235 | +- reorder the inputs |
| 236 | +- make the `body` input multiline |
| 237 | +- mark the `publicationDate` input as read-only |
| 238 | + |
| 239 | + |
| 240 | +```diff |
| 241 | +export const ReviewEdit = () => ( |
| 242 | + <EditGuesser> |
| 243 | +- <InputGuesser source="rating" /> |
| 244 | +- <InputGuesser source="body" /> |
| 245 | +- <InputGuesser source="author" /> |
| 246 | +- <InputGuesser source="publicationDate" /> |
| 247 | +- <InputGuesser source="book" /> |
| 248 | ++ <InputGuesser source="author" /> |
| 249 | ++ <InputGuesser source="book" /> |
| 250 | ++ <InputGuesser source="body" multiline /> |
| 251 | ++ <InputGuesser source="rating" /> |
| 252 | ++ <InputGuesser source="publicationDate" readOnly /> |
| 253 | + </EditGuesser> |
173 | 254 | );
|
174 | 255 | ```
|
175 | 256 |
|
176 |
| -In addition to the `<InputGuesser>` component, [all React Admin Input components](https://marmelab.com/react-admin/Inputs.html) can be passed as children of `<EditGuesser>`. |
| 257 | +Here is the result: |
177 | 258 |
|
178 |
| -For instance, using an autocomplete input is straightforward, [checkout the dedicated documentation entry](handling-relations.md#using-an-autocomplete-input-for-relations)! |
| 259 | + |
| 260 | + |
| 261 | +**Tip:** Here, we leveraged the `multiline` and `readOnly` props of the `<InputGuesser>` component. But you can use any [common input prop](https://marmelab.com/react-admin/Inputs.html#common-input-props) supported by react-admin [Inputs](https://marmelab.com/react-admin/Inputs.html) on them. |
179 | 262 |
|
180 | 263 | ## Going Further
|
181 | 264 |
|
182 |
| -API Platform is built on top of [React Admin](https://marmelab.com/react-admin/). |
183 |
| -You can use all the features provided by the underlying library with API Platform Admin, including support for [authentication](https://marmelab.com/react-admin/Authentication.html), [authorization](https://marmelab.com/react-admin/Authorization.html) and deeper customization. |
| 265 | +The above examples limit to customizing the various API Platform Admin Guessers, but this is just the tip of the iceberg. |
| 266 | + |
| 267 | +By leveraging React Admin components and props, you can go much further in customizing the generated pages. |
184 | 268 |
|
185 |
| -To learn more about these capabilities, refer to [the React Admin documentation](https://marmelab.com/react-admin/documentation.html). |
| 269 | +Head to the next section, [Customizing the Admin](./customizing-the-admin.md), for step-by-step examples. |
0 commit comments