ra-form-layout
New form layouts for complex data entry tasks (accordion, wizard, etc.).
Installation
npm install --save @react-admin/ra-form-layout
# or
yarn add @react-admin/ra-form-layout
The package contains new translation messages (in English and French). You should add them to your i18nProvider
:
import { Admin } from 'react-admin';
import polyglotI18nProvider from 'ra-i18n-polyglot';
import englishMessages from 'ra-language-english';
import frenchMessages from 'ra-language-french';
import {
raFormLayoutLanguageEnglish,
raFormLayoutLanguageFrench,
} from '@react-admin/ra-form-layout';
const messages = {
en: { ...englishMessages, ...raFormLayoutLanguageEnglish },
fr: { ...frenchMessages, ...raFormLayoutLanguageFrench },
};
const i18nProvider = polyglotI18nProvider(locale => messages[locale], 'en');
const App = () => (
<Admin i18nProvider={is18nProvider}>
{/* ... */}
</Admin>
);
<AccordionForm>
Alternative to <SimpleForm>
, to be used as child of <Create>
or <Edit>
. Expects <AccordionFormPanel>
elements as children.
By default, each child accordion element handles its expanded state independently.
import {
Edit,
TextField,
TextInput,
DateInput,
SelectInput,
ArrayInput,
SimpleFormIterator,
BooleanInput,
} from 'react-admin';
import { AccordionForm, AccordionFormPanel } from '@react-admin/ra-form-layout';
// don't forget the component="div" prop on the main component to disable the main Card
const CustomerEdit: FC = props => (
<Edit {...props} component="div">
<AccordionForm autoClose>
<AccordionFormPanel label="Identity">
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="sex" choices={sexChoices} />
</AccordionFormPanel>
<AccordionFormPanel label="Occupations">
<ArrayInput source="occupations" label="">
<SimpleFormIterator>
<TextInput source="name" validate={required()} />
<DateInput source="from" validate={required()} />
<DateInput source="to" />
</SimpleFormIterator>
</ArrayInput>
</AccordionFormPanel>
<AccordionFormPanel label="Preferences">
<SelectInput
source="language"
choices={languageChoices}
defaultValue="en"
/>
<BooleanInput source="dark_theme" />
<BooleanInput source="accepts_emails_from_partners" />
</AccordionFormPanel>
</AccordionForm>
</Edit>
);
autoClose
When setting autoClose
in the <AccordionForm>
, only one accordion remains open at a time. The first accordion is open by default, and when a user opens another one, the current open accordion closes.
import { Edit, TextField, TextInput, DateInput, SelectInput, ArrayInput, SimpleFormIterator, BooleanInput } from 'react-admin';
import { AccordionForm, AccordionFormPanel } from '@react-admin/ra-form-layout';
// don't forget the component="div" prop on the main component to disable the main Card
const CustomerEdit: FC = props => (
<Edit {...props} component="div">
- <AccordionForm>
+ <AccordionForm autoClose>
<AccordionFormPanel label="Identity" defaultExpanded>
<TextField source="id" />
...
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 in the react-admin docs.
<AccordionFormPanel>
Renders a material-ui <Accordion>
component. In the <AccordionDetails>
, renders each child inside a <FormInput>
(the same layout as in <SimpleForm>
).
Prop | Required | Type | Default | Description |
---|---|---|---|---|
label |
Required | string |
- | The main label used as the accordion summary. Appears in red when the accordion has errors |
children |
Required | ReactNode |
- | A list of <Input> elements |
secondary |
Optional | string |
- | The secondary label used as the accordion summary |
defaultExpanded |
Optional | boolean |
false |
Set to true to have the accordion expanded by default (except if autoClose = true on the parent) |
disabled |
Optional | boolean |
false |
If true, the accordion will be displayed in a disabled state. |
square |
Optional | boolean |
false |
If true, rounded corners are disabled. |
import {
Edit,
TextField,
TextInput,
DateInput,
SelectInput,
ArrayInput,
SimpleFormIterator,
BooleanInput,
} from 'react-admin';
import { AccordionForm, AccordionFormPanel } from '@react-admin/ra-form-layout';
const CustomerEdit: FC = props => (
<Edit {...props} component="div">
<AccordionForm>
<AccordionFormPanel label="Identity" defaultExpanded>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="sex" choices={sexChoices} />
</AccordionFormPanel>
</AccordionFormPanel>
</Edit>
);
<AccordionSection>
Renders children (Inputs) inside a material-ui <Accordion>
element without a Card style. To be used as child of a <SimpleForm>
or a <TabbedForm>
element.
Prefer <AccordionSection>
to <AccordionForm>
to always display a list of important inputs, then offer accordions for secondary inputs.
Prop | Required | Type | Default | Description |
---|---|---|---|---|
label |
Required | string |
- | The main label used as the accordion summary. |
children |
Required | ReactNode |
- | A list of <Input> elements |
fullWidth |
Optional | boolean |
false |
If true, the Accordion take sthe entire form width. |
className |
Optional | string |
- | A class name to style the underlying <Accordion> |
secondary |
Optional | string |
- | The secondary label used as the accordion summary |
defaultExpanded |
Optional | boolean |
false |
Set to true to have the accordion expanded by default |
disabled |
Optional | boolean |
false |
If true, the accordion will be displayed in a disabled state. |
square |
Optional | boolean |
false |
If true, rounded corners are disabled. |
import {
Edit,
TextField,
TextInput,
DateInput,
SelectInput,
ArrayInput,
SimpleForm,
SimpleFormIterator,
BooleanInput,
} from 'react-admin';
import { AccordionForm, AccordionFormPanel } from '@react-admin/ra-form-layout';
const CustomerEdit: FC = props => (
<Edit {...props} component="div">
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="sex" choices={sexChoices} />
<AccordionSection label="Occupations" fullWidth>
<ArrayInput source="occupations" label="">
<SimpleFormIterator>
<TextInput source="name" validate={required()} />
<DateInput source="from" validate={required()} />
<DateInput source="to" />
</SimpleFormIterator>
</ArrayInput>
</AccordionSection>
<AccordionSection label="Preferences" fullWidth>
<SelectInput
source="language"
choices={languageChoices}
defaultValue="en"
/>
<BooleanInput source="dark_theme" />
<BooleanInput source="accepts_emails_from_partners" />
</AccordionSection>
</SimpleFormForm>
</Edit>
);
<WizardForm>
Alternative to <SimpleForm>
, to be used as child of <Create>
. Expects <WizardFormStep>
elements as children.
Note: You can also use the <WizardForm>
as child of <Edit>
but it's considered as a bad practice to provide a wizard form for existing resources.
import React, { FC } from 'react';
import { Create, TextInput, required } from 'react-admin';
import { WizardForm, WizardFormStep } from '@react-admin/ra-form-layout';
const PostCreate: FC = props => (
<Create {...props}>
<WizardForm>
<WizardFormStep label="First step">
<TextInput source="title" validate={required()} />
</WizardFormStep>
<WizardFormStep label="Second step">
<TextInput source="description" />
</WizardFormStep>
<WizardFormStep label="Third step">
<TextInput source="fullDescription" validate={required()} />
</WizardFormStep>
</WizardForm>
</Create>
);
toolbar
You can customize the form toolbar by passing a custom component in the toolbar
prop.
import React, { FC } from 'react';
import { Create, TextInput, required } from 'react-admin';
import { WizardForm, WizardFormStep } from '@react-admin/ra-form-layout';
const MyToolbar: FC = ({
hasPreviousStep,
hasNextStep,
onPreviousClick,
onNextClick,
handleSubmit,
handleSubmitWithRedirect,
invalid,
redirect,
saving,
submitOnEnter,
}) => {
const save = handleSubmitWithRedirect || handleSubmit;
return (
<ul>
{hasPreviousStep ? (
<li>
<Button onClick={onPreviousClick}>
PREVIOUS
</Button>
</li>
) : null}
{hasNextStep ? (
<li>
<Button disabled={invalid} onClick={onNextClick}>
NEXT
</Button>
</li>
) : (
<li>
<Button disabled={invalid} onClick={save}>
SAVE
</Button>
</li>
)}
</ul>
);
};
const PostCreate: FC = props => (
<Create {...props}>
<WizardForm toolbar={MyToolbar}>
<WizardFormStep label="First step">
<TextInput source="title" validate={required()} />
</WizardFormStep>
<WizardFormStep label="Second step">
<TextInput source="description" />
</WizardFormStep>
<WizardFormStep label="Third step">
<TextInput source="fullDescription" validate={required()} />
</WizardFormStep>
</WizardForm>
</Create>
);
progress
You can also customize the progress stepper by passing a custom component in the progress
prop.
const MyProgress: FC = ({
currentStep,
onStepClick,
steps,
}) => (
<ul>
{steps.map((step, index) => {
const label = React.cloneElement(step, { intent: 'label' });
return (
<li key={`step_${index}`}>
{!onStepClick ? (
<span className={currentStep === index ? 'active' : undefined}>
{label}
</span>
) : (
<button onClick={() => onStepClick(index)}>
{label}
</button>
)}
</li>
);
})}
</ul>
);
const PostCreate: FC = props => (
<Create {...props}>
<WizardForm progress={MyProgress}>
<WizardFormStep label="First step">
<TextInput source="title" validate={required()} />
</WizardFormStep>
<WizardFormStep label="Second step">
<TextInput source="description" />
</WizardFormStep>
<WizardFormStep label="Third step">
<TextInput source="fullDescription" validate={required()} />
</WizardFormStep>
</WizardForm>
</Create>
);
<CreateDialog>
& <EditDialog>
Sometimes it makes sense to edit or create a resource without leaving the context of the list page.
For those cases, you can use the <CreateDialog>
and <EditDialog>
components.
They accept a single child which is the form, either a <SimpleForm>
, a <TabbedForm>
or a custom one, just like the <Create>
and <Edit>
components.
import React from 'react';
import { List, Datagrid, SimpleForm, TextField, TextInput, DateInput, required } from 'react-admin';
import { EditDialog, CreateDialog } from '@react-admin/ra-form-layout';
const CustomerList = props => (
<>
<List {...props}>
<Datagrid>
...
</Datagrid>
</List>
<EditDialog {...props}>
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="date_of_birth" label="born" validate={required()} />
</SimpleForm>
</EditDialog>
<CreateDialog {...props}>
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="date_of_birth" label="born" validate={required()} />
</SimpleForm>
</CreateDialog>
</>
);
Unlike the <Create>
and <Edit>
components, their title will be displayed in the <Dialog>
, not in the <AppBar>
.
import React from 'react';
import { List, Datagrid, SimpleForm, TextField, TextInput, DateInput, required } from 'react-admin';
import { EditDialog, CreateDialog } from '@react-admin/ra-form-layout';
const CustomerEditTitle = ({ record }) =>
record
? <span>{record.last_name} {record.first_name}</span>
: null;
const CustomerList = props => (
<>
<List {...props}>
<Datagrid>
...
</Datagrid>
</List>
<EditDialog {...props} title={<CustomerEditTitle />}>
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="date_of_birth" label="born" validate={required()} />
</SimpleForm>
</EditDialog>
<CreateDialog {...props} title="Create a new customer">
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="date_of_birth" label="born" validate={required()} />
</SimpleForm>
</CreateDialog>
</>
);
Besides, you can also pass the props accepted by the material-ui <Dialog>
like fullWidth
or maxWidth
.
import React from 'react';
import { List, Datagrid, SimpleForm, TextField, TextInput, DateInput, required } from 'react-admin';
import { EditDialog, CreateDialog } from '@react-admin/ra-form-layout';
const CustomerList = props => (
<>
<List {...props}>
<Datagrid>
...
</Datagrid>
</List>
<EditDialog {...props} fullWidth maxWidth="md">
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="date_of_birth" label="born" validate={required()} />
</SimpleForm>
</EditDialog>
<CreateDialog {...props} fullWidth maxWidth="md">
<SimpleForm>
<TextField source="id" />
<TextInput source="first_name" validate={required()} />
<TextInput source="last_name" validate={required()} />
<DateInput source="date_of_birth" label="born" validate={required()} />
</SimpleForm>
</CreateDialog>
</>
);
CHANGELOG
v1.4.0
2020-10-26
- (feat) Allow customizing the accordion sub-components (
Accordion
,<AccordionSummary>
and<AccordionDetails>
) - (feat) Add types for the
<AccordionSection>
v1.3.0
2020-10-05
- (deps) Upgrade react-admin to v3.9.0
v1.2.0
2020-10-01
- (feat) Dialog Form (CreateDialog & EditDialog)
v1.1.0
2020-09-28
- (feat) Wizard Form
v1.0.1
2020-09-22
- (fix) Fix Storybook error on
history.replace
v1.0.0
2020-09-22
- First release