From d9c2c84ba48978021d4bfe2fb6f1ce753e0a5914 Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Thu, 28 Apr 2022 14:39:10 +0100 Subject: [PATCH 1/7] Support allOf blocks within other allOf blocks as described here https://github.com/rjsf-team/react-jsonschema-form/issues/2752 --- packages/core/src/utils.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index c313515eaa..3fd46f58f4 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -767,6 +767,26 @@ export function retrieveSchema(schema, rootSchema = {}, formData = {}) { return resolveCondition(schema, rootSchema, formData); } + if (resolvedSchema.properties) { + var properties = {}; + + Object.entries(resolvedSchema.properties).forEach(entries => { + var propName = entries[0]; + var propSchema = entries[1]; + var propData = formData && formData[propName]; + var resolvedPropSchema = retrieveSchema(propSchema, rootSchema, propData); + + properties[propName] = resolvedPropSchema; + + if ( + propSchema !== resolvedPropSchema && + resolvedSchema.properties !== properties + ) { + resolvedSchema = { ...resolvedSchema, properties }; + } + }); + } + if ("allOf" in schema) { try { resolvedSchema = mergeAllOf({ From 173dd1699ad8f16783cb00612a986073e4d2cbdb Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Fri, 29 Apr 2022 01:42:20 +0100 Subject: [PATCH 2/7] Add test for allOf at multiple levels --- packages/core/src/utils.js | 18 +- packages/core/test/utils_test.js | 276 +++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 6 deletions(-) diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 3fd46f58f4..233554bb0e 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -768,19 +768,25 @@ export function retrieveSchema(schema, rootSchema = {}, formData = {}) { } if (resolvedSchema.properties) { - var properties = {}; + const properties = {}; Object.entries(resolvedSchema.properties).forEach(entries => { - var propName = entries[0]; - var propSchema = entries[1]; - var propData = formData && formData[propName]; - var resolvedPropSchema = retrieveSchema(propSchema, rootSchema, propData); + const propName = entries[0]; + const propSchema = entries[1]; + const rawPropData = formData && formData[propName]; + const propData = isObject(rawPropData) ? rawPropData : {}; + const resolvedPropSchema = retrieveSchema( + propSchema, + rootSchema, + propData + ); properties[propName] = resolvedPropSchema; if ( propSchema !== resolvedPropSchema && - resolvedSchema.properties !== properties + resolvedSchema.properties !== properties && + properties !== null ) { resolvedSchema = { ...resolvedSchema, properties }; } diff --git a/packages/core/test/utils_test.js b/packages/core/test/utils_test.js index a9854c2147..756f4d5f61 100644 --- a/packages/core/test/utils_test.js +++ b/packages/core/test/utils_test.js @@ -2540,6 +2540,282 @@ describe("utils", () => { required: ["animal", "food"], }); }); + it("should resolve multiple conditions in nested allOf blocks", () => { + const schema = { + type: "object", + properties: { + Animal: { + default: "Cat", + enum: ["Cat", "Dog"], + title: "Animal", + type: "string", + }, + }, + allOf: [ + { + if: { + required: ["Animal"], + properties: { + Animal: { + const: "Cat", + }, + }, + }, + then: { + properties: { + Tail: { + default: "Long", + enum: ["Long", "Short", "None"], + title: "Tail length", + type: "string", + }, + }, + required: ["Tail"], + }, + }, + { + if: { + required: ["Animal"], + properties: { + Animal: { + const: "Dog", + }, + }, + }, + then: { + properties: { + Breed: { + title: "Breed", + properties: { + BreedName: { + default: "Alsatian", + enum: ["Alsatian", "Dalmation"], + title: "Breed name", + type: "string", + }, + }, + allOf: [ + { + if: { + required: ["BreedName"], + properties: { + BreedName: { + const: "Alsatian", + }, + }, + }, + then: { + properties: { + Fur: { + default: "brown", + enum: ["black", "brown"], + title: "Fur", + type: "string", + }, + }, + required: ["Fur"], + }, + }, + { + if: { + required: ["BreedName"], + properties: { + BreedName: { + const: "Dalmation", + }, + }, + }, + then: { + properties: { + Spots: { + default: "small", + enum: ["large", "small"], + title: "Spots", + type: "string", + }, + }, + required: ["Spots"], + }, + }, + ], + required: ["BreedName"], + }, + }, + }, + }, + ], + required: ["Animal"], + }; + const definitions = {}; + const formData = { + Animal: "Cat", + Tail: "Long", + }; + + expect(retrieveSchema(schema, { definitions }, formData)).eql({ + type: "object", + properties: { + Animal: { + default: "Cat", + enum: ["Cat", "Dog"], + title: "Animal", + type: "string", + }, + Tail: { + default: "Long", + enum: ["Long", "Short", "None"], + title: "Tail length", + type: "string", + }, + }, + required: ["Animal", "Tail"], + }); + }); + it("should resolve multiple conditions in nested allOf blocks", () => { + const schema = { + type: "object", + properties: { + Animal: { + default: "Cat", + enum: ["Cat", "Dog"], + title: "Animal", + type: "string", + }, + }, + allOf: [ + { + if: { + required: ["Animal"], + properties: { + Animal: { + const: "Cat", + }, + }, + }, + then: { + properties: { + Tail: { + default: "Long", + enum: ["Long", "Short", "None"], + title: "Tail length", + type: "string", + }, + }, + required: ["Tail"], + }, + }, + { + if: { + required: ["Animal"], + properties: { + Animal: { + const: "Dog", + }, + }, + }, + then: { + properties: { + Breed: { + title: "Breed", + properties: { + BreedName: { + default: "Alsatian", + enum: ["Alsatian", "Dalmation"], + title: "Breed name", + type: "string", + }, + }, + allOf: [ + { + if: { + required: ["BreedName"], + properties: { + BreedName: { + const: "Alsatian", + }, + }, + }, + then: { + properties: { + Fur: { + default: "brown", + enum: ["black", "brown"], + title: "Fur", + type: "string", + }, + }, + required: ["Fur"], + }, + }, + { + if: { + required: ["BreedName"], + properties: { + BreedName: { + const: "Dalmation", + }, + }, + }, + then: { + properties: { + Spots: { + default: "small", + enum: ["large", "small"], + title: "Spots", + type: "string", + }, + }, + required: ["Spots"], + }, + }, + ], + required: ["BreedName"], + }, + }, + }, + }, + ], + required: ["Animal"], + }; + const definitions = {}; + const formData = { + Animal: "Dog", + Breed: { + BreedName: "Dalmation", + }, + }; + + expect(retrieveSchema(schema, { definitions }, formData)).eql({ + type: "object", + properties: { + Animal: { + default: "Cat", + enum: ["Cat", "Dog"], + title: "Animal", + type: "string", + }, + Breed: { + properties: { + BreedName: { + default: "Alsatian", + enum: ["Alsatian", "Dalmation"], + title: "Breed name", + type: "string", + }, + Spots: { + default: "small", + enum: ["large", "small"], + title: "Spots", + type: "string", + }, + }, + required: ["BreedName", "Spots"], + title: "Breed", + }, + }, + required: ["Animal"], + }); + }); it("should resolve $ref", () => { const schema = { type: "object", From 4d4a05d0d04b3c315ec6b758302eb2d200bb1f93 Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Fri, 29 Apr 2022 02:23:15 +0100 Subject: [PATCH 3/7] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1382423fcb..ef83ffec72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ should change the heading of the (upcoming) version to include a major version b ## @rjsf/core - Feature for ui:submitButtonOptions on the submit button for forms (https://github.com/rjsf-team/react-jsonschema-form/pull/2640) +- Fix for nested allOf blocks with multiple if/then/else statements failing to render correctly (https://github.com/rjsf-team/react-jsonschema-form/pull/2839) ## Dev / docs / playground - Enable ui options in playground, to demonstrate submit button options (https://github.com/rjsf-team/react-jsonschema-form/pull/2640) From e02560927126e460f9c3dca423ff17f46368e332 Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Fri, 29 Apr 2022 09:39:14 +0100 Subject: [PATCH 4/7] Remove uneeded test --- packages/core/src/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 233554bb0e..7721b3be6d 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -785,8 +785,7 @@ export function retrieveSchema(schema, rootSchema = {}, formData = {}) { if ( propSchema !== resolvedPropSchema && - resolvedSchema.properties !== properties && - properties !== null + resolvedSchema.properties !== properties ) { resolvedSchema = { ...resolvedSchema, properties }; } From 3511d4bd214759d0004e77f53208815abe46cb99 Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Wed, 4 May 2022 10:28:08 +0100 Subject: [PATCH 5/7] Remove unnecessary test --- packages/core/test/utils_test.js | 131 ------------------------------- 1 file changed, 131 deletions(-) diff --git a/packages/core/test/utils_test.js b/packages/core/test/utils_test.js index 756f4d5f61..005e287731 100644 --- a/packages/core/test/utils_test.js +++ b/packages/core/test/utils_test.js @@ -2647,137 +2647,6 @@ describe("utils", () => { required: ["Animal"], }; const definitions = {}; - const formData = { - Animal: "Cat", - Tail: "Long", - }; - - expect(retrieveSchema(schema, { definitions }, formData)).eql({ - type: "object", - properties: { - Animal: { - default: "Cat", - enum: ["Cat", "Dog"], - title: "Animal", - type: "string", - }, - Tail: { - default: "Long", - enum: ["Long", "Short", "None"], - title: "Tail length", - type: "string", - }, - }, - required: ["Animal", "Tail"], - }); - }); - it("should resolve multiple conditions in nested allOf blocks", () => { - const schema = { - type: "object", - properties: { - Animal: { - default: "Cat", - enum: ["Cat", "Dog"], - title: "Animal", - type: "string", - }, - }, - allOf: [ - { - if: { - required: ["Animal"], - properties: { - Animal: { - const: "Cat", - }, - }, - }, - then: { - properties: { - Tail: { - default: "Long", - enum: ["Long", "Short", "None"], - title: "Tail length", - type: "string", - }, - }, - required: ["Tail"], - }, - }, - { - if: { - required: ["Animal"], - properties: { - Animal: { - const: "Dog", - }, - }, - }, - then: { - properties: { - Breed: { - title: "Breed", - properties: { - BreedName: { - default: "Alsatian", - enum: ["Alsatian", "Dalmation"], - title: "Breed name", - type: "string", - }, - }, - allOf: [ - { - if: { - required: ["BreedName"], - properties: { - BreedName: { - const: "Alsatian", - }, - }, - }, - then: { - properties: { - Fur: { - default: "brown", - enum: ["black", "brown"], - title: "Fur", - type: "string", - }, - }, - required: ["Fur"], - }, - }, - { - if: { - required: ["BreedName"], - properties: { - BreedName: { - const: "Dalmation", - }, - }, - }, - then: { - properties: { - Spots: { - default: "small", - enum: ["large", "small"], - title: "Spots", - type: "string", - }, - }, - required: ["Spots"], - }, - }, - ], - required: ["BreedName"], - }, - }, - }, - }, - ], - required: ["Animal"], - }; - const definitions = {}; const formData = { Animal: "Dog", Breed: { From 515ff2eeac7bed81df0fb3d2f42d042d07110e64 Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Wed, 4 May 2022 16:44:58 +0100 Subject: [PATCH 6/7] Add comment for new code block --- packages/core/src/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 7721b3be6d..33bc61f99a 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -767,6 +767,8 @@ export function retrieveSchema(schema, rootSchema = {}, formData = {}) { return resolveCondition(schema, rootSchema, formData); } + // For each level of the dependency, we need to recursively determine the appropriate resolved schema given the current state of formData. + // Otherwise, nested allOf subschemas will not be correctly displayed. if (resolvedSchema.properties) { const properties = {}; From 4c76f16f1eb4025109e3a7f46f4d8847c1fa1dde Mon Sep 17 00:00:00 2001 From: Stuart Wilkes Date: Thu, 5 May 2022 15:01:23 +0100 Subject: [PATCH 7/7] Remove trailing space on comment --- packages/core/src/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 33bc61f99a..28063c7b57 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -767,7 +767,7 @@ export function retrieveSchema(schema, rootSchema = {}, formData = {}) { return resolveCondition(schema, rootSchema, formData); } - // For each level of the dependency, we need to recursively determine the appropriate resolved schema given the current state of formData. + // For each level of the dependency, we need to recursively determine the appropriate resolved schema given the current state of formData. // Otherwise, nested allOf subschemas will not be correctly displayed. if (resolvedSchema.properties) { const properties = {};