Skip to content

Commit c9a8e18

Browse files
authored
Merge pull request #7862 from marmelab/doc-longform
[Doc] Document LongForm component
2 parents 1ea5573 + 4d86c92 commit c9a8e18

4 files changed

+215
-1
lines changed

docs/LongForm.md

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
layout: default
3+
title: "LongForm"
4+
---
5+
6+
# `<LongForm>`
7+
8+
This [Enterprise Edition](https://marmelab.com/ra-enterprise)<img class="icon" src="./img/premium.svg" /> component offers an alternative form layout, to be used as child of `<Create>` or `<Edit>`. Expects `<LongForm.Section>` elements as children.
9+
10+
![LongForm](./img/ra-longform-overview.gif)
11+
12+
Test it live on [the Enterprise Edition Storybook](https://storybook.ra-enterprise.marmelab.com/?path=/story/ra-form-layout-longform--basic).
13+
14+
This component will come in handy if you need to create a long form, with many input fields divided into several sections. It makes navigation easier, by providing a TOC (Table Of Contents) and by keeping the toolbar fixed at the bottom position.
15+
16+
## Usage
17+
18+
Use `<LongForm>` as a child of `<Create>` or `<Edit>`. It should have `<LongForm.Section>` children, which contain inputs.
19+
20+
```jsx
21+
import {
22+
ArrayInput,
23+
BooleanInput,
24+
DateInput,
25+
Edit,
26+
required,
27+
SelectInput,
28+
SimpleFormIterator,
29+
TextField,
30+
TextInput,
31+
Labeled,
32+
} from 'react-admin';
33+
import { LongForm } from '@react-admin/ra-form-layout';
34+
35+
const sexChoices = [
36+
{ id: 'male', name: 'Male' },
37+
{ id: 'female', name: 'Female' },
38+
];
39+
40+
const languageChoices = [
41+
{ id: 'en', name: 'English' },
42+
{ id: 'fr', name: 'French' },
43+
];
44+
45+
const CustomerEdit = () => (
46+
<Edit component="div">
47+
<LongForm>
48+
<LongForm.Section label="Identity">
49+
<Labeled label="id">
50+
<TextField source="id" />
51+
</Labeled>
52+
<TextInput source="first_name" validate={required()} />
53+
<TextInput source="last_name" validate={required()} />
54+
<DateInput source="dob" label="born" validate={required()} />
55+
<SelectInput source="sex" choices={sexChoices} />
56+
</LongForm.Section>
57+
<LongForm.Section label="Occupations">
58+
<ArrayInput source="occupations" label="">
59+
<SimpleFormIterator>
60+
<TextInput source="name" validate={required()} />
61+
<DateInput source="from" validate={required()} />
62+
<DateInput source="to" />
63+
</SimpleFormIterator>
64+
</ArrayInput>
65+
</LongForm.Section>
66+
<LongForm.Section label="Preferences">
67+
<SelectInput
68+
source="language"
69+
choices={languageChoices}
70+
defaultValue="en"
71+
/>
72+
<BooleanInput source="dark_theme" />
73+
<BooleanInput source="accepts_emails_from_partners" />
74+
</LongForm.Section>
75+
</LongForm>
76+
</Edit>
77+
);
78+
```
79+
80+
`<LongForm>` accepts the same props as [the `<Form>` component](./Form.md).
81+
82+
* `defaultValues`
83+
* `id`
84+
* `noValidate`
85+
* `onSubmit`
86+
* `sx`
87+
* `toolbar`
88+
* `validate`
89+
* `warnWhenUnsavedChanges`
90+
91+
Additional props are passed to `react-hook-form`'s [`useForm` hook](https://react-hook-form.com/api/useform).
92+
93+
## `toolbar`
94+
95+
You can customize the form Toolbar by passing a custom element in the `toolbar` prop. The form expects the same type of element as `<SimpleForm>`, see [the `<SimpleForm toolbar>` prop documentation](https://marmelab.com/react-admin/CreateEdit.html#toolbar) in the react-admin docs.
96+
97+
```jsx
98+
import {
99+
Edit,
100+
SaveButton,
101+
Toolbar as RaToolbar,
102+
} from 'react-admin';
103+
import { LongForm } from '@react-admin/ra-form-layout';
104+
105+
const CustomerCustomToolbar = props => (
106+
<RaToolbar {...props}>
107+
<SaveButton label="Save and return" type="button" variant="outlined" />
108+
</RaToolbar>
109+
);
110+
111+
const CustomerEditWithToolbar = () => (
112+
<Edit component="div">
113+
<LongForm toolbar={<CustomerCustomToolbar />}>
114+
<LongForm.Section label="Identity">
115+
...
116+
</LongForm.Section>
117+
<LongForm.Section label="Occupations">
118+
...
119+
</LongForm.Section>
120+
<LongForm.Section label="Preferences">
121+
...
122+
</LongForm.Section>
123+
</LongForm>
124+
</Edit>
125+
);
126+
```
127+
128+
## `sx`: CSS API
129+
130+
The `<LongForm>` component accepts the usual `className` prop. You can also override the styles of the inner components thanks to the `sx` property. This property accepts the following subclasses:
131+
132+
| Rule name | Description |
133+
|------------------------|----------------------------------------|
134+
| `RaLongForm` | Applied to the root component |
135+
| `& .RaLongForm-toc` | Applied to the TOC |
136+
| `& .RaLongForm-main` | Applied to the main `<Card>` component |
137+
| `& .RaLongForm-toolbar`| Applied to the toolbar |
138+
| `& .RaLongForm-error` | Applied to the `<MenuItem>` in case the section has validation errors |
139+
140+
## `<LongForm.Section>`
141+
142+
The children of `<LongForm>` must be `<LongForm.Section>` elements.
143+
144+
This component adds a section title (using a `<Typography variant="h4">`), then renders each child inside a [MUI `<Stack>`](https://mui.com/material-ui/react-stack/), and finally adds an MUI `<Divider>` at the bottom of the section.
145+
146+
It accepts the following props:
147+
148+
| Prop | Required | Type | Default | Description |
149+
| ----------------- | -------- | ----------- | ------- | ------------------------------------------------------------------------------------ |
150+
| `label` | Required | `string` | - | The main label used as the section title. Appears in red when the section has errors |
151+
| `children` | Required | `ReactNode` | - | A list of `<Input>` elements |
152+
| `cardinality` | Optional | `number` | - | A number to be displayed next to the label in TOC, to quantify it |
153+
| `sx` | Optional | `object` | - | An object containing the MUI style overrides to apply to the root component |
154+
155+
### `cardinality`
156+
157+
The `cardinality` prop allows to specify a numeral quantity to be displayed next to the section label in the TOC.
158+
159+
![LongForm.Section cardinality](./img/ra-longform-cardinality.png)
160+
161+
```jsx
162+
import React, { useEffect, useState } from 'react';
163+
import {
164+
Edit,
165+
TextField,
166+
} from 'react-admin';
167+
168+
import { LongForm } from '@react-admin/ra-form-layout';
169+
170+
const CustomerEditWithCardinality = () => {
171+
const [publications, setPublications] = useState([]);
172+
useEffect(() => {
173+
setTimeout(() => {
174+
setPublications([
175+
{ id: 1, title: 'Publication 1' },
176+
{ id: 2, title: 'Publication 2' },
177+
{ id: 3, title: 'Publication 3' },
178+
]);
179+
}, 500);
180+
}, []);
181+
182+
return (
183+
<Edit component="div">
184+
<LongForm>
185+
<LongForm.Section label="Identity">
186+
...
187+
</LongForm.Section>
188+
<LongForm.Section label="Occupations">
189+
...
190+
</LongForm.Section>
191+
<LongForm.Section label="Preferences">
192+
...
193+
</LongForm.Section>
194+
<LongForm.Section
195+
label="Publications"
196+
cardinality={publications.length}
197+
>
198+
<ul>
199+
{publications.map(publication => (
200+
<li key={publication.id}>
201+
<TextField
202+
source="title"
203+
record={publication}
204+
/>
205+
</li>
206+
))}
207+
</ul>
208+
</LongForm.Section>
209+
</LongForm>
210+
</Edit>
211+
);
212+
};
213+
```

docs/img/ra-longform-cardinality.png

22.5 KB
Loading

docs/img/ra-longform-overview.gif

789 KB
Loading

docs/navigation.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,12 @@
8080
<li {% if page.path == 'EditGuesser.md' %} class="active" {% endif %}><a class="nav-link" href="./EditGuesser.html"><code>&lt;EditGuesser&gt;</code></a></li>
8181
<li {% if page.path == 'SimpleForm.md' %} class="active" {% endif %}><a class="nav-link" href="./SimpleForm.html"><code>&lt;SimpleForm&gt;</code></a></li>
8282
<li {% if page.path == 'TabbedForm.md' %} class="active" {% endif %}><a class="nav-link" href="./TabbedForm.html"><code>&lt;TabbedForm&gt;</code></a></li>
83+
<li {% if page.path == 'Form.md' %} class="active" {% endif %}><a class="nav-link" href="./Form.html"><code>&lt;Form&gt;</code></a></li>
84+
<li {% if page.path == 'LongForm.md' %} class="active" {% endif %}><a class="nav-link" href="./LongForm.html"><code>&lt;LongForm&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
8385
<li {% if page.path == 'AccordionForm.md' %} class="active" {% endif %}><a class="nav-link" href="./AccordionForm.html"><code>&lt;AccordionForm&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
8486
<li {% if page.path == 'WizardForm.md' %} class="active" {% endif %}><a class="nav-link" href="./WizardForm.html"><code>&lt;WizardForm&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
8587
<li {% if page.path == 'EditDialog.md' %} class="active" {% endif %}><a class="nav-link" href="./EditDialog.html"><code>&lt;EditDialog&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
8688
<li {% if page.path == 'CreateDialog.md' %} class="active" {% endif %}><a class="nav-link" href="./CreateDialog.html"><code>&lt;CreateDialog&gt;</code><img class="premium" src="./img/premium.svg" /></a></li>
87-
<li {% if page.path == 'Form.md' %} class="active" {% endif %}><a class="nav-link" href="./Form.html"><code>&lt;Form&gt;</code></a></li>
8889
<li {% if page.path == 'Toolbar.md' %} class="active" {% endif %}><a class="nav-link" href="./Toolbar.html"><code>&lt;Toolbar&gt;</code></a></li>
8990
<li {% if page.path == 'SaveButton.md' %} class="active" {% endif %}><a class="nav-link" href="./SaveButton.html"><code>&lt;SaveButton&gt;</code></a></li>
9091
<li {% if page.path == 'useCreateContext.md' %} class="active" {% endif %}><a class="nav-link" href="./useCreateContext.html"><code>useCreateContext</code></a></li>

0 commit comments

Comments
 (0)