Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
45 changes: 42 additions & 3 deletions packages/core/src/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import {
ValidatorType,
Experimental_DefaultFormStateBehavior,
Experimental_CustomMergeAllOf,
createErrorHandler,
unwrapErrorHandler,
} from '@rjsf/utils';
import _forEach from 'lodash/forEach';
import _get from 'lodash/get';
Expand Down Expand Up @@ -519,6 +521,22 @@ export default class Form<
return shouldRender(this, nextProps, nextState);
}

/** Gets the previously raised customValidate errors.
*
* @returns the previous customValidate errors
*/
private getPreviousCustomValidateErrors = (): ErrorSchema<T> => {
const { customValidate, uiSchema } = this.props;
const prevFormData = this.state.formData as T;
let customValidateErrors = {};
if (typeof customValidate === 'function') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using the lodash isFunction() method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using typeof === 'function' in multiple places and I see that we never use lodash isFunction. If you think it's an improvement I can change it here and everywhere else.

const errorHandler = customValidate(prevFormData, createErrorHandler<T>(prevFormData), uiSchema);
const userErrorSchema = unwrapErrorHandler<T>(errorHandler);
customValidateErrors = userErrorSchema;
}
return customValidateErrors;
};

/** Validates the `formData` against the `schema` using the `altSchemaUtils` (if provided otherwise it uses the
* `schemaUtils` in the state), returning the results.
*
Expand Down Expand Up @@ -644,18 +662,39 @@ export default class Form<
if (resolvedSchema?.type !== 'object' && resolvedSchema?.type !== 'array') {
filteredErrors.__errors = schemaErrors.__errors;
}

const prevCustomValidateErrors = this.getPreviousCustomValidateErrors();
// Filtering out the previous raised customValidate errors so that they are cleared when no longer valid.
const filterPreviousCustomErrors = (errors: string[] = [], prevCustomErrors: string[]) => {
if (errors.length === 0) {
return errors;
}

return errors.filter((error) => {
return !prevCustomErrors.includes(error);
});
};

// Removing undefined, null and empty errors.
const filterNilOrEmptyErrors = (errors: any): ErrorSchema<T> => {
const filterNilOrEmptyErrors = (errors: any, previousCustomValidateErrors: any = {}): ErrorSchema<T> => {
_forEach(errors, (errorAtKey, errorKey: keyof typeof errors) => {
const prevCustomValidateErrorAtKey = previousCustomValidateErrors[errorKey];
if (_isNil(errorAtKey)) {
delete errors[errorKey];
} else if (
isObject(errorAtKey) &&
isObject(prevCustomValidateErrorAtKey) &&
Array.isArray(prevCustomValidateErrorAtKey?.__errors)
) {
// if previous customValidate error is an object and has __errors array, filter out the errors previous customValidate errors.
errors[errorKey] = filterPreviousCustomErrors(errorAtKey.__errors, prevCustomValidateErrorAtKey.__errors);
} else if (typeof errorAtKey === 'object' && !Array.isArray(errorAtKey.__errors)) {
filterNilOrEmptyErrors(errorAtKey);
filterNilOrEmptyErrors(errorAtKey, previousCustomValidateErrors[errorKey]);
}
});
return errors;
};
return filterNilOrEmptyErrors(filteredErrors);
return filterNilOrEmptyErrors(filteredErrors, prevCustomValidateErrors);
}

/** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the
Expand Down
10 changes: 9 additions & 1 deletion packages/playground/src/components/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
window.alert('Form submitted');
}, []);

const customValidate = (formData: any, errors: any) => {
if (formData['Test'] > formData['Test2']) {
errors['Test']?.addError('Validate error: Test should be LE than Test2');
}
return errors;
};

return (
<>
<Header
Expand Down Expand Up @@ -189,7 +196,7 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
extraErrors={extraErrors}
schema={schema}
uiSchema={uiSchema}
formData={formData}
// formData={formData}
fields={{
geo: GeoPosition,
'/schemas/specialString': SpecialInput,
Expand All @@ -201,6 +208,7 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
onFocus={(id: string, value: string) => console.log(`Focused ${id} with value ${value}`)}
onError={(errorList: RJSFValidationError[]) => console.log('errors', errorList)}
ref={playGroundFormRef}
customValidate={customValidate}
/>
</DemoFrame>
)}
Expand Down
Loading