Skip to content

Commit

Permalink
Merge pull request #1 from datacamp-engineering/jdmunro/fix-anyof-bug
Browse files Browse the repository at this point in the history
[LO-1769] Fix default option for anyOf fields
  • Loading branch information
jdmunro authored Dec 2, 2020
2 parents b9cb280 + eb810a8 commit 820d36e
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 2 deletions.
24 changes: 22 additions & 2 deletions packages/core/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,26 @@ export function rangeSpec(schema) {
return spec;
}

function recursivelyRemoveRequiredFromSchema(schema) {
const copyOfSchema = { ...schema };
const copyOfProperties = { ...schema.properties };
const propertyKeys = Object.keys(copyOfProperties);

delete copyOfSchema.required;

for (const key of propertyKeys) {
copyOfProperties[key] = { ...copyOfProperties[key] };
delete copyOfProperties[key].required;
if (copyOfProperties[key].items != null) {
copyOfProperties[key].items = recursivelyRemoveRequiredFromSchema(
copyOfProperties[key].items
);
}
}

return { ...copyOfSchema, properties: copyOfProperties };
}

export function getMatchingOption(formData, options, rootSchema) {
for (let i = 0; i < options.length; i++) {
const option = options[i];
Expand Down Expand Up @@ -1199,9 +1219,9 @@ export function getMatchingOption(formData, options, rootSchema) {
augmentedSchema = Object.assign({}, option, requiresAnyOf);
}

// Remove the "required" field as it's likely that not all fields have
// Remove the "required" fields 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;
augmentedSchema = recursivelyRemoveRequiredFromSchema(augmentedSchema);

if (isValid(augmentedSchema, formData)) {
return i;
Expand Down
170 changes: 170 additions & 0 deletions packages/core/test/utils_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
mergeSchemas,
getDisplayLabel,
schemaRequiresTrueValue,
getMatchingOption,
} from "../src/utils";
import { createSandbox } from "./test_utils";

Expand Down Expand Up @@ -3714,4 +3715,173 @@ describe("utils", () => {
expect(schemaRequiresTrueValue({ type: "string" })).eql(false);
});
});

describe("getMatchingOption", () => {
describe("basic example", () => {
const options = Object.freeze([
{
type: "object",
properties: {
type: {
type: "string",
enum: ["One"],
default: "One",
},
},
required: ["type"],
},
{
type: "object",
properties: {
type: {
type: "string",
enum: ["Two"],
default: "Two",
},
},
required: ["type"],
},
]);

it("returns undefined (default option) if nothing matches the schema", () => {
const data = { type: "Three" };

expect(getMatchingOption(data, options)).eql(undefined);
});

it("returns the expected option if it matches the schema", () => {
const data = { type: "Two" };

expect(getMatchingOption(data, options)).eql(1);
});
});

describe("partial form data", () => {
it("returns the expected option for a partial match", () => {
const options = Object.freeze([
{
type: "object",
properties: {
type: {
type: "string",
enum: ["One"],
default: "One",
},
extraType: {
type: "string",
enum: ["extra"],
default: "extra",
},
},
required: ["type", "extraType"],
},
]);
const data = { type: "One" };

expect(getMatchingOption(data, options)).eql(0);
});

it("disregards nested required properties", () => {
const options = Object.freeze([
{
type: "object",
properties: {
type: {
type: "string",
enum: ["One"],
default: "One",
},
extraObject: {
type: "object",
properties: {
extraProperty: {
type: "string",
},
},
required: ["extraProperty"],
},
},
required: ["type", "extraObject"],
},
]);
const data = { type: "One", extraObject: {} };

expect(getMatchingOption(data, options)).eql(0);
});

it("disregards nested required properties (array)", () => {
const options = Object.freeze([
{
type: "object",
properties: {
type: {
type: "string",
enum: ["One"],
default: "One",
},
extraArray: {
type: "array",
items: {
type: "object",
properties: {
extraProperty: {
type: "string",
},
},
required: ["extraProperty"],
},
required: ["items"],
},
},
required: ["type", "extraArray"],
},
]);
const data = { type: "One", extraArray: [] };

expect(getMatchingOption(data, options)).eql(0);
});

it("disregards nested required properties (recursive array)", () => {
const options = Object.freeze([
{
type: "object",
properties: {
type: {
type: "string",
enum: ["One"],
default: "One",
},
extraArray: {
type: "array",
items: {
type: "object",
properties: {
nestedArray: {
type: "array",
items: {
type: "object",
properties: {
nestedProperty: {
type: "string",
},
},
required: ["nestedProperty"],
},
required: ["items"],
},
},
required: ["nestedArray"],
},
required: ["items"],
},
},
required: ["type", "extraArray"],
},
]);
const data = { type: "One", extraArray: [{ nestedArray: [{}] }] };

expect(getMatchingOption(data, options)).eql(0);
});
});
});
});

0 comments on commit 820d36e

Please sign in to comment.