@nolwenture/formik-jsonschema-form 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -786,6 +786,57 @@ Schemas can be referenced. A referenced schema can be given to a property using
786
786
 
787
787
  In order to give uiSchema to reused or recursive fields defined in `schema.definitons`, the uiSchema needs to be given to the matching prop of `definitions`-object. This way, the given uiProps are added to the referencing field, no matter how deeply nested it is inside the schema.
788
788
 
789
+ The schema of the field and the referenced schema are merged, meaning that aspects of the reused schema can be adapted:
790
+
791
+ ```tsx
792
+ const schema: Schema = {
793
+ type: "object",
794
+ properties: {
795
+ address: {
796
+ $ref: "#/definitions/address",
797
+ required: ["postcode"],
798
+ default: { country: "US" },
799
+ properties: {
800
+ apartmentNumber: { type: "string", title: "Apartment number" },
801
+ },
802
+ },
803
+ },
804
+ definitions: {
805
+ address: {
806
+ type: "object",
807
+ title: "Address",
808
+ required: ["country", "city", "street"],
809
+ properties: {
810
+ country: { type: "string", title: "Country" },
811
+ city: { type: "string", title: "City" },
812
+ street: { type: "string", title: "Street" },
813
+ postcode: { type: "string", title: "Postcode" },
814
+ },
815
+ },
816
+ },
817
+ };
818
+ ```
819
+
820
+ The resulting schema of the `address` field:
821
+
822
+ ```tsx
823
+ {
824
+ type: "object",
825
+ title: "Address",
826
+ required: ["country", "city", "street", "postcode"],
827
+ default: { country: "US" },
828
+ properties: {
829
+ country: { type: "string", title: "Country" },
830
+ city: { type: "string", title: "City" },
831
+ street: { type: "string", title: "Street" },
832
+ postcode: { type: "string", title: "Postcode" },
833
+ apartmentNumber: { type: "string", title: "Apartment number" },
834
+ },
835
+ }
836
+ ```
837
+
838
+ If aspects of the `uiSchema` need to be adapted, `uiSchema.definitions` of that field can be overridden inside `uiSchema` by giving the desired properties to the respective field key. In the following schema/uiSchema `petBirthDate` can be used as an example. The settings are overriding the title of the field from the original "When is your birthday?" to "When is the birthday of your pet?".
839
+
789
840
  ```tsx
790
841
  const familyObjectField: Schema = {
791
842
  type: "object",
@@ -822,6 +873,8 @@ const refDefSchema: Schema = {
822
873
  },
823
874
  },
824
875
  },
876
+ pet: { type: "string", title: "Pet name" },
877
+ petBirthDate: { $ref: "#/definitions/birthDate" },
825
878
  },
826
879
 
827
880
  definitions: {
@@ -846,6 +899,7 @@ const refDefSchema: Schema = {
846
899
  };
847
900
 
848
901
  const refDefUiSchema: UiSchema = {
902
+ petBirthDate: { "ui:title": "When is the birthday of your pet?" },
849
903
  definitions: {
850
904
  birthDate: { "ui:field": "DateField" },
851
905
  names: {
@@ -98,7 +98,10 @@ export const ArrayField = (props) => {
98
98
  // Backwards compatibility ends
99
99
  const { disabled } = useExtractUiSchema(uiSchema);
100
100
  const valueArray = Array.isArray(value) ? value : undefined;
101
- return schema.anyOf !== undefined ? (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", multiple: (enums === null || enums === void 0 ? void 0 : enums.type) === "multiSelect", children: enums === null || enums === void 0 ? void 0 : enums.values.map((option, index) => (_jsx("option", { value: option.const, children: option.title }, index))) })) }))) : (_jsxs(ArrayFieldTemplate, Object.assign({}, props, { children: [_jsxs(ArrayFieldList, { children: [(items || []).map((itemSchema, index) => (_jsx(ArrayFieldItem, { children: _jsx(SchemaField, { name: `${name}[${index}]`, schema: typeof itemSchema !== "boolean" ? itemSchema : {}, uiSchema: (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[index]) || (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema.items), forceDefaultValue: true }) }, index))), ((valueArray === null || valueArray === void 0 ? void 0 : valueArray.slice(items.length)) || []).map((_, index) => {
101
+ return schema.anyOf !== undefined ? (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", multiple: (enums === null || enums === void 0 ? void 0 : enums.type) === "multiSelect", children: enums === null || enums === void 0 ? void 0 : enums.values.map((option, index) => (_jsx("option", { value: option.const, children: option.title }, index))) })) }))) : (_jsxs(ArrayFieldTemplate, Object.assign({}, props, { children: [_jsxs(ArrayFieldList, { children: [(items || []).map((itemSchema, index) => (_jsx(ArrayFieldItem, { children: _jsx(SchemaField, { name: `${name}[${index}]`, schema: typeof itemSchema !== "boolean" ? itemSchema : {}, uiSchema: (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[index]) || (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema.items),
102
+ // "true" will prevent validation for required array fields
103
+ // since a value always exists in the array
104
+ forceDefaultValue: false }) }, index))), ((valueArray === null || valueArray === void 0 ? void 0 : valueArray.slice(items.length)) || []).map((_, index) => {
102
105
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
103
106
  return (_jsxs(ArrayFieldItem, { children: [_jsx(SchemaField, { name: `${name}[${index + items.length}]`, schema: additionalItems && typeof additionalItems !== "boolean"
104
107
  ? additionalItems
@@ -14,6 +14,7 @@ export const refDefSchema = {
14
14
  properties: {
15
15
  names: {
16
16
  $ref: "#/definitions/names",
17
+ required: ["firstName", "lastName"],
17
18
  },
18
19
  family: {
19
20
  type: "object",
@@ -45,7 +46,10 @@ export const refDefSchema = {
45
46
  $ref: "#/definitions/age",
46
47
  },
47
48
  birthDate: {
48
- $ref: "#/definitions/birthDate",
49
+ $ref: "#/definitions/someDate",
50
+ },
51
+ specialDate: {
52
+ $ref: "#/definitions/someDate",
49
53
  },
50
54
  creditCard: {
51
55
  type: "number",
@@ -243,7 +247,7 @@ export const refDefSchema = {
243
247
  },
244
248
  },
245
249
  age: ageFieldDefinition,
246
- birthDate: { type: "string", title: "When is your birthday?" },
250
+ someDate: { type: "string", title: "When is your birthday?" },
247
251
  middleName: { type: "string", title: " Middle name" },
248
252
  },
249
253
  };
@@ -252,6 +256,7 @@ export const refDefUiSchema = {
252
256
  "names",
253
257
  "age",
254
258
  "birthDate",
259
+ "specialDate",
255
260
  "family",
256
261
  "creditCard",
257
262
  "billing_address",
@@ -271,8 +276,13 @@ export const refDefUiSchema = {
271
276
  },
272
277
  },
273
278
  feedback: { "ui:field": "TextAreaField" },
279
+ specialDate: {
280
+ "ui:title": "A day to remember",
281
+ },
274
282
  definitions: {
275
- birthDate: { "ui:field": "DateField" },
283
+ someDate: {
284
+ "ui:field": "DateField",
285
+ },
276
286
  names: {
277
287
  history: { "ui:field": "TextAreaField" },
278
288
  middleNames: { "ui:info": { content: "You can leave those blank" } },
@@ -13,8 +13,8 @@ describe("solveReferences", () => {
13
13
  });
14
14
  });
15
15
  it("should resolve simple references with uiSchema correctly", () => {
16
- var _a;
17
- const schemaSolvedForReferences = solveReferences((_a = refDefSchema === null || refDefSchema === void 0 ? void 0 : refDefSchema.properties) === null || _a === void 0 ? void 0 : _a.birthday, refDefSchema.definitions, refDefUiSchema, refDefUiSchema.definitions);
16
+ var _a, _b;
17
+ const schemaSolvedForReferences = solveReferences((_a = refDefSchema === null || refDefSchema === void 0 ? void 0 : refDefSchema.properties) === null || _a === void 0 ? void 0 : _a.birthday, refDefSchema.definitions, (_b = refDefUiSchema.birthday) !== null && _b !== void 0 ? _b : {}, refDefUiSchema.definitions);
18
18
  expect(schemaSolvedForReferences).toEqual({
19
19
  schema: {
20
20
  type: "string",
@@ -23,4 +23,38 @@ describe("solveReferences", () => {
23
23
  uiSchema: { "ui:field": "DateField" },
24
24
  });
25
25
  });
26
+ it("should override uiSchema settings when key of field is also given outside definitions object of uiSchema", () => {
27
+ var _a, _b;
28
+ const schemaSolvedForReferences = solveReferences((_a = refDefSchema === null || refDefSchema === void 0 ? void 0 : refDefSchema.properties) === null || _a === void 0 ? void 0 : _a.friendBirthday, refDefSchema.definitions, (_b = refDefUiSchema.friendBirthday) !== null && _b !== void 0 ? _b : {}, refDefUiSchema.definitions);
29
+ expect(schemaSolvedForReferences).toEqual({
30
+ schema: {
31
+ type: "string",
32
+ title: "Birthday", //this is the original title that comes from the definitions, but is getting overridden
33
+ },
34
+ uiSchema: {
35
+ "ui:field": "DateField",
36
+ "ui:title": "The birthday of your best friend", //this is the title that will be displayed as field title
37
+ },
38
+ });
39
+ });
40
+ it("should merge field and definition schema", () => {
41
+ var _a;
42
+ const schemaSolvedForReferences = solveReferences((_a = refDefSchema === null || refDefSchema === void 0 ? void 0 : refDefSchema.properties) === null || _a === void 0 ? void 0 : _a.address, refDefSchema.definitions, {}, {});
43
+ expect(schemaSolvedForReferences).toEqual({
44
+ schema: {
45
+ type: "object",
46
+ title: "Address",
47
+ required: ["country", "city", "street", "postcode"],
48
+ default: { country: "US" },
49
+ properties: {
50
+ country: { type: "string", title: "Country" },
51
+ city: { type: "string", title: "City" },
52
+ street: { type: "string", title: "Street" },
53
+ postcode: { type: "string", title: "Postcode" },
54
+ apartmentNumber: { type: "string", title: "Apartment number" },
55
+ },
56
+ },
57
+ uiSchema: {},
58
+ });
59
+ });
26
60
  });
@@ -9,13 +9,36 @@ export const refDefSchema = {
9
9
  birthday: {
10
10
  $ref: "#/definitions/birthday",
11
11
  },
12
+ friendBirthday: {
13
+ $ref: "#/definitions/birthday",
14
+ },
15
+ address: {
16
+ $ref: "#/definitions/address",
17
+ required: ["postcode"],
18
+ default: { country: "US" },
19
+ properties: {
20
+ apartmentNumber: { type: "string", title: "Apartment number" },
21
+ },
22
+ },
12
23
  },
13
24
  definitions: {
14
25
  name: { type: "string", title: "Name" },
15
26
  birthday: { type: "string", title: "Birthday" },
27
+ address: {
28
+ type: "object",
29
+ title: "Address",
30
+ required: ["country", "city", "street"],
31
+ properties: {
32
+ country: { type: "string", title: "Country" },
33
+ city: { type: "string", title: "City" },
34
+ street: { type: "string", title: "Street" },
35
+ postcode: { type: "string", title: "Postcode" },
36
+ },
37
+ },
16
38
  },
17
39
  };
18
40
  export const refDefUiSchema = {
41
+ friendBirthday: { "ui:title": "The birthday of your best friend" },
19
42
  definitions: {
20
43
  birthday: { "ui:field": "DateField" },
21
44
  },
@@ -34,7 +34,14 @@ export function createValidationSchema(schema, definitions = {}) {
34
34
  throw new Error("Unsupported schema type");
35
35
  }
36
36
  if (requiredFields !== undefined && requiredFields.includes(key)) {
37
- yupType = yupType["required"]("Required");
37
+ if (type === "array") {
38
+ yupType = yupType
39
+ .min(1, "Required")
40
+ .required("Required");
41
+ }
42
+ else {
43
+ yupType = yupType["required"]("Required");
44
+ }
38
45
  }
39
46
  return yupType;
40
47
  }
@@ -1,11 +1,14 @@
1
+ import { mergeSchemas } from "./mergeSchemas";
1
2
  export function solveReferences(schema, definitions, uiSchema, uiSchemaDefinitions) {
2
- var _a, _b, _c;
3
+ var _a, _b, _c, _d;
3
4
  let solvedSchema = schema;
4
5
  let solvedUiSchema = uiSchema;
5
6
  if (typeof schema === "object" && schema["$ref"] && definitions) {
6
7
  const schemaKey = (_b = (_a = schema["$ref"]) === null || _a === void 0 ? void 0 : _a.substring(14)) !== null && _b !== void 0 ? _b : "";
7
- solvedSchema = definitions[schemaKey];
8
- solvedUiSchema = (_c = uiSchemaDefinitions === null || uiSchemaDefinitions === void 0 ? void 0 : uiSchemaDefinitions[schemaKey]) !== null && _c !== void 0 ? _c : {};
8
+ // TODO: Should we overwrite rather than merge? Maybe add a flag for choosing the behavior?
9
+ solvedSchema = mergeSchemas((_c = definitions[schemaKey]) !== null && _c !== void 0 ? _c : {}, schema !== null && schema !== void 0 ? schema : {});
10
+ delete solvedSchema.$ref;
11
+ solvedUiSchema = mergeSchemas((_d = uiSchemaDefinitions === null || uiSchemaDefinitions === void 0 ? void 0 : uiSchemaDefinitions[schemaKey]) !== null && _d !== void 0 ? _d : {}, uiSchema !== null && uiSchema !== void 0 ? uiSchema : {});
9
12
  }
10
13
  return { schema: solvedSchema, uiSchema: solvedUiSchema };
11
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nolwenture/formik-jsonschema-form",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Formik form generation from json schema",
5
5
  "author": "Antti Vikman <antti.vikman@nolwenture.com>",
6
6
  "contributors": [