Skip to content

Commit b1cf91d

Browse files
fix: omitExtraData on submit and on validateForm (#4228)
* fix: omitExtraData on submit and on validateForm * test: update tests for omitExtraData * ci: update changelog * Update CHANGELOG.md Co-authored-by: Heath C <[email protected]> * Apply suggestions from code review Co-authored-by: Heath C <[email protected]> * docs: update changelog --------- Co-authored-by: Heath C <[email protected]>
1 parent 3d851f4 commit b1cf91d

File tree

3 files changed

+155
-29
lines changed

3 files changed

+155
-29
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ should change the heading of the (upcoming) version to include a major version b
2222

2323
- Fix disabled property of options in CheckboxesWidget and RadioWidget ([#4216](https://github.com/rjsf-team/react-jsonschema-form/pull/4216))
2424

25+
## @rjsf/core
26+
27+
- Fixed `omitExtraData` not working in `onSubmit` and `validateForm`; fixing [#4187](https://github.com/rjsf-team/react-jsonschema-form/issues/4187), [#4165](https://github.com/rjsf-team/react-jsonschema-form/issues/4165) and [#4109](https://github.com/rjsf-team/react-jsonschema-form/issues/4109)
28+
2529
# 5.18.5
2630

2731
## @rjsf/antd

packages/core/src/components/Form.tsx

+38-19
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,23 @@ export default class Form<
567567
return getAllPaths(pathSchema);
568568
};
569569

570+
/** Returns the `formData` after filtering to remove any extra data not in a form field
571+
*
572+
* @param formData - The data for the `Form`
573+
* @returns The `formData` after omitting extra data
574+
*/
575+
omitExtraData = (formData?: T): T | undefined => {
576+
const { schema, schemaUtils } = this.state;
577+
const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
578+
const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData);
579+
const fieldNames = this.getFieldNames(pathSchema, formData);
580+
const newFormData = this.getUsedFormData(formData, fieldNames);
581+
return newFormData;
582+
};
583+
570584
/** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the
571585
* `formData` along with a new `ErrorSchema`. It will first update the `formData` with any missing default fields and
572-
* then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filterer to remove any extra data not
586+
* then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not
573587
* in a form field. Then, the resulting formData will be validated if required. The state will be updated with the new
574588
* updated (potentially filtered) `formData`, any errors that resulted from validation. Finally the `onChange`
575589
* callback will be called if specified with the updated state.
@@ -593,12 +607,7 @@ export default class Form<
593607

594608
let _retrievedSchema: S | undefined;
595609
if (omitExtraData === true && liveOmit === true) {
596-
_retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
597-
const pathSchema = schemaUtils.toPathSchema(_retrievedSchema, '', formData);
598-
599-
const fieldNames = this.getFieldNames(pathSchema, formData);
600-
601-
newFormData = this.getUsedFormData(formData, fieldNames);
610+
newFormData = this.omitExtraData(formData);
602611
state = {
603612
formData: newFormData,
604613
};
@@ -702,18 +711,12 @@ export default class Form<
702711
event.persist();
703712
const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;
704713
let { formData: newFormData } = this.state;
705-
const { schema, schemaUtils } = this.state;
706714

707715
if (omitExtraData === true) {
708-
const retrievedSchema = schemaUtils.retrieveSchema(schema, newFormData);
709-
const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', newFormData);
710-
711-
const fieldNames = this.getFieldNames(pathSchema, newFormData);
712-
713-
newFormData = this.getUsedFormData(newFormData, fieldNames);
716+
newFormData = this.omitExtraData(newFormData);
714717
}
715718

716-
if (noValidate || this.validateForm()) {
719+
if (noValidate || this.validateFormWithFormData(newFormData)) {
717720
// There are no errors generated through schema validation.
718721
// Check for user provided errors and update state accordingly.
719722
const errorSchema = extraErrors || {};
@@ -804,14 +807,15 @@ export default class Form<
804807
}
805808
}
806809

807-
/** Programmatically validate the form. If `onError` is provided, then it will be called with the list of errors the
808-
* same way as would happen on form submission.
810+
/** Validates the form using the given `formData`. For use on form submission or on programmatic validation.
811+
* If `onError` is provided, then it will be called with the list of errors.
809812
*
813+
* @param formData - The form data to validate
810814
* @returns - True if the form is valid, false otherwise.
811815
*/
812-
validateForm() {
816+
validateFormWithFormData = (formData?: T): boolean => {
813817
const { extraErrors, extraErrorsBlockSubmit, focusOnFirstError, onError } = this.props;
814-
const { formData, errors: prevErrors } = this.state;
818+
const { errors: prevErrors } = this.state;
815819
const schemaValidation = this.validate(formData);
816820
let errors = schemaValidation.errors;
817821
let errorSchema = schemaValidation.errorSchema;
@@ -855,6 +859,21 @@ export default class Form<
855859
});
856860
}
857861
return !hasError;
862+
};
863+
864+
/** Programmatically validate the form. If `omitExtraData` is true, the `formData` will first be filtered to remove
865+
* any extra data not in a form field. If `onError` is provided, then it will be called with the list of errors the
866+
* same way as would happen on form submission.
867+
*
868+
* @returns - True if the form is valid, false otherwise.
869+
*/
870+
validateForm() {
871+
const { omitExtraData } = this.props;
872+
let { formData: newFormData } = this.state;
873+
if (omitExtraData === true) {
874+
newFormData = this.omitExtraData(newFormData);
875+
}
876+
return this.validateFormWithFormData(newFormData);
858877
}
859878

860879
/** Renders the `Form` fields inside the <form> | `tagName` or `_internalFormWrapper`, rendering any errors if

packages/core/test/Form.test.jsx

+113-10
Original file line numberDiff line numberDiff line change
@@ -3291,7 +3291,7 @@ describe('Form omitExtraData and liveOmit', () => {
32913291
sandbox.restore();
32923292
});
32933293

3294-
it('should call getUsedFormData when the omitExtraData prop is true and liveOmit is true', () => {
3294+
it('should call omitExtraData when the omitExtraData prop is true and liveOmit is true', () => {
32953295
const schema = {
32963296
type: 'object',
32973297
properties: {
@@ -3316,18 +3316,18 @@ describe('Form omitExtraData and liveOmit', () => {
33163316
liveOmit,
33173317
});
33183318

3319-
sandbox.stub(comp.ref.current, 'getUsedFormData').returns({
3319+
sandbox.stub(comp.ref.current, 'omitExtraData').returns({
33203320
foo: '',
33213321
});
33223322

33233323
fireEvent.change(node.querySelector('[type=text]'), {
33243324
target: { value: 'new' },
33253325
});
33263326

3327-
sinon.assert.calledOnce(comp.ref.current.getUsedFormData);
3327+
sinon.assert.calledOnce(comp.ref.current.omitExtraData);
33283328
});
33293329

3330-
it('should not call getUsedFormData when the omitExtraData prop is true and liveOmit is unspecified', () => {
3330+
it('should not call omitExtraData when the omitExtraData prop is true and liveOmit is unspecified', () => {
33313331
const schema = {
33323332
type: 'object',
33333333
properties: {
@@ -3349,19 +3349,19 @@ describe('Form omitExtraData and liveOmit', () => {
33493349
omitExtraData,
33503350
});
33513351

3352-
sandbox.stub(comp.ref.current, 'getUsedFormData').returns({
3352+
sandbox.stub(comp.ref.current, 'omitExtraData').returns({
33533353
foo: '',
33543354
});
33553355

33563356
fireEvent.change(node.querySelector('[type=text]'), {
33573357
target: { value: 'new' },
33583358
});
33593359

3360-
sinon.assert.notCalled(comp.ref.current.getUsedFormData);
3360+
sinon.assert.notCalled(comp.ref.current.omitExtraData);
33613361
});
33623362

3363-
describe('getUsedFormData', () => {
3364-
it('should call getUsedFormData when the omitExtraData prop is true', () => {
3363+
describe('omitExtraData on submit', () => {
3364+
it('should call omitExtraData when the omitExtraData prop is true', () => {
33653365
const schema = {
33663366
type: 'object',
33673367
properties: {
@@ -3385,14 +3385,65 @@ describe('Form omitExtraData and liveOmit', () => {
33853385
omitExtraData,
33863386
});
33873387

3388-
sandbox.stub(comp.ref.current, 'getUsedFormData').returns({
3388+
sandbox.stub(comp.ref.current, 'omitExtraData').returns({
33893389
foo: '',
33903390
});
33913391

33923392
fireEvent.submit(node);
33933393

3394-
sinon.assert.calledOnce(comp.ref.current.getUsedFormData);
3394+
sinon.assert.calledOnce(comp.ref.current.omitExtraData);
3395+
});
3396+
3397+
it('Should call validateFormWithFormData with the current formData if omitExtraData is false', () => {
3398+
const omitExtraData = false;
3399+
const schema = {
3400+
type: 'object',
3401+
properties: {
3402+
foo: { type: 'string' },
3403+
},
3404+
};
3405+
const formData = { foo: 'bar', baz: 'baz' };
3406+
const formRef = React.createRef();
3407+
const props = {
3408+
ref: formRef,
3409+
schema,
3410+
formData,
3411+
omitExtraData: omitExtraData,
3412+
};
3413+
const { comp, node } = createFormComponent(props);
3414+
sandbox.stub(comp.ref.current, 'validateFormWithFormData').returns({
3415+
foo: '',
3416+
});
3417+
fireEvent.submit(node);
3418+
sinon.assert.calledWithMatch(comp.ref.current.validateFormWithFormData, formData);
33953419
});
3420+
3421+
it('Should call validateFormWithFormData with a new formData with only used fields if omitExtraData is true', () => {
3422+
const omitExtraData = true;
3423+
const schema = {
3424+
type: 'object',
3425+
properties: {
3426+
foo: { type: 'string' },
3427+
},
3428+
};
3429+
const formData = { foo: 'bar', baz: 'baz' };
3430+
const formRef = React.createRef();
3431+
const props = {
3432+
ref: formRef,
3433+
schema,
3434+
formData,
3435+
omitExtraData: omitExtraData,
3436+
};
3437+
const { comp, node } = createFormComponent(props);
3438+
sandbox.stub(comp.ref.current, 'validateFormWithFormData').returns({
3439+
foo: '',
3440+
});
3441+
fireEvent.submit(node);
3442+
sinon.assert.calledWithMatch(comp.ref.current.validateFormWithFormData, { foo: 'bar' });
3443+
});
3444+
});
3445+
3446+
describe('getUsedFormData', () => {
33963447
it('should just return the single input form value', () => {
33973448
const schema = {
33983449
title: 'A single-field form',
@@ -4352,6 +4403,58 @@ describe('Form omitExtraData and liveOmit', () => {
43524403
});
43534404

43544405
describe('validateForm()', () => {
4406+
it('Should call validateFormWithFormData with the current formData if omitExtraData is false', () => {
4407+
const omitExtraData = false;
4408+
const schema = {
4409+
type: 'object',
4410+
properties: {
4411+
foo: { type: 'string' },
4412+
},
4413+
};
4414+
const formData = { foo: 'bar', baz: 'baz' };
4415+
const formRef = React.createRef();
4416+
const props = {
4417+
ref: formRef,
4418+
schema,
4419+
formData,
4420+
omitExtraData: omitExtraData,
4421+
};
4422+
const { comp } = createFormComponent(props);
4423+
sandbox.stub(comp.ref.current, 'validateFormWithFormData').returns({
4424+
foo: '',
4425+
});
4426+
act(() => {
4427+
comp.ref.current.validateForm();
4428+
});
4429+
sinon.assert.calledWithMatch(comp.ref.current.validateFormWithFormData, formData);
4430+
});
4431+
4432+
it('Should call validateFormWithFormData with a new formData with only used fields if omitExtraData is true', () => {
4433+
const omitExtraData = true;
4434+
const schema = {
4435+
type: 'object',
4436+
properties: {
4437+
foo: { type: 'string' },
4438+
},
4439+
};
4440+
const formData = { foo: 'bar', baz: 'baz' };
4441+
const formRef = React.createRef();
4442+
const props = {
4443+
ref: formRef,
4444+
schema,
4445+
formData,
4446+
omitExtraData: omitExtraData,
4447+
};
4448+
const { comp } = createFormComponent(props);
4449+
sandbox.stub(comp.ref.current, 'validateFormWithFormData').returns({
4450+
foo: '',
4451+
});
4452+
act(() => {
4453+
comp.ref.current.validateForm();
4454+
});
4455+
sinon.assert.calledWithMatch(comp.ref.current.validateFormWithFormData, { foo: 'bar' });
4456+
});
4457+
43554458
it('Should update state when data updated from invalid to valid', () => {
43564459
const ref = createRef();
43574460
const props = {

0 commit comments

Comments
 (0)