Skip to content

Commit

Permalink
fix: Fixed 3757 by supporting extraErrors that block the submit (#3772)
Browse files Browse the repository at this point in the history
Fix #3757 by adding a new `extraErrorsBlockSubmit` prop on `Form`
- Updated the `FormProps` and `Form` for the new `extraErrorsBlockSubmit` flag
- Updated the validation code to detect when the flag is set and there are `extraErrors` and allow the extra error to be focused on
- Updated `form-props` documentation with additional details about `extraErrors` as well as the new prop
- Updated the `CHANGELOG.md` file accordingly switching from a patch to minor release due to the new feature
  • Loading branch information
heath-freenome committed Jul 14, 2023
1 parent fc3a0d3 commit 4ea75d8
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 8 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ it according to semantic versioning. For example, if your PR adds a breaking cha
should change the heading of the (upcoming) version to include a major version bump.
-->
# 5.10.1
# 5.11.0

## @rjsf/core

- Updated `MultiSchemaField` to use `mergeSchema()` for merging in the remaining schema for `anyOf`/`oneOf`
- Added new `extraErrorsBlockSubmit` prop to `Form` that allows the extra asynchronous errors to block a form submit, fixing [#3757](https://github.com/rjsf-team/react-jsonschema-form/issues/3757)

## @rjsf/utils

Expand Down
15 changes: 9 additions & 6 deletions packages/core/src/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,12 @@ export interface FormProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
/** Formerly the `validate` prop; Takes a function that specifies custom validation rules for the form */
customValidate?: CustomValidator<T, S, F>;
/** This prop allows passing in custom errors that are augmented with the existing JSON Schema errors on the form; it
* can be used to implement asynchronous validation
* can be used to implement asynchronous validation. By default, these are non-blocking errors, meaning that you can
* still submit the form when these are the only errors displayed to the user.
*/
extraErrors?: ErrorSchema<T>;
/** If set to true, causes the `extraErrors` to become blocking when the form is submitted */
extraErrorsBlockSubmit?: boolean;
/** If set to true, turns off HTML5 validation on the form; Set to `false` by default */
noHtml5Validate?: boolean;
/** If set to true, turns off all validation. Set to `false` by default
Expand Down Expand Up @@ -603,7 +606,7 @@ export default class Form<
/** Callback function to handle when the form is submitted. First, it prevents the default event behavior. Nothing
* happens if the target and currentTarget of the event are not the same. It will omit any extra data in the
* `formData` in the state if `omitExtraData` is true. It will validate the resulting `formData`, reporting errors
* via the `onError()` callback unless validation is disabled. Finally it will add in any `extraErrors` and then call
* via the `onError()` callback unless validation is disabled. Finally, it will add in any `extraErrors` and then call
* back the `onSubmit` callback if it was provided.
*
* @param event - The submit HTML form event
Expand Down Expand Up @@ -725,24 +728,24 @@ export default class Form<
* @returns - True if the form is valid, false otherwise.
*/
validateForm() {
const { extraErrors, focusOnFirstError, onError } = this.props;
const { extraErrors, extraErrorsBlockSubmit, focusOnFirstError, onError } = this.props;
const { formData } = this.state;
const schemaValidation = this.validate(formData);
let errors = schemaValidation.errors;
let errorSchema = schemaValidation.errorSchema;
const schemaValidationErrors = errors;
const schemaValidationErrorSchema = errorSchema;
if (errors.length > 0) {
if (errors.length > 0 || (extraErrors && extraErrorsBlockSubmit)) {
if (extraErrors) {
const merged = validationDataMerge(schemaValidation, extraErrors);
errorSchema = merged.errorSchema;
errors = merged.errors;
}
if (focusOnFirstError) {
if (typeof focusOnFirstError === 'function') {
focusOnFirstError(schemaValidation.errors[0]);
focusOnFirstError(errors[0]);
} else {
this.focusOnError(schemaValidation.errors[0]);
this.focusOnError(errors[0]);
}
}
this.setState(
Expand Down
39 changes: 39 additions & 0 deletions packages/core/test/Form.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2044,6 +2044,45 @@ describeRepeated('Form common', (createFormComponent) => {
sinon.assert.calledOnce(focusOnFirstError);
});

it('should call the onError handler and call the provided focusOnFirstError callback function', () => {
const onError = sandbox.spy();

const focusCallback = () => {
console.log('focusCallback called');
};
const extraErrors = {
__errors: ['foo'],
};

const focusOnFirstError = sandbox.spy(focusCallback);
const { node } = createFormComponent({
schema,
onError,
focusOnFirstError,
extraErrors,
extraErrorsBlockSubmit: true,
});

const input = node.querySelector('input[type=text]');
const focusSpy = sinon.spy();
// Since programmatically triggering focus does not call onFocus, change the focus method to a spy
input.focus = focusSpy;

Simulate.change(input, {
target: { value: 'valid string' },
});
Simulate.submit(node);

sinon.assert.calledWithMatch(
onError,
sinon.match((value) => {
return value.length === 1 && value[0].message === 'foo';
})
);
sinon.assert.notCalled(focusSpy);
sinon.assert.calledOnce(focusOnFirstError);
});

it('should reset errors and errorSchema state to initial state after correction and resubmission', () => {
const { node, onError, onSubmit } = createFormComponent({
schema,
Expand Down
8 changes: 7 additions & 1 deletion packages/docs/docs/api-reference/form-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,13 @@ The value of this prop will be passed to the `enctype` [HTML attribute on the fo

## extraErrors

This prop allows passing in custom errors that are augmented with the existing JSON Schema errors on the form; it can be used to implement asynchronous validation. See [Validation](../usage/validation.md) for more information.
This prop allows passing in custom errors that are augmented with the existing JSON Schema errors on the form; it can be used to implement asynchronous validation.
By default, these are non-blocking errors, meaning that you can still submit the form when these are the only errors displayed to the user.
See [Validation](../usage/validation.md) for more information.

## extraErrorsBlockSubmit

If set to true, causes the `extraErrors` to become blocking when the form is submitted.

## fields

Expand Down

0 comments on commit 4ea75d8

Please sign in to comment.