diff --git a/CHANGELOG.md b/CHANGELOG.md index 0735abc61f..2ec580441b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,18 @@ should change the heading of the (upcoming) version to include a major version b --> +# 5.22.2 + +## @rjsf/core + +- Fixed validation regression Form not revalidating after formData change, fixing [#4343](https://github.com/rjsf-team/react-jsonschema-form/issues/4343) + +# 5.22.1 + +## Dev / docs / playground + +- Bumped peer dependencies to 5.22.x due to updated type definition and API changes in @rjsf/utils + # 5.22.0 ## @rjsf/core diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index d9ea45529d..7bc39f6c83 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -441,9 +441,9 @@ export default class Form< if (mustValidate) { const schemaValidation = this.validate(formData, schema, schemaUtils, _retrievedSchema); errors = schemaValidation.errors; - // If the schema has changed, we do not merge state.errorSchema. + // If retrievedSchema is undefined which means the schema or formData has changed, we do not merge state. // Else in the case where it hasn't changed, we merge 'state.errorSchema' with 'schemaValidation.errorSchema.' This done to display the raised field error. - if (isSchemaChanged) { + if (retrievedSchema === undefined) { errorSchema = schemaValidation.errorSchema; } else { errorSchema = mergeObjects( diff --git a/packages/core/test/ArrayField.test.jsx b/packages/core/test/ArrayField.test.jsx index c80a65ee32..7dec35974a 100644 --- a/packages/core/test/ArrayField.test.jsx +++ b/packages/core/test/ArrayField.test.jsx @@ -3219,6 +3219,27 @@ describe('ArrayField', () => { }); }); + it('Check that when formData changes, the form should re-validate', () => { + const { node, rerender } = createFormComponent({ + schema, + formData: [ + { + text: null, + }, + ], + liveValidate: true, + }); + + const errorMessages = node.querySelectorAll('#root_0_text__error'); + expect(errorMessages).to.have.length(1); + const errorMessageContent = node.querySelector('#root_0_text__error .text-danger').textContent; + expect(errorMessageContent).to.contain('must be string'); + + rerender({ schema, formData: [{ text: 'test' }], liveValidate: true }); + + expect(node.querySelectorAll('#root_0_text__error')).to.have.length(0); + }); + it('raise an error and check if the error is displayed', () => { const { node } = createFormComponent({ schema, diff --git a/packages/core/test/ObjectField.test.jsx b/packages/core/test/ObjectField.test.jsx index 89a83d9b63..fc0cfd08ab 100644 --- a/packages/core/test/ObjectField.test.jsx +++ b/packages/core/test/ObjectField.test.jsx @@ -227,6 +227,25 @@ describe('ObjectField', () => { }); }); + it('Check that when formData changes, the form should re-validate', () => { + const { node, rerender } = createFormComponent({ + schema, + formData: { + foo: null, + }, + liveValidate: true, + }); + + const errorMessages = node.querySelectorAll('#root_foo__error'); + expect(errorMessages).to.have.length(1); + const errorMessageContent = node.querySelector('#root_foo__error .text-danger').textContent; + expect(errorMessageContent).to.contain('must be string'); + + rerender({ schema, formData: { foo: 'test' }, liveValidate: true }); + + expect(node.querySelectorAll('#root_foo__error')).to.have.length(0); + }); + it('raise an error and check if the error is displayed', () => { const { node } = createFormComponent({ schema, diff --git a/packages/core/test/StringField.test.jsx b/packages/core/test/StringField.test.jsx index f14d195d67..3449ab1278 100644 --- a/packages/core/test/StringField.test.jsx +++ b/packages/core/test/StringField.test.jsx @@ -299,6 +299,23 @@ describe('StringField', () => { expect(node.querySelector('input').getAttribute('autocomplete')).eql('family-name'); }); + it('Check that when formData changes, the form should re-validate', () => { + const { node, rerender } = createFormComponent({ + schema: { type: 'string' }, + formData: null, + liveValidate: true, + }); + + const errorMessages = node.querySelectorAll('#root__error'); + expect(errorMessages).to.have.length(1); + const errorMessageContent = node.querySelector('#root__error .text-danger').textContent; + expect(errorMessageContent).to.contain('must be string'); + + rerender({ schema: { type: 'string' }, formData: 'hello', liveValidate: true }); + + expect(node.querySelectorAll('#root__error')).to.have.length(0); + }); + it('raise an error and check if the error is displayed', () => { const { node } = createFormComponent({ schema: { type: 'string' }, diff --git a/packages/core/test/test_utils.js b/packages/core/test/test_utils.js index 3db1807bf2..b859221db5 100644 --- a/packages/core/test/test_utils.js +++ b/packages/core/test/test_utils.js @@ -14,10 +14,13 @@ export function createComponent(Component, props) { const onError = sinon.spy(); const onSubmit = sinon.spy(); const comp = ; - const { container } = render(comp); + const { container, rerender } = render(comp); + + const rerenderFunction = (props) => + rerender(); const node = findDOMNode(container).firstElementChild; - return { comp, node, onChange, onError, onSubmit }; + return { comp, node, onChange, onError, onSubmit, rerender: rerenderFunction }; } export function createFormComponent(props) {