Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 12 additions & 1 deletion src/components/fields/MultiSchemaField.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ class AnyOfField extends Component {
augmentedSchema = Object.assign({}, option, requiresAnyOf);
}

// Remove the "required" field as it's likely that not all fields have
// been filled in yet, which will mean that the schema is not valid
delete augmentedSchema.required;

if (isValid(augmentedSchema, formData)) {
return i;
}
Expand All @@ -85,7 +89,14 @@ class AnyOfField extends Component {
const selectedOption = parseInt(event.target.value, 10);
const { formData, onChange, options } = this.props;

if (guessType(formData) === "object") {
const newOption = options[selectedOption];

// If the new option is of type object and the current data is an object,
// discard properties added using the old option.
if (
guessType(formData) === "object" &&
(newOption.type === "object" || newOption.properties)
) {
Comment thread
epicfaace marked this conversation as resolved.
const newFormData = Object.assign({}, formData);

const optionsToDiscard = options.slice();
Expand Down
2 changes: 1 addition & 1 deletion src/components/fields/ObjectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ class ObjectField extends Component {
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
idPrefix={idPrefix}
formData={formData[name]}
formData={(formData || {})[name]}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In what case(s) would formData be null?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

formData can be null if you have just added an item to an array

onKeyChange={this.onKeyChange(name)}
onChange={this.onPropertyChange(
name,
Expand Down
4 changes: 3 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,9 @@ export function toIdSchema(
field,
fieldId,
definitions,
formData[name],
// It's possible that formData is not an object -- this can happen if an
// array item has just been added, but not populated with data yet
(formData || {})[name],
idPrefix
);
}
Expand Down
46 changes: 46 additions & 0 deletions test/anyOf_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,5 +571,51 @@ describe("anyOf", () => {

expect(node.querySelectorAll("input#root_foo")).to.have.length.of(1);
});

it("should correctly render mixed types for anyOf inside array items", () => {
const schema = {
type: "object",
properties: {
items: {
type: "array",
items: {
anyOf: [
{
type: "string",
},
{
type: "object",
properties: {
foo: {
type: "integer",
},
bar: {
type: "string",
},
},
},
],
},
},
},
};

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 },
});

expect(node.querySelectorAll("input#root_foo")).to.have.length.of(1);
expect(node.querySelectorAll("input#root_bar")).to.have.length.of(1);
});
});
});
135 changes: 135 additions & 0 deletions test/oneOf_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,139 @@ describe("oneOf", () => {

expect(node.querySelector("select").value).eql("1");
});

it("should not change the selected option when entering values on a subschema with multiple required options", () => {
const schema = {
type: "object",
properties: {
items: {
oneOf: [
{
type: "string",
},
{
type: "object",
properties: {
foo: {
type: "integer",
},
bar: {
type: "string",
},
},
required: ["foo", "bar"],
},
],
},
},
};

const { node } = createFormComponent({
schema,
});

const $select = node.querySelector("select");

expect($select.value).eql("0");

Simulate.change($select, {
target: { value: $select.options[1].value },
});

expect($select.value).eql("1");

Simulate.change(node.querySelector("input#root_bar"), {
target: { value: "Lorem ipsum dolor sit amet" },
});

expect($select.value).eql("1");
});

it("should empty the form data when switching from an option of type 'object'", () => {
const schema = {
oneOf: [
{
type: "object",
properties: {
foo: {
type: "integer",
},
bar: {
type: "string",
},
},
required: ["foo", "bar"],
},
{
type: "string",
},
],
};

const { node } = createFormComponent({
schema,
formData: {
foo: 1,
bar: "abc",
},
});

const $select = node.querySelector("select");

Simulate.change($select, {
target: { value: $select.options[1].value },
});

expect($select.value).eql("1");

expect(node.querySelector("input#root").value).eql("");
});

describe("Arrays", () => {
it("should correctly render mixed types for oneOf inside array items", () => {
const schema = {
type: "object",
properties: {
items: {
type: "array",
items: {
oneOf: [
{
type: "string",
},
{
type: "object",
properties: {
foo: {
type: "integer",
},
bar: {
type: "string",
},
},
},
],
},
},
},
};

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 },
});

expect(node.querySelectorAll("input#root_foo")).to.have.length.of(1);
expect(node.querySelectorAll("input#root_bar")).to.have.length.of(1);
});
});
});
76 changes: 76 additions & 0 deletions test/utils_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,64 @@ describe("utils", () => {
});
});

it("should return an idSchema for nested property dependencies", () => {
const schema = {
type: "object",
properties: {
obj: {
type: "object",
properties: {
foo: { type: "string" },
},
dependencies: {
foo: {
properties: {
bar: { type: "string" },
},
},
},
},
},
};
const formData = {
obj: {
foo: "test",
},
};

expect(toIdSchema(schema, undefined, schema.definitions, formData)).eql({
$id: "root",
obj: {
$id: "root_obj",
foo: { $id: "root_obj_foo" },
bar: { $id: "root_obj_bar" },
},
});
});

it("should return an idSchema for unmet property dependencies", () => {
const schema = {
type: "object",
properties: {
foo: { type: "string" },
},
dependencies: {
foo: {
properties: {
bar: { type: "string" },
},
},
},
};

const formData = {};

expect(toIdSchema(schema, undefined, schema.definitions, formData)).eql({
$id: "root",
foo: { $id: "root_foo" },
});
});

it("should handle idPrefix parameter", () => {
const schema = {
definitions: {
Expand All @@ -1205,6 +1263,24 @@ describe("utils", () => {
}
);
});

it("should handle null form data for object schemas", () => {
const schema = {
type: "object",
properties: {
foo: { type: "string" },
bar: { type: "string" },
},
};
const formData = null;
const result = toIdSchema(schema, null, {}, formData, "rjsf");

expect(result).eql({
$id: "rjsf",
foo: { $id: "rjsf_foo" },
bar: { $id: "rjsf_bar" },
});
});
});

describe("parseDateString()", () => {
Expand Down