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

fix: Fixed 3757 by supporting extraErrors that block the submit #3772

Merged
merged 1 commit into from
Jul 14, 2023
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
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