@nolwenture/formik-jsonschema-form 0.7.1 → 0.9.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.
Files changed (82) hide show
  1. package/README.md +176 -1
  2. package/dist/Form.js +2 -1
  3. package/dist/Form.stories.d.ts +2 -0
  4. package/dist/Form.stories.js +15 -0
  5. package/dist/Form.test.d.ts +1 -1
  6. package/dist/Form.test.js +3 -1
  7. package/dist/fields/ArrayField.js +2 -1
  8. package/dist/fields/MultipleChoiceField.d.ts +3 -0
  9. package/dist/fields/MultipleChoiceField.js +13 -0
  10. package/dist/fields/ObjectField.js +3 -1
  11. package/dist/fields/SchemaField.js +7 -5
  12. package/dist/fields/StringField.js +1 -1
  13. package/dist/fields/index.d.ts +1 -0
  14. package/dist/fields/index.js +1 -0
  15. package/dist/fields/stories/AnyField.stories.d.ts +5 -0
  16. package/dist/fields/stories/AnyField.stories.js +79 -0
  17. package/dist/fields/stories/ArrayField.stories.d.ts +7 -0
  18. package/dist/fields/stories/ArrayField.stories.js +191 -0
  19. package/dist/fields/stories/BooleanField.stories.d.ts +5 -0
  20. package/dist/fields/stories/BooleanField.stories.js +36 -0
  21. package/dist/fields/stories/DateField.stories.d.ts +5 -0
  22. package/dist/fields/stories/DateField.stories.js +34 -0
  23. package/dist/fields/stories/NullField.stories.d.ts +5 -0
  24. package/dist/fields/stories/NullField.stories.js +38 -0
  25. package/dist/fields/stories/NumberField.stories.d.ts +5 -0
  26. package/dist/fields/stories/NumberField.stories.js +33 -0
  27. package/dist/fields/stories/ObjectField.stories.d.ts +5 -0
  28. package/dist/fields/stories/ObjectField.stories.js +76 -0
  29. package/dist/fields/stories/RadioField.stories.d.ts +5 -0
  30. package/dist/fields/stories/RadioField.stories.js +42 -0
  31. package/dist/fields/stories/SchemaField.stories.d.ts +1716 -0
  32. package/dist/fields/stories/SchemaField.stories.js +85 -0
  33. package/dist/fields/stories/SearchMultiTagSelectField.stories.d.ts +5 -0
  34. package/dist/fields/stories/SearchMultiTagSelectField.stories.js +58 -0
  35. package/dist/fields/stories/StringField.stories.d.ts +7 -0
  36. package/dist/fields/stories/StringField.stories.js +75 -0
  37. package/dist/fields/stories/ToggleField.stories.d.ts +5 -0
  38. package/dist/fields/stories/ToggleField.stories.js +34 -0
  39. package/dist/formEditorSchemas.js +177 -21
  40. package/dist/hooks/__tests__/useExtractUiSchema.test.d.ts +1 -0
  41. package/dist/hooks/__tests__/useExtractUiSchema.test.js +45 -0
  42. package/dist/hooks/__tests__/useSetDefault.test.d.ts +1 -0
  43. package/dist/hooks/__tests__/useSetDefault.test.js +65 -0
  44. package/dist/hooks/useExtractUiSchema.js +7 -3
  45. package/dist/sampleData/ageFieldDefinition.d.ts +2 -0
  46. package/dist/sampleData/ageFieldDefinition.js +5 -0
  47. package/dist/sampleData/multipleChoiceSchema.d.ts +3 -0
  48. package/dist/sampleData/multipleChoiceSchema.js +112 -0
  49. package/dist/sampleData/refDefSchema.d.ts +3 -0
  50. package/dist/sampleData/refDefSchema.js +219 -0
  51. package/dist/sampleData/requiredInputSchema.js +0 -5
  52. package/dist/styledComponents.d.ts +2 -1
  53. package/dist/styledComponents.js +8 -0
  54. package/dist/types.d.ts +19 -2
  55. package/dist/utils/__tests/createInitialValues.test.d.ts +1 -0
  56. package/dist/utils/__tests/createInitialValues.test.js +72 -0
  57. package/dist/utils/__tests/deepOverlap.test.d.ts +1 -0
  58. package/dist/utils/__tests/deepOverlap.test.js +41 -0
  59. package/dist/utils/__tests/getEnums.test.js +11 -16
  60. package/dist/utils/__tests/getTitleAndDescription.test.d.ts +1 -0
  61. package/dist/utils/__tests/getTitleAndDescription.test.js +53 -0
  62. package/dist/utils/__tests/getTypes.test.d.ts +1 -0
  63. package/dist/utils/__tests/getTypes.test.js +62 -0
  64. package/dist/utils/__tests/mergeSchemas.test.d.ts +1 -0
  65. package/dist/utils/__tests/mergeSchemas.test.js +40 -0
  66. package/dist/utils/__tests/resolveSchema.test.js +43 -33
  67. package/dist/utils/__tests/solveReferences.test.d.ts +1 -0
  68. package/dist/utils/__tests/solveReferences.test.js +12 -0
  69. package/dist/utils/__tests/testSchemas.d.ts +4 -0
  70. package/dist/utils/__tests/testSchemas.js +67 -0
  71. package/dist/utils/createInitialValues.d.ts +4 -0
  72. package/dist/utils/createInitialValues.js +27 -0
  73. package/dist/utils/getStyle.d.ts +5 -0
  74. package/dist/utils/getStyle.js +18 -0
  75. package/dist/utils/getTypes.js +1 -1
  76. package/dist/utils/index.d.ts +2 -0
  77. package/dist/utils/index.js +2 -0
  78. package/dist/utils/resolveSchema.d.ts +1 -1
  79. package/dist/utils/resolveSchema.js +88 -39
  80. package/dist/utils/solveReferences.d.ts +2 -0
  81. package/dist/utils/solveReferences.js +8 -0
  82. package/package.json +5 -2
@@ -0,0 +1,85 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { userEvent, expect } from "@storybook/test";
12
+ import { Formik, Form } from "formik";
13
+ import { SchemaField } from "./../SchemaField";
14
+ const initialValues = {
15
+ name: "Test name",
16
+ age: "50",
17
+ creditCard: "test card",
18
+ billing_address: "Street 1",
19
+ };
20
+ export default {
21
+ title: "Fields/SchemaField",
22
+ component: SchemaField,
23
+ decorators: [
24
+ (Story) => (_jsx(Formik, { initialValues: initialValues, onSubmit: () => { }, children: _jsx(Form, { children: Story() }) })),
25
+ ],
26
+ };
27
+ const Template = {
28
+ args: {
29
+ name: "myField",
30
+ schema: {
31
+ type: "string",
32
+ },
33
+ uiSchema: {},
34
+ },
35
+ };
36
+ export const StringField = Object.assign(Object.assign({}, Template), { args: Object.assign(Object.assign({}, Template.args), { schema: { type: "string", title: "My String Field" }, uiSchema: { "ui:help": "Enter a string" } }), play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
37
+ const input = canvasElement.querySelector('input[name="myField"]');
38
+ yield userEvent.type(input, "Hello World");
39
+ expect(input).toHaveValue("Hello World");
40
+ }) });
41
+ export const NumberField = Object.assign(Object.assign({}, Template), { args: Object.assign(Object.assign({}, Template.args), { schema: { type: "number", title: "My Number Field" }, uiSchema: { "ui:help": "Enter a number" } }), play: (_b) => __awaiter(void 0, [_b], void 0, function* ({ canvasElement }) {
42
+ const input = canvasElement.querySelector('input[name="myField"]');
43
+ yield userEvent.type(input, "123");
44
+ expect(input).toHaveValue(123);
45
+ }) });
46
+ export const ReferenceAndInitialValues = {
47
+ args: {
48
+ schema: {
49
+ title: "References and Definitions",
50
+ type: "object",
51
+ required: ["string", "number", "boolean", "enum"],
52
+ properties: {
53
+ name: { $ref: "#/definitions/name" },
54
+ age: { type: "number", title: "Age" },
55
+ creditCard: {
56
+ type: "string",
57
+ },
58
+ },
59
+ dependencies: {
60
+ creditCard: {
61
+ properties: {
62
+ billing_address: {
63
+ $ref: "#/definitions/address",
64
+ },
65
+ },
66
+ required: ["billing_address"],
67
+ },
68
+ },
69
+ definitions: {
70
+ name: { type: "string", title: "Name" },
71
+ address: { type: "string" },
72
+ },
73
+ },
74
+ },
75
+ play: (_c) => __awaiter(void 0, [_c], void 0, function* ({ canvasElement }) {
76
+ const nameField = canvasElement.querySelector('input[name="name"]');
77
+ const ageField = canvasElement.querySelector('input[name="age"]');
78
+ const creditCardField = canvasElement.querySelector('input[name="creditCard"]');
79
+ const addressField = canvasElement.querySelector('input[name="billing_address"]');
80
+ expect(nameField).toHaveValue(initialValues.name);
81
+ expect(ageField).toHaveValue(parseInt(initialValues.age));
82
+ expect(creditCardField).toHaveValue(initialValues.creditCard);
83
+ expect(addressField).toHaveValue(initialValues.billing_address);
84
+ }),
85
+ };
@@ -0,0 +1,5 @@
1
+ import { StoryObj } from "@storybook/react";
2
+ import { SearchMultiTagSelectField } from "./../SearchMultiTagSelectField";
3
+ declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react").ReactRenderer, import("../../types").FieldProps>;
4
+ export default _default;
5
+ export declare const Default: StoryObj<typeof SearchMultiTagSelectField>;
@@ -0,0 +1,58 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { within, userEvent, expect } from "@storybook/test";
12
+ import { SearchMultiTagSelectField } from "./../SearchMultiTagSelectField";
13
+ import { Formik, Form } from "formik";
14
+ export default {
15
+ title: "Fields/SearchMultiTagSelectField",
16
+ component: SearchMultiTagSelectField,
17
+ decorators: [
18
+ (Story) => (_jsx(Formik, { initialValues: { tags: [] }, onSubmit: () => { }, children: _jsx(Form, { children: Story() }) })),
19
+ ],
20
+ };
21
+ export const Default = {
22
+ args: {
23
+ name: "tags",
24
+ schema: {
25
+ type: "string",
26
+ title: "Enum searchable tag select anyOf",
27
+ anyOf: [
28
+ { const: "1", title: "First tag" },
29
+ { const: "2", title: "Second tag" },
30
+ { const: "3", title: "Third tag" },
31
+ ],
32
+ },
33
+ uiSchema: {
34
+ "ui:placeholder": "Search tags...",
35
+ "ui:emptySearchMessage": "No tags found",
36
+ },
37
+ },
38
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
39
+ const canvas = within(canvasElement);
40
+ const input = canvas.getByPlaceholderText("Search tags...");
41
+ yield userEvent.type(input, "First tag");
42
+ const option = yield canvas.findByText("First tag");
43
+ yield userEvent.click(option);
44
+ yield userEvent.type(input, "Third tag");
45
+ const option2 = yield canvas.findByText("Third tag");
46
+ yield userEvent.click(option2);
47
+ const firstTag = canvas.queryByText("First tag");
48
+ const secondTag = canvas.queryByText("Second tag");
49
+ const thirdTag = canvas.queryByText("Third tag");
50
+ expect(firstTag).toBeInTheDocument();
51
+ expect(secondTag).not.toBeInTheDocument();
52
+ expect(thirdTag).toBeInTheDocument();
53
+ // await userEvent.click(firstTag.nextElementSibling as Element);
54
+ // await userEvent.click(thirdTag.nextSibling as Element);
55
+ // expect(firstTag).not.toBeInTheDocument();
56
+ // expect(thirdTag).not.toBeInTheDocument();
57
+ }),
58
+ };
@@ -0,0 +1,7 @@
1
+ import { StoryObj } from "@storybook/react";
2
+ import { StringField } from "./../StringField";
3
+ declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react").ReactRenderer, import("../../types").FieldProps>;
4
+ export default _default;
5
+ export declare const TextInput: StoryObj<typeof StringField>;
6
+ export declare const SelectInput: StoryObj<typeof StringField>;
7
+ export declare const MultiSelect: StoryObj<typeof StringField>;
@@ -0,0 +1,75 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { userEvent, expect } from "@storybook/test";
12
+ import { StringField } from "./../StringField";
13
+ import { Formik, Form } from "formik";
14
+ export default {
15
+ title: "Fields/StringField",
16
+ component: StringField,
17
+ decorators: [
18
+ (Story) => (_jsx(Formik, { initialValues: { name: "" }, onSubmit: () => { }, children: _jsx(Form, { children: Story() }) })),
19
+ ],
20
+ };
21
+ // Story for a regular text input
22
+ export const TextInput = {
23
+ args: {
24
+ name: "name",
25
+ schema: { type: "string" },
26
+ uiSchema: {},
27
+ },
28
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
29
+ const input = canvasElement.querySelector('input[name="name"]');
30
+ yield userEvent.type(input, "John Doe");
31
+ yield expect(input).toHaveValue("John Doe");
32
+ }),
33
+ };
34
+ export const SelectInput = {
35
+ args: {
36
+ name: "select",
37
+ schema: {
38
+ type: "string",
39
+ oneOf: [
40
+ { const: "1", title: "Option 1" },
41
+ { const: "2", title: "Option 2" },
42
+ ],
43
+ },
44
+ uiSchema: {},
45
+ },
46
+ play: (_b) => __awaiter(void 0, [_b], void 0, function* ({ canvasElement }) {
47
+ const select = canvasElement.querySelector('select[name="select"]');
48
+ yield userEvent.selectOptions(select, "1");
49
+ yield expect(select).toHaveValue("1");
50
+ yield expect(select).toHaveDisplayValue("Option 1");
51
+ yield userEvent.selectOptions(select, "2");
52
+ yield expect(select).toHaveValue("2");
53
+ yield expect(select).toHaveDisplayValue("Option 2");
54
+ }),
55
+ };
56
+ export const MultiSelect = {
57
+ args: {
58
+ name: "multiSelect",
59
+ schema: {
60
+ type: "array",
61
+ anyOf: [
62
+ { const: "1", title: "Option 1" },
63
+ { const: "2", title: "Option 2" },
64
+ { const: "3", title: "Option 3" },
65
+ ],
66
+ },
67
+ uiSchema: {},
68
+ },
69
+ play: (_c) => __awaiter(void 0, [_c], void 0, function* ({ canvasElement }) {
70
+ const multiSelect = canvasElement.querySelector('select[name="multiSelect"]');
71
+ yield userEvent.selectOptions(multiSelect, ["1", "3"]);
72
+ yield expect(multiSelect).toHaveValue(["1", "3"]);
73
+ yield expect(multiSelect).not.toHaveValue(["2"]);
74
+ }),
75
+ };
@@ -0,0 +1,5 @@
1
+ import { StoryObj } from "@storybook/react";
2
+ import { ToggleField } from "../ToggleField";
3
+ declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react").ReactRenderer, import("../../types").FieldProps>;
4
+ export default _default;
5
+ export declare const Default: StoryObj<typeof ToggleField>;
@@ -0,0 +1,34 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { userEvent, expect } from "@storybook/test";
12
+ import { ToggleField } from "../ToggleField";
13
+ import { Formik, Form } from "formik";
14
+ export default {
15
+ title: "Fields/ToggleField",
16
+ component: ToggleField,
17
+ decorators: [
18
+ (Story) => (_jsx(Formik, { initialValues: { toggle: false }, onSubmit: () => { }, children: _jsx(Form, { children: Story() }) })),
19
+ ],
20
+ };
21
+ export const Default = {
22
+ args: {
23
+ name: "toggle",
24
+ schema: { type: "boolean", default: false },
25
+ uiSchema: {},
26
+ },
27
+ play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
28
+ const toggleSwitch = canvasElement.querySelector('input[type="checkbox"][name="toggle"]');
29
+ yield userEvent.click(toggleSwitch);
30
+ yield expect(toggleSwitch).toBeChecked();
31
+ yield userEvent.click(toggleSwitch);
32
+ yield expect(toggleSwitch).not.toBeChecked();
33
+ }),
34
+ };
@@ -1,35 +1,191 @@
1
+ const fieldObject = {
2
+ type: {
3
+ type: "string",
4
+ title: "Field type",
5
+ oneOf: [
6
+ { const: "string", title: "String" },
7
+ { const: "number", title: "Number" },
8
+ { const: "array", title: "Array" },
9
+ { const: "object", title: "Object" },
10
+ { const: "boolean", title: "Boolean" },
11
+ { const: "null", title: "Null" },
12
+ { const: "any", title: "Any" },
13
+ ],
14
+ },
15
+ title: {
16
+ type: "string",
17
+ title: "Field title",
18
+ },
19
+ key: { type: "string", title: "Object Key" },
20
+ };
1
21
  const field = {
2
22
  type: "object",
3
- properties: {
4
- type: {
5
- type: "string",
6
- title: "Field type",
7
- oneOf: [
8
- { const: "string", title: "String" },
9
- { const: "number", title: "Number" },
10
- { const: "array", title: "Array" },
11
- { const: "object", title: "Object" },
12
- ],
23
+ properties: fieldObject,
24
+ allOf: [
25
+ {
26
+ if: {
27
+ properties: {
28
+ type: {
29
+ const: "array",
30
+ },
31
+ },
32
+ },
33
+ then: {
34
+ properties: {
35
+ hasFixedFields: {
36
+ type: "string",
37
+ title: "Fixed fields?",
38
+ oneOf: [
39
+ { const: "true", title: "Yes" },
40
+ { const: "false", title: "No" },
41
+ ],
42
+ },
43
+ additionalItemsSchema: {
44
+ type: "array",
45
+ items: { type: "object", properties: fieldObject },
46
+ additionalItems: { type: "object", properties: fieldObject },
47
+ },
48
+ },
49
+ },
13
50
  },
14
- title: {
15
- type: "string",
16
- title: "Field title",
51
+ {
52
+ if: {
53
+ properties: {
54
+ type: {
55
+ const: "object",
56
+ },
57
+ },
58
+ },
59
+ then: {
60
+ properties: {
61
+ hasAdditionalProperties: {
62
+ type: "string",
63
+ title: "Additional fields?",
64
+ oneOf: [
65
+ { const: "true", title: "Yes" },
66
+ { const: "false", title: "No" },
67
+ ],
68
+ },
69
+ objectField: {
70
+ type: "array",
71
+ items: { type: "object", properties: fieldObject },
72
+ additionalItems: { type: "object", properties: fieldObject },
73
+ },
74
+ },
75
+ },
17
76
  },
18
- },
77
+ {
78
+ if: {
79
+ properties: {
80
+ hasAdditionalProperties: {
81
+ const: "true",
82
+ },
83
+ },
84
+ },
85
+ then: {
86
+ properties: {
87
+ additionalItemsSchema: {
88
+ type: "array",
89
+ items: { type: "object", properties: fieldObject },
90
+ additionalItems: { type: "object", properties: fieldObject },
91
+ },
92
+ },
93
+ },
94
+ },
95
+ {
96
+ if: {
97
+ properties: {
98
+ hasFixedFields: {
99
+ const: "true",
100
+ },
101
+ },
102
+ },
103
+ then: {
104
+ properties: {
105
+ fixedArrayField: {
106
+ type: "array",
107
+ items: { type: "object", properties: fieldObject },
108
+ additionalItems: { type: "object", properties: fieldObject },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ ],
19
114
  };
20
115
  export const schema = {
116
+ // type: "object",
117
+ // properties: {
118
+ // "schema.type": {
119
+ // type: "string",
120
+ // default: "object",
121
+ // },
122
+ // "schema.title": {
123
+ // type: "string",
124
+ // title: "Form title",
125
+ // },
126
+ // "schema.properties": { type: "array", additionalItems: field },
127
+ // },
128
+ //$ref needs to get implemented correctly, so that this would work. Works in the playground, though
21
129
  type: "object",
22
130
  properties: {
23
- "schema.type": {
24
- type: "string",
25
- default: "object",
131
+ tree: {
132
+ $ref: "#/definitions/tree",
26
133
  },
27
- "schema.title": {
28
- type: "string",
29
- title: "Form title",
134
+ },
135
+ definitions: {
136
+ tree: {
137
+ type: "object",
138
+ properties: {
139
+ fieldType: {
140
+ type: "string",
141
+ enum: ["string", "object", "array"],
142
+ default: "string",
143
+ },
144
+ },
145
+ dependencies: {
146
+ fieldType: {
147
+ oneOf: [
148
+ {
149
+ properties: {
150
+ fieldType: {
151
+ enum: ["string"],
152
+ },
153
+ field: {
154
+ type: "string",
155
+ },
156
+ },
157
+ },
158
+ {
159
+ properties: {
160
+ fieldType: {
161
+ enum: ["object"],
162
+ },
163
+ field: {
164
+ $ref: "#/definitions/tree",
165
+ },
166
+ },
167
+ },
168
+ {
169
+ properties: {
170
+ fieldType: {
171
+ enum: ["array"],
172
+ },
173
+ field: {
174
+ type: "array",
175
+ items: {
176
+ $ref: "#/definitions/tree",
177
+ },
178
+ additionalItems: {
179
+ $ref: "#/definitions/tree",
180
+ },
181
+ },
182
+ },
183
+ },
184
+ ],
185
+ },
186
+ },
30
187
  },
31
188
  },
32
- additionalProperties: field,
33
189
  };
34
190
  export const uiSchema = {
35
191
  "schema.type": { props: { type: "hidden" } },
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { useExtractUiSchema } from "../useExtractUiSchema"; // Adjust the import path as needed
3
+ describe("useExtractUiSchema", () => {
4
+ it("should return an empty object when provided with an empty object", () => {
5
+ const { result } = renderHook(() => useExtractUiSchema({}));
6
+ expect(result.current).toEqual({});
7
+ });
8
+ it("should correctly extract ui:field and ui:props", () => {
9
+ const uiSchema = {
10
+ "ui:field": "TextField",
11
+ "ui:props": { className: "test-class" },
12
+ };
13
+ const { result } = renderHook(() => useExtractUiSchema(uiSchema));
14
+ expect(result.current.field).toBe("TextField");
15
+ expect(result.current.props).toEqual({ className: "test-class" });
16
+ });
17
+ it("should handle ui:options and merge them into the result", () => {
18
+ const uiSchema = {
19
+ "ui:options": {
20
+ anotherProp: "value",
21
+ },
22
+ };
23
+ const { result } = renderHook(() => useExtractUiSchema(uiSchema));
24
+ expect(result.current.anotherProp).toBe("value");
25
+ });
26
+ it("should remove 'ui:' prefix from keys", () => {
27
+ const uiSchema = {
28
+ "ui:title": "Test Title",
29
+ "ui:description": "Test Description",
30
+ };
31
+ const { result } = renderHook(() => useExtractUiSchema(uiSchema));
32
+ expect(result.current.title).toBe("Test Title");
33
+ expect(result.current.description).toBe("Test Description");
34
+ });
35
+ it("does not crash if schema is frozen by React", () => {
36
+ const uiSchema = { props: { not: { freezed: true } } };
37
+ const frozen = Object.freeze(uiSchema);
38
+ const { result } = renderHook(() => useExtractUiSchema(frozen));
39
+ expect(result.current.props).toEqual({ not: { freezed: true } });
40
+ const uiSchema2 = { props: { is: { extendable: true } } };
41
+ const notExtendable = Object.preventExtensions(uiSchema2);
42
+ const { result: result2 } = renderHook(() => useExtractUiSchema(notExtendable));
43
+ expect(result2.current.props).toEqual({ is: { extendable: true } });
44
+ });
45
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { useField } from "formik";
3
+ import { useSetDefault } from "../useSetDefault";
4
+ import { getDefaultValueForType } from "../../utils/getTypes";
5
+ jest.mock("formik", () => ({
6
+ useField: jest.fn(),
7
+ }));
8
+ jest.mock("../../utils/getTypes", () => ({
9
+ getDefaultValueForType: jest.fn(),
10
+ }));
11
+ describe("useSetDefault", () => {
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ });
15
+ test("sets default value from schema if present", () => {
16
+ const mockSetValue = jest.fn();
17
+ useField.mockReturnValue([
18
+ { value: "" },
19
+ {},
20
+ { setValue: mockSetValue },
21
+ ]);
22
+ const fieldProps = {
23
+ name: "testField",
24
+ schema: { default: "defaultValue" },
25
+ uiSchema: {},
26
+ forceDefaultValue: false,
27
+ };
28
+ renderHook(() => useSetDefault(fieldProps));
29
+ expect(mockSetValue).toHaveBeenCalledWith("defaultValue");
30
+ });
31
+ test("sets default value using getDefaultValueForType if forceDefaultValue is true and no default is provided in schema", () => {
32
+ const mockSetValue = jest.fn();
33
+ useField.mockReturnValue([
34
+ { value: "" },
35
+ {},
36
+ { setValue: mockSetValue },
37
+ ]);
38
+ getDefaultValueForType.mockReturnValue("generatedDefaultValue");
39
+ const fieldProps = {
40
+ name: "testField",
41
+ schema: { type: [] },
42
+ uiSchema: {},
43
+ forceDefaultValue: true,
44
+ };
45
+ renderHook(() => useSetDefault(fieldProps));
46
+ expect(mockSetValue).toHaveBeenCalledWith("generatedDefaultValue");
47
+ expect(getDefaultValueForType).toHaveBeenCalledWith([]);
48
+ });
49
+ test("does not set default value if value is already set", () => {
50
+ const mockSetValue = jest.fn();
51
+ useField.mockReturnValue([
52
+ { value: "existingValue" },
53
+ {},
54
+ { setValue: mockSetValue },
55
+ ]);
56
+ const fieldProps = {
57
+ name: "testField",
58
+ schema: { default: "defaultValue" },
59
+ uiSchema: {},
60
+ forceDefaultValue: true,
61
+ };
62
+ renderHook(() => useSetDefault(fieldProps));
63
+ expect(mockSetValue).not.toHaveBeenCalled();
64
+ });
65
+ });
@@ -12,11 +12,15 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import { useMemo } from "react";
13
13
  export const useExtractUiSchema = (props = {}) => {
14
14
  var _a, _b;
15
- if (props.props !== undefined) {
16
- props["ui:props"] = Object.assign(Object.assign({}, ((_a = props["ui:props"]) !== null && _a !== void 0 ? _a : {})), ((_b = props.props) !== null && _b !== void 0 ? _b : {}));
15
+ // props is frozen by React, so it needs to be copied
16
+ const schema = Object.assign({}, props);
17
+ if (schema.props !== undefined) {
18
+ schema["ui:props"] = Object.assign(Object.assign({}, ((_a = schema["ui:props"]) !== null && _a !== void 0 ? _a : {})), ((_b = schema.props) !== null && _b !== void 0 ? _b : {}));
17
19
  }
18
20
  return useMemo(() => {
19
- const { "ui:options": options = {} } = props, uiSchema = __rest(props, ["ui:options"]);
21
+ const { "ui:options": options = {} } = schema, uiSchema = __rest(schema, ["ui:options"]);
20
22
  return Object.assign(Object.assign({}, Object.keys(uiSchema || {}).reduce((result, key) => (Object.assign(Object.assign({}, result), { [key.replace("ui:", "")]: uiSchema[key] })), {})), options);
23
+ // Input props needs to be relied, not schema. Otherwise, it will be recomputed on every render.
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21
25
  }, [props]);
22
26
  };
@@ -0,0 +1,2 @@
1
+ import type { Schema } from "../types";
2
+ export declare const ageFieldDefinition: Schema;
@@ -0,0 +1,5 @@
1
+ export const ageFieldDefinition = {
2
+ type: "number",
3
+ title: "Age",
4
+ description: "The schema for this field got imported from a separate file",
5
+ };
@@ -0,0 +1,3 @@
1
+ import type { Schema, UiSchema } from "../types";
2
+ export declare const multipleChoiceSchema: Schema;
3
+ export declare const multipleChoiceUiSchema: UiSchema;