Skip to content

Commit

Permalink
feat: add ability to control oneOf/anyOf from any property type (micr…
Browse files Browse the repository at this point in the history
  • Loading branch information
janechu authored Feb 11, 2019
1 parent c7bc125 commit 1c849df
Show file tree
Hide file tree
Showing 41 changed files with 1,290 additions and 701 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"id": "anyOf",
"anyOf": [
{
"additionalProperties": false,
"description": "String",
"type": "object",
"properties": {
Expand All @@ -19,6 +20,7 @@
]
},
{
"additionalProperties": false,
"description": "Number",
"type": "object",
"properties": {
Expand All @@ -32,6 +34,7 @@
]
},
{
"additionalProperties": false,
"type": "object",
"properties": {
"nestedAnyOf": {
Expand Down Expand Up @@ -87,7 +90,63 @@
}
]
}
}
},
"required": [
"nestedAnyOf"
]
},
{
"additionalProperties": false,
"description": "Number or String",
"type": "object",
"properties": {
"numberOrString": {
"anyOf": [
{
"title": "Number",
"type": "number"
},
{
"title": "String",
"type": "string"
},
{
"title": "Array",
"type": "array",
"items": {
"title": "Array item",
"type": "string"
}
},
{
"title": "Array with anyOf in items",
"type": "array",
"items": {
"anyOf": [
{
"additionalProperties": false,
"title": "Array item object",
"type": "object",
"properties": {
"string": {
"title": "Array item object string",
"type": "string"
}
}
},
{
"title": "Array item number",
"type": "number"
}
]
}
}
]
}
},
"required": [
"numberOrString"
]
}
]
}
44 changes: 43 additions & 1 deletion packages/fast-data-utilities-react/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,53 @@ describe("mapSchemaLocationFromDataLocation", () => {
anyOfSchema
);

expect(schemaLocationRootProperty).toBe("anyOf.2.properties.nestedAnyOf");
expect(schemaLocationRootProperty).toBe("anyOf.2.properties.nestedAnyOf.anyOf.1");
expect(schemaLocation).toBe(
"anyOf.2.properties.nestedAnyOf.anyOf.1.properties.string"
);
});
test("should return a schema location from a non-object anyOf/oneOf location", () => {
const schemaLocationNumber: string = mapSchemaLocationFromDataLocation(
"numberOrString",
{ numberOrString: 50 },
anyOfSchema
);
const schemaLocationString: string = mapSchemaLocationFromDataLocation(
"numberOrString",
{ numberOrString: "foo" },
anyOfSchema
);

expect(schemaLocationNumber).toBe("anyOf.3.properties.numberOrString.anyOf.0");
expect(schemaLocationString).toBe("anyOf.3.properties.numberOrString.anyOf.1");
});
test("should return a schema location from a non-object anyOf/oneOf location in an array", () => {
const chemaLocationArrayOfStrings: string = mapSchemaLocationFromDataLocation(
"numberOrString.0",
{ numberOrString: ["Foo"] },
anyOfSchema
);
const schemaLocationArrayOfObjects: string = mapSchemaLocationFromDataLocation(
"numberOrString[0].string",
{ numberOrString: [{ string: "Foo" }] },
anyOfSchema
);
const schemaLocationArrayOfNumbers: string = mapSchemaLocationFromDataLocation(
"numberOrString[0]",
{ numberOrString: [1, 2, 3] },
anyOfSchema
);

expect(chemaLocationArrayOfStrings).toBe(
"anyOf.3.properties.numberOrString.anyOf.2.items"
);
expect(schemaLocationArrayOfObjects).toBe(
"anyOf.3.properties.numberOrString.anyOf.3.items.anyOf.0.properties.string"
);
expect(schemaLocationArrayOfNumbers).toBe(
"anyOf.3.properties.numberOrString.anyOf.3.items.anyOf.1"
);
});
test("should return a schema location from a child location", () => {
const schemaLocation: string = mapSchemaLocationFromDataLocation(
"children",
Expand Down
46 changes: 41 additions & 5 deletions packages/fast-data-utilities-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,24 @@ function getSchemaLocationSegmentsFromDataLocationSegment(
schema: any,
data: any
): string[] {
const schemaLocationSegments: string[] = [];
let schemaLocationSegments: string[] = [];
const normalizedDataLocationForArrayRemoval: string = dataLocation.replace(
/\[\d+\]/g,
squareBracketsRegex,
""
);
const subSchema: any = get(
const childrensSubSchema: any = get(
schema,
`${PropertyKeyword.reactProperties}.${normalizedDataLocationForArrayRemoval}`
);
const isChildren: boolean = subSchema && subSchema.type === childrenKeyword;
const isChildren: boolean =
childrensSubSchema && childrensSubSchema.type === childrenKeyword;
const objectSubSchema: any = get(
schema,
`${PropertyKeyword.properties}.${normalizedDataLocationForArrayRemoval}`
);

if (isPlainObject(data)) {
schemaLocationSegments.push(getObjectPropertyKeyword(subSchema));
schemaLocationSegments.push(getObjectPropertyKeyword(childrensSubSchema));
}

schemaLocationSegments.push(
Expand All @@ -255,12 +260,43 @@ function getSchemaLocationSegmentsFromDataLocationSegment(
// In the case that this is an array and not an array of children,
// add the JSON schema "items" keyword
if (isDataLocationArrayItem(dataLocation) && !isChildren) {
if (hasOneOfOrAnyOf(objectSubSchema)) {
schemaLocationSegments = schemaLocationSegments.concat(
getSchemaOneOfAnyOfLocationSegments(
getPartialData(
normalizeSchemaLocation(schemaLocationSegments.join(".")),
schema
),
getPartialData(dataLocation.replace(squareBracketsRegex, ""), data)
)
);
}

schemaLocationSegments.push("items");
}

if (hasOneOfOrAnyOf(objectSubSchema)) {
schemaLocationSegments = schemaLocationSegments.concat(
getSchemaOneOfAnyOfLocationSegments(
getPartialData(
normalizeSchemaLocation(schemaLocationSegments.join(".")),
schema
),
getPartialData(dataLocation, data)
)
);
}

return schemaLocationSegments;
}

/**
* Determines if a schema has a oneOf or anyOf at root level
*/
function hasOneOfOrAnyOf(schema: any): boolean {
return schema && (schema.oneOf || schema.anyOf);
}

/**
* Get an array of schema location strings from an array of data location strings
*/
Expand Down
6 changes: 3 additions & 3 deletions packages/fast-form-generator-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ Example:
Because the style is optional, you can toggle to add it. The schema form generator will see that color is a required piece of data and use the example given to fill in.
### oneOf & anyOf
The oneOf and anyOf keywords can be used inside a property and at the root level of a schema. This will create a select dropdown so that the user can switch between them. If data has been provided, it will select the first oneOf/anyOf instance it can validate against. The contents of a 'description' property will be used for the contents of the dropdown.
The oneOf and anyOf keywords can be used inside a property and at the root level of a schema. This will create a select dropdown so that the user can switch between them. If data has been provided, it will select the first oneOf/anyOf instance it can validate against. The contents of a 'title' property will be used for the contents of the dropdown.
Example:
```json
Expand All @@ -280,7 +280,7 @@ Example:
"title": "My component",
"oneOf": [
{
"description": "color",
"title": "color",
"type": "object",
"properties": {
"color": {
Expand All @@ -291,7 +291,7 @@ Example:
}
},
{
"description": "text",
"title": "text",
"type": "object",
"properties": {
"text": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@
]
},
{
"title": "Nested anyOf",
"description": "Nested anyOf",
"type": "object",
"additionalProperties": false,
"properties": {
"nestedAnyOf": {
"title": "Nested anyOf",
"description": "Nested anyOf Configuration",
"anyOf": [
{
"description": "Object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"title": "Array with nested children",
"type": "array",
"items": {
"title": "Nested array item",
"type": "object",
"reactProperties": {
"nestedArrayChildren": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
"title": "string",
"type": "string"
}
}
},
"required": [
"string"
]
},
{
"description": "number",
Expand All @@ -23,7 +26,84 @@
"title": "number",
"type": "number"
}
}
},
"required": [
"number"
]
},
{
"title": "Number or string",
"description": "number or string",
"type": "object",
"properties": {
"numberOrString": {
"title": "Number or string configuration",
"oneOf": [
{
"title": "number",
"type": "number"
},
{
"title": "string",
"type": "string"
},
{
"title": "object",
"type": "object",
"properties": {
"object": {
"title": "An object",
"type": "object",
"properties": {
"number": {
"title": "number",
"type": "number"
}
},
"required": [
"number"
]
}
},
"required": [
"object"
]
},
{
"title": "array",
"type": "array",
"items": {
"title": "Array",
"oneOf": [
{
"title": "string item",
"type": "string"
},
{
"title": "number item",
"type": "number"
}
]

}
},
{
"title": "children object",
"type": "object",
"properties": {},
"reactProperties": {
"children": {
"title": "children",
"type": "children"
}
}
}
]
}
},
"required": [
"numberOrString"
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import FormCategory, { FormCategoryProps } from "./form-category";
configure({ adapter: new Adapter() });

const formCategoryProps: FormCategoryProps = {
categoryItem: [<div key="1">foo</div>, <div key="2">bar</div>],
title: "",
id: "Foo",
};

describe("FormCategory", () => {
Expand Down
Loading

0 comments on commit 1c849df

Please sign in to comment.