Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Document LongForm component #7862

Merged
merged 1 commit into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 213 additions & 0 deletions docs/LongForm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
layout: default
title: "LongForm"
---

# `<LongForm>`

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.

![LongForm](./img/ra-longform-overview.gif)

Test it live on [the Enterprise Edition Storybook](https://storybook.ra-enterprise.marmelab.com/?path=/story/ra-form-layout-longform--basic).

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.

## Usage

Use `<LongForm>` as a child of `<Create>` or `<Edit>`. It should have `<LongForm.Section>` children, which contain inputs.

```jsx
import {
ArrayInput,
BooleanInput,
DateInput,
Edit,
required,
SelectInput,
SimpleFormIterator,
TextField,
TextInput,
Labeled,
} from 'react-admin';
import { LongForm } from '@react-admin/ra-form-layout';

const sexChoices = [
{ id: 'male', name: 'Male' },
{ id: 'female', name: 'Female' },
];

const languageChoices = [
{ id: 'en', name: 'English' },
{ id: 'fr', name: 'French' },
];

const CustomerEdit = () => (
<Edit component="div">
<LongForm>
<LongForm.Section label="Identity">
<Labeled label="id">
<TextField source="id" />
</Labeled>
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="sex" choices={sexChoices} />
</LongForm.Section>
<LongForm.Section label="Occupations">
<ArrayInput source="occupations" label="">
<SimpleFormIterator>
<TextInput source="name" validate={required()} />
<DateInput source="from" validate={required()} />
<DateInput source="to" />
</SimpleFormIterator>
</ArrayInput>
</LongForm.Section>
<LongForm.Section label="Preferences">
<SelectInput
source="language"
choices={languageChoices}
defaultValue="en"
/>
<BooleanInput source="dark_theme" />
<BooleanInput source="accepts_emails_from_partners" />
</LongForm.Section>
</LongForm>
</Edit>
);
```

`<LongForm>` accepts the same props as [the `<Form>` component](./Form.md).

* `defaultValues`
* `id`
* `noValidate`
* `onSubmit`
* `sx`
* `toolbar`
* `validate`
* `warnWhenUnsavedChanges`

Additional props are passed to `react-hook-form`'s [`useForm` hook](https://react-hook-form.com/api/useform).

## `toolbar`

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.

```jsx
import {
Edit,
SaveButton,
Toolbar as RaToolbar,
} from 'react-admin';
import { LongForm } from '@react-admin/ra-form-layout';

const CustomerCustomToolbar = props => (
<RaToolbar {...props}>
<SaveButton label="Save and return" type="button" variant="outlined" />
</RaToolbar>
);

const CustomerEditWithToolbar = () => (
<Edit component="div">
<LongForm toolbar={<CustomerCustomToolbar />}>
<LongForm.Section label="Identity">
...
</LongForm.Section>
<LongForm.Section label="Occupations">
...
</LongForm.Section>
<LongForm.Section label="Preferences">
...
</LongForm.Section>
</LongForm>
</Edit>
);
```

## `sx`: CSS API

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:

| Rule name | Description |
|------------------------|----------------------------------------|
| `RaLongForm` | Applied to the root component |
| `& .RaLongForm-toc` | Applied to the TOC |
| `& .RaLongForm-main` | Applied to the main `<Card>` component |
| `& .RaLongForm-toolbar`| Applied to the toolbar |
| `& .RaLongForm-error` | Applied to the `<MenuItem>` in case the section has validation errors |

## `<LongForm.Section>`

The children of `<LongForm>` must be `<LongForm.Section>` elements.

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.

It accepts the following props:

| Prop | Required | Type | Default | Description |
| ----------------- | -------- | ----------- | ------- | ------------------------------------------------------------------------------------ |
| `label` | Required | `string` | - | The main label used as the section title. Appears in red when the section has errors |
| `children` | Required | `ReactNode` | - | A list of `<Input>` elements |
| `cardinality` | Optional | `number` | - | A number to be displayed next to the label in TOC, to quantify it |
| `sx` | Optional | `object` | - | An object containing the MUI style overrides to apply to the root component |

### `cardinality`

The `cardinality` prop allows to specify a numeral quantity to be displayed next to the section label in the TOC.

![LongForm.Section cardinality](./img/ra-longform-cardinality.png)

```jsx
import React, { useEffect, useState } from 'react';
import {
Edit,
TextField,
} from 'react-admin';

import { LongForm } from '@react-admin/ra-form-layout';

const CustomerEditWithCardinality = () => {
const [publications, setPublications] = useState([]);
useEffect(() => {
setTimeout(() => {
setPublications([
{ id: 1, title: 'Publication 1' },
{ id: 2, title: 'Publication 2' },
{ id: 3, title: 'Publication 3' },
]);
}, 500);
}, []);

return (
<Edit component="div">
<LongForm>
<LongForm.Section label="Identity">
...
</LongForm.Section>
<LongForm.Section label="Occupations">
...
</LongForm.Section>
<LongForm.Section label="Preferences">
...
</LongForm.Section>
<LongForm.Section
label="Publications"
cardinality={publications.length}
>
<ul>
{publications.map(publication => (
<li key={publication.id}>
<TextField
source="title"
record={publication}
/>
</li>
))}
</ul>
</LongForm.Section>
</LongForm>
</Edit>
);
};
```
Binary file added docs/img/ra-longform-cardinality.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/ra-longform-overview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion docs/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@
<li {% if page.path == 'EditGuesser.md' %} class="active" {% endif %}><a class="nav-link" href="./EditGuesser.html"><code>&lt;EditGuesser&gt;</code></a></li>
<li {% if page.path == 'SimpleForm.md' %} class="active" {% endif %}><a class="nav-link" href="./SimpleForm.html"><code>&lt;SimpleForm&gt;</code></a></li>
<li {% if page.path == 'TabbedForm.md' %} class="active" {% endif %}><a class="nav-link" href="./TabbedForm.html"><code>&lt;TabbedForm&gt;</code></a></li>
<li {% if page.path == 'Form.md' %} class="active" {% endif %}><a class="nav-link" href="./Form.html"><code>&lt;Form&gt;</code></a></li>
<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>
<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>
<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>
<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>
<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>
<li {% if page.path == 'Form.md' %} class="active" {% endif %}><a class="nav-link" href="./Form.html"><code>&lt;Form&gt;</code></a></li>
<li {% if page.path == 'Toolbar.md' %} class="active" {% endif %}><a class="nav-link" href="./Toolbar.html"><code>&lt;Toolbar&gt;</code></a></li>
<li {% if page.path == 'SaveButton.md' %} class="active" {% endif %}><a class="nav-link" href="./SaveButton.html"><code>&lt;SaveButton&gt;</code></a></li>
<li {% if page.path == 'useCreateContext.md' %} class="active" {% endif %}><a class="nav-link" href="./useCreateContext.html"><code>useCreateContext</code></a></li>
Expand Down