From b703c3aa5ee371d60a63deb588eada53cc629b7b Mon Sep 17 00:00:00 2001 From: Lucian Date: Fri, 17 May 2019 13:51:44 +0100 Subject: [PATCH 1/2] Correctly use $ref when matching options with oneOf Fixes #1173 Additionally, if a matching option isn't found, preserve the currently selected option Change-type: patch Signed-off-by: Lucian --- src/components/fields/MultiSchemaField.js | 32 +++++++++--- test/oneOf_test.js | 62 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/components/fields/MultiSchemaField.js b/src/components/fields/MultiSchemaField.js index 6ecaa82761..a8609b730c 100644 --- a/src/components/fields/MultiSchemaField.js +++ b/src/components/fields/MultiSchemaField.js @@ -1,7 +1,12 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import * as types from "../../types"; -import { getUiOptions, getWidget, guessType } from "../../utils"; +import { + getUiOptions, + getWidget, + guessType, + retrieveSchema, +} from "../../utils"; import { isValid } from "../../validate"; class AnyOfField extends Component { @@ -29,8 +34,16 @@ class AnyOfField extends Component { } getMatchingOption(formData, options) { + const { definitions } = this.props.registry; for (let i = 0; i < options.length; i++) { - const option = options[i]; + // Assign the definitions to the option, otherwise the match can fail if + // the new option uses a $ref + const option = Object.assign( + { + definitions, + }, + options[i] + ); // If the schema describes an object then we need to add slightly more // strict matching to the schema, because unless the schema uses the @@ -81,15 +94,20 @@ class AnyOfField extends Component { } } - // If the form data matches none of the options, use the first option - return 0; + // If the form data matches none of the options, use the currently selected + // option, assuming its available, otherwise use the first option + return this && this.state ? this.state.selectedOption : 0; } onOptionChange = option => { const selectedOption = parseInt(option, 10); - const { formData, onChange, options } = this.props; - - const newOption = options[selectedOption]; + const { formData, onChange, options, registry } = this.props; + const { definitions } = registry; + const newOption = retrieveSchema( + options[selectedOption], + definitions, + formData + ); // If the new option is of type object and the current data is an object, // discard properties added using the old option. diff --git a/test/oneOf_test.js b/test/oneOf_test.js index 98ba91ed6d..3fa952faf2 100644 --- a/test/oneOf_test.js +++ b/test/oneOf_test.js @@ -464,4 +464,66 @@ describe("oneOf", () => { expect(node.querySelectorAll("input#root_bar")).to.have.length.of(1); }); }); + + describe("definitions", () => { + it("should handle the $ref keyword correctly", () => { + const schema = { + definitions: { + fieldEither: { + type: "object", + oneOf: [ + { + type: "object", + properties: { + value: { + type: "string", + }, + }, + }, + { + type: "object", + properties: { + value: { + type: "array", + items: { + $ref: "#/definitions/fieldEither", + }, + }, + }, + }, + ], + }, + }, + type: "object", + properties: { + value: { + type: "array", + items: { + $ref: "#/definitions/fieldEither", + }, + }, + }, + }; + + const { node } = createFormComponent({ + schema, + }); + + expect(node.querySelector(".array-item-add button")).not.eql(null); + + Simulate.click(node.querySelector(".array-item-add button")); + + const $select = node.querySelector("select"); + expect($select).not.eql(null); + Simulate.change($select, { + target: { value: $select.options[1].value }, + }); + + // This works because the nested "add" button will now be the first to + // appear in the dom + Simulate.click(node.querySelector(".array-item-add button")); + + expect($select.value).to.eql($select.options[1].value); + }); + }); }); From 67b8cf5a30788159703dc646026339363faa6a47 Mon Sep 17 00:00:00 2001 From: Ashwin Ramaswami Date: Sun, 19 May 2019 12:25:34 -0700 Subject: [PATCH 2/2] Update src/components/fields/MultiSchemaField.js --- src/components/fields/MultiSchemaField.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/fields/MultiSchemaField.js b/src/components/fields/MultiSchemaField.js index a8609b730c..44401dc06a 100644 --- a/src/components/fields/MultiSchemaField.js +++ b/src/components/fields/MultiSchemaField.js @@ -95,7 +95,7 @@ class AnyOfField extends Component { } // If the form data matches none of the options, use the currently selected - // option, assuming its available, otherwise use the first option + // option, assuming it's available; otherwise use the first option return this && this.state ? this.state.selectedOption : 0; }