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

Add ability to make the <SaveButton> not disabled by default #5002

Merged
merged 12 commits into from
Jul 22, 2020
Merged
19 changes: 17 additions & 2 deletions docs/CreateEdit.md
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,7 @@ export const PostCreate = (props) => (
);
```

Another use case is to remove the `<DeleteButton>` from the toolbar in an edit view. In that case, create a custom toolbar containing only the `<SaveButton>` as a child;
Another use case is to remove the `<DeleteButton>` from the toolbar in an edit view. In that case, create a custom toolbar containing only the `<SaveButton>` as a child:

```jsx
import * as React from "react";
Expand All @@ -1194,6 +1194,21 @@ export const PostEdit = (props) => (
);
```

By default the `<SaveButton>` is disabled when the form is `pristine`. You can bypass this behavior and always enable it without recreating the whole `<Toolbar>`:

```jsx
import * as React from 'react';
import { Edit, SimpleForm, Toolbar } from 'react-admin';

export const PostEdit = (props) => (
<Edit {...props}>
<SimpleForm toolbar={<Toolbar alwaysEnableSaveButton />}>
...
</SimpleForm>
</Edit>
);
```

Here are the props received by the `Toolbar` component when passed as the `toolbar` prop of the `SimpleForm` or `TabbedForm` components:

* `handleSubmitWithRedirect`: The function to call in order to submit the form. It accepts a single parameter overriding the form's default redirect.
Expand Down Expand Up @@ -1248,7 +1263,7 @@ You can customize each row in a `<SimpleForm>` or in a `<TabbedForm>` by passing

You can find more about these props in [the Input documentation](./Inputs.md#common-input-props).

You can also [wrap inputs inside containers](#custom-row-container), or [create a custom Form component](#custom-form-component), alternative to `<SimpleForm>` or `<TabbedForm>`.
You can also [wrap inputs inside containers](#custom-row-container), or [create a custom Form component](#custom-form-component), alternative to `<SimpleForm>` or `<TabbedForm>`.

### Variant

Expand Down
3 changes: 2 additions & 1 deletion packages/ra-ui-materialui/src/button/SaveButton.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const invalidButtonDomProps = {
handleSubmitWithRedirect: jest.fn(),
invalid: false,
onSave: jest.fn(),
disabled: true,
pristine: false,
record: { id: 123, foo: 'bar' },
redirect: 'list',
Expand Down Expand Up @@ -57,7 +58,7 @@ describe('<SaveButton />', () => {
const { getByLabelText } = render(
<TestContext>
<ThemeProvider theme={theme}>
<SaveButton pristine={true} />
<SaveButton disabled={true} />
</ThemeProvider>
</TestContext>
);
Expand Down
10 changes: 4 additions & 6 deletions packages/ra-ui-materialui/src/button/SaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import { sanitizeButtonRestProps } from './Button';
/**
* Submit button for resource forms (Edit and Create).
*
* @typedef {Object} Props the props you can use (other props are injected by Toolbar)
* @typedef {Object} Props the props you can use (other props are injected by the <Toolbar>)
* @prop {string} className
* @prop {string} label Button label. Defaults to 'ra.action.save', translated.
* @prop {boolean} disabled Injected by SimpleForm, which disables the SaveButton when it's pristine
* @prop {boolean} disabled Disable the button.
* @prop {string} variant Material-ui variant for the button. Defaults to 'contained'.
* @prop {ReactElement} icon
* @prop {string|boolean} redirect Override of the default redirect in case of success. Can be 'list', 'show', 'edit' (for create views), or false (to stay on the creation form).
Expand Down Expand Up @@ -69,7 +69,6 @@ const SaveButton: FC<SaveButtonProps> = props => {
invalid,
label = 'ra.action.save',
disabled,
pristine,
redirect,
saving,
submitOnEnter,
Expand Down Expand Up @@ -142,7 +141,7 @@ const SaveButton: FC<SaveButtonProps> = props => {
onClick={handleClick}
color={saving ? 'default' : 'primary'}
aria-label={displayedLabel}
disabled={disabled || pristine}
disabled={disabled}
{...sanitizeButtonRestProps(rest)}
>
{saving ? (
Expand Down Expand Up @@ -191,7 +190,7 @@ interface Props {
invalid?: boolean;
label?: string;
onClick?: () => void;
pristine?: boolean;
disabled?: boolean;
redirect?: RedirectionSideEffect;
saving?: boolean;
submitOnEnter?: boolean;
Expand All @@ -214,7 +213,6 @@ SaveButton.propTypes = {
onSave: PropTypes.func,
invalid: PropTypes.bool,
label: PropTypes.string,
pristine: PropTypes.bool,
redirect: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/form/SimpleForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import CardContentInner from '../layout/CardContentInner';
* @prop {Function} validate
* @prop {boolean} submitOnEnter
* @prop {string} redirect
* @prop {ReactElement} toolbar The element displayed at the bottom of the form, contzining the SaveButton
* @prop {ReactElement} toolbar The element displayed at the bottom of the form, containing the SaveButton
* @prop {string} variant Apply variant to all inputs. Possible values are 'standard', 'outlined', and 'filled' (default)
* @prop {string} margin Apply variant to all inputs. Possible values are 'none', 'normal', and 'dense' (default)
*
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/form/TabbedForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import TabbedFormTabs, { getTabFullPath } from './TabbedFormTabs';
* @prop {Function} validate
* @prop {boolean} submitOnEnter
* @prop {string} redirect
* @prop {ReactElement} toolbar The element displayed at the bottom of the form, contzining the SaveButton
* @prop {ReactElement} toolbar The element displayed at the bottom of the form, containing the SaveButton
* @prop {string} variant Apply variant to all inputs. Possible values are 'standard', 'outlined', and 'filled' (default)
* @prop {string} margin Apply variant to all inputs. Possible values are 'none', 'normal', and 'dense' (default)
*
Expand Down
44 changes: 44 additions & 0 deletions packages/ra-ui-materialui/src/form/Toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,46 @@ const useStyles = makeStyles(
const valueOrDefault = (value, defaultValue) =>
typeof value === 'undefined' ? defaultValue : value;

/**
* The Toolbar displayed at the bottom of forms.
*
* @example Always enable the <SaveButton />
*
* import * as React from 'react';
* import {
* Create,
* DateInput,
* TextInput,
* SimpleForm,
* Toolbar,
* required,
* } from 'react-admin';
*
* const now = new Date();
* const defaultSort = { field: 'title', order: 'ASC' };
*
* const CommentCreate = props => (
* <Create {...props}>
* <SimpleForm redirect={false} toolbar={<Toolbar alwaysEnableSaveButton={true} />}>
* <TextInput
* source="author.name"
* fullWidth
* />
* <DateInput source="created_at" defaultValue={now} />
* <TextInput source="body" fullWidth={true} multiline={true} />
* </SimpleForm>
* </Create>
* );
*
* @typedef {Object} Props the props you can use (other props are injected by the <SimpleForm>)
* @prop {boolean} alwaysEnableSaveButton Force enabling the <SaveButton>. It it's not defined, the <SaveButton> will be enabled using the `pristine` prop (disabled if pristine, enabled otherwise).
* @prop {ReactElement[]} children Customize the buttons you want to display in the <Toolbar>.
* @prop {string} width Apply the mobile or the desktop classes depending on the width. Pass "xs" to display the mobile version.
*
*/
const Toolbar = props => {
const {
alwaysEnableSaveButton,
basePath,
children,
className,
Expand All @@ -67,6 +105,11 @@ const Toolbar = props => {
...rest
} = props;
const classes = useStyles(props);

// Use form pristine to enable or disable the save button
// if alwaysEnableSaveButton is undefined
const disabled = !valueOrDefault(alwaysEnableSaveButton, !pristine);

return (
<Fragment>
<MuiToolbar
Expand All @@ -87,6 +130,7 @@ const Toolbar = props => {
handleSubmitWithRedirect={
handleSubmitWithRedirect || handleSubmit
}
disabled={disabled}
invalid={invalid}
redirect={redirect}
saving={saving}
Expand Down