Skip to content

Commit 54cb765

Browse files
jfthuillieralanpoulain
authored andcommitted
Overriding documentation (#765)
1 parent e84c399 commit 54cb765

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed

admin/customizing.md

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
# Customizing the Admin
2+
3+
## Preparing your App
4+
5+
```javascript
6+
import React from 'react';
7+
import { HydraAdmin, replaceResources } from '@api-platform/admin';
8+
import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
9+
import { greetingsNameInput } from './components/greetings/inputs.js';
10+
import { greetingsNameField } from './components/greetings/fields.js';
11+
import BookList from './components/books/list.js';
12+
import BookCreate from './components/books/create.js';
13+
import BookEdit from './components/books/edit.js';
14+
import { booksDescriptionInput } from './components/books/inputs.js';
15+
16+
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT;
17+
18+
const greetings = {
19+
name: 'greetings',
20+
fields: [
21+
{
22+
name: 'name',
23+
input: greetingsNameInput,
24+
field: greetingsNameField,
25+
}
26+
],
27+
listFields: [],
28+
};
29+
30+
const books = {
31+
name: 'books',
32+
list: BookList,
33+
create: BookCreate,
34+
edit: BookEdit,
35+
fields: [
36+
{
37+
name: 'description',
38+
input: booksDescriptionInput,
39+
}
40+
]
41+
};
42+
43+
const newResources = [
44+
greetings,
45+
books,
46+
];
47+
48+
const myApiDocumentationParser = entrypoint => parseHydraDocumentation(entrypoint)
49+
.then(({ api }) => {
50+
replaceResources(api.resources, newResources);
51+
return { api };
52+
})
53+
;
54+
55+
export default () => <HydraAdmin apiDocumentationParser={myApiDocumentationParser} entrypoint={entrypoint} />;
56+
```
57+
58+
All you have to do is to provide a collection of objects (the `newResources` variable).
59+
The value of the `name` property must match the resource name you want to customize, or it will be ignored.
60+
Other available properties will be explained further.
61+
62+
## Customizing Inputs
63+
64+
```javascript
65+
import React from 'react';
66+
import { TextInput } from 'react-admin';
67+
68+
const myInput = props => (
69+
<TextInput label="My new label" {...props} />
70+
);
71+
72+
const greetings = {
73+
name: 'greetings',
74+
fields: [
75+
{
76+
name: 'name',
77+
input: myInput,
78+
},
79+
],
80+
};
81+
82+
export default [
83+
greetings,
84+
];
85+
```
86+
87+
That's it! Our custom `TextInput` component will now be used in all forms to edit the `name` property of the `greeting` resource.
88+
In this example, we are reusing an `Input` component provided by `react-admin`, but you can use any component you want as long as you respect [the signature expected by react-admin](https://marmelab.com/react-admin/Inputs.html).
89+
90+
## Customizing Fields
91+
92+
```javascript
93+
import React from 'react';
94+
import { UrlField } from 'react-admin';
95+
96+
const myField = props => (
97+
<UrlField {...props} />
98+
);
99+
100+
const greetings = {
101+
name: 'greetings',
102+
fields: [
103+
{
104+
name: 'url',
105+
field: myField,
106+
},
107+
],
108+
};
109+
110+
export default [
111+
greetings,
112+
];
113+
```
114+
115+
That's it! Our custom `myField` component will now be used to display the resource.
116+
In this example, we are reusing a `Field` component provided by `react-admin`, but you can use any component you want as long as you respect [the signature expected by react-admin](https://marmelab.com/react-admin/Fields.html).
117+
118+
## "Free" Mode
119+
120+
If you want to fully customize the admin, here is how you can do it:
121+
122+
```javascript
123+
import React from 'react';
124+
125+
const GreetingList = props => <p>Yay! I can do what I want!</p>;
126+
const GreetingCreate = props => <p>Yay! I can do what I want!</p>;
127+
const GreetingEdit = props => <p>Yay! I can do what I want!</p>;
128+
129+
const greetings = {
130+
name: 'greetings',
131+
list: GreetingList,
132+
create: GreetingCreate,
133+
edit: GreetingEdit,
134+
};
135+
136+
export default [
137+
greetings,
138+
];
139+
```
140+
141+
## Reusing the Default Layout
142+
143+
Most of the time you want to keep the default layout and just customize what is inside, here is how to do it:
144+
145+
### List
146+
147+
```javascript
148+
import React from 'react';
149+
import { List, Datagrid } from 'react-admin';
150+
151+
const GreetingList = props => {
152+
const getField = fieldName => {
153+
const {options: {resource: {fields}}} = props;
154+
155+
return fields.find(resourceField => resourceField.name === fieldName) ||
156+
null;
157+
};
158+
159+
const displayField = fieldName => {
160+
const {options: {api, fieldFactory, resource}} = props;
161+
162+
const field = getField(fieldName);
163+
164+
if (field === null) {
165+
return;
166+
}
167+
168+
return fieldFactory(field, {api, resource});
169+
};
170+
171+
return (
172+
<List {...props}>
173+
<Datagrid>
174+
{displayField('name')}
175+
</Datagrid>
176+
</List>
177+
);
178+
};
179+
180+
const greetings = {
181+
name: 'greetings',
182+
list: GreetingList,
183+
};
184+
185+
export default [
186+
greetings,
187+
];
188+
```
189+
190+
### Create
191+
192+
#### Customizing the Form Layout
193+
194+
```javascript
195+
import React from 'react';
196+
import { Create, SimpleForm } from 'react-admin';
197+
import { getResourceField } from '@api-platform/admin/lib/docsUtils';
198+
199+
const GreetingCreate = props => {
200+
const {options: {inputFactory, resource}} = props;
201+
202+
return (
203+
<Create {...props}>
204+
<SimpleForm>
205+
<div className="custom-grid">
206+
<div className="column">
207+
{inputFactory(getResourceField(resource, 'name'))}
208+
</div>
209+
<div className="column">
210+
{inputFactory(getResourceField(resource, 'description'))}
211+
</div>
212+
</div>
213+
</SimpleForm>
214+
</Create>
215+
);
216+
};
217+
218+
export default [
219+
greetings,
220+
];
221+
```
222+
223+
This way, we have been reusing most of the default behavior, but we managed to had a custom grid. This could also be a way to customize the fields order, and many more things you could think of.
224+
225+
#### Dynamic Display
226+
227+
If you want to have a form with dynamic display, just use a connected component like this one:
228+
229+
```javascript
230+
import React from 'react';
231+
import { connect } from 'react-redux';
232+
import { formValueSelector } from 'redux-form';
233+
import { getResourceField } from '@api-platform/admin/lib/docsUtils';
234+
import { Create, SimpleForm } from 'react-admin';
235+
236+
const GreetingCreateView = props => {
237+
const {options: {inputFactory, resource}, formValueName} = props;
238+
239+
return (
240+
<Create {...props}>
241+
<SimpleForm>
242+
{inputFactory(getResourceField(resource, 'name'))}
243+
{formValueName && (
244+
inputFactory(getResourceField(resource, 'description'))
245+
)}
246+
</SimpleForm>
247+
</Create>
248+
);
249+
};
250+
251+
const mapStateToProps = state => ({
252+
formValueName: formValueSelector('record-form')(state, 'name'),
253+
});
254+
255+
const GreetingCreate = connect(mapStateToProps)(GreetingCreateView);
256+
257+
const greetings = {
258+
name: 'greetings',
259+
create: GreetingCreate,
260+
};
261+
262+
export default [
263+
greetings,
264+
];
265+
```
266+
267+
### Edit
268+
269+
```javascript
270+
import React from 'react';
271+
import { Edit, SimpleForm } from 'react-admin';
272+
import { getResourceField } from '@api-platform/admin/lib/docsUtils';
273+
274+
const GreetingEdit = props => {
275+
const {options: {inputFactory, resource}} = props;
276+
277+
return (
278+
<Edit {...props}>
279+
<SimpleForm>
280+
<div className="custom-grid">
281+
<div className="column">
282+
{inputFactory(getResourceField(resource, 'name'))}
283+
</div>
284+
<div className="column">
285+
{inputFactory(getResourceField(resource, 'description'))}
286+
</div>
287+
</div>
288+
</SimpleForm>
289+
</Edit>
290+
);
291+
};
292+
293+
export default [
294+
greetings,
295+
];
296+
```
297+
298+
In this example, we have been able to customize the template to add a custom grid, but you could do more, have a look at the `Create`part above to see more examples.

0 commit comments

Comments
 (0)