@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.
- package/README.md +176 -1
- package/dist/Form.js +2 -1
- package/dist/Form.stories.d.ts +2 -0
- package/dist/Form.stories.js +15 -0
- package/dist/Form.test.d.ts +1 -1
- package/dist/Form.test.js +3 -1
- package/dist/fields/ArrayField.js +2 -1
- package/dist/fields/MultipleChoiceField.d.ts +3 -0
- package/dist/fields/MultipleChoiceField.js +13 -0
- package/dist/fields/ObjectField.js +3 -1
- package/dist/fields/SchemaField.js +7 -5
- package/dist/fields/StringField.js +1 -1
- package/dist/fields/index.d.ts +1 -0
- package/dist/fields/index.js +1 -0
- package/dist/fields/stories/AnyField.stories.d.ts +5 -0
- package/dist/fields/stories/AnyField.stories.js +79 -0
- package/dist/fields/stories/ArrayField.stories.d.ts +7 -0
- package/dist/fields/stories/ArrayField.stories.js +191 -0
- package/dist/fields/stories/BooleanField.stories.d.ts +5 -0
- package/dist/fields/stories/BooleanField.stories.js +36 -0
- package/dist/fields/stories/DateField.stories.d.ts +5 -0
- package/dist/fields/stories/DateField.stories.js +34 -0
- package/dist/fields/stories/NullField.stories.d.ts +5 -0
- package/dist/fields/stories/NullField.stories.js +38 -0
- package/dist/fields/stories/NumberField.stories.d.ts +5 -0
- package/dist/fields/stories/NumberField.stories.js +33 -0
- package/dist/fields/stories/ObjectField.stories.d.ts +5 -0
- package/dist/fields/stories/ObjectField.stories.js +76 -0
- package/dist/fields/stories/RadioField.stories.d.ts +5 -0
- package/dist/fields/stories/RadioField.stories.js +42 -0
- package/dist/fields/stories/SchemaField.stories.d.ts +1716 -0
- package/dist/fields/stories/SchemaField.stories.js +85 -0
- package/dist/fields/stories/SearchMultiTagSelectField.stories.d.ts +5 -0
- package/dist/fields/stories/SearchMultiTagSelectField.stories.js +58 -0
- package/dist/fields/stories/StringField.stories.d.ts +7 -0
- package/dist/fields/stories/StringField.stories.js +75 -0
- package/dist/fields/stories/ToggleField.stories.d.ts +5 -0
- package/dist/fields/stories/ToggleField.stories.js +34 -0
- package/dist/formEditorSchemas.js +177 -21
- package/dist/hooks/__tests__/useExtractUiSchema.test.d.ts +1 -0
- package/dist/hooks/__tests__/useExtractUiSchema.test.js +45 -0
- package/dist/hooks/__tests__/useSetDefault.test.d.ts +1 -0
- package/dist/hooks/__tests__/useSetDefault.test.js +65 -0
- package/dist/hooks/useExtractUiSchema.js +7 -3
- package/dist/sampleData/ageFieldDefinition.d.ts +2 -0
- package/dist/sampleData/ageFieldDefinition.js +5 -0
- package/dist/sampleData/multipleChoiceSchema.d.ts +3 -0
- package/dist/sampleData/multipleChoiceSchema.js +112 -0
- package/dist/sampleData/refDefSchema.d.ts +3 -0
- package/dist/sampleData/refDefSchema.js +219 -0
- package/dist/sampleData/requiredInputSchema.js +0 -5
- package/dist/styledComponents.d.ts +2 -1
- package/dist/styledComponents.js +8 -0
- package/dist/types.d.ts +19 -2
- package/dist/utils/__tests/createInitialValues.test.d.ts +1 -0
- package/dist/utils/__tests/createInitialValues.test.js +72 -0
- package/dist/utils/__tests/deepOverlap.test.d.ts +1 -0
- package/dist/utils/__tests/deepOverlap.test.js +41 -0
- package/dist/utils/__tests/getEnums.test.js +11 -16
- package/dist/utils/__tests/getTitleAndDescription.test.d.ts +1 -0
- package/dist/utils/__tests/getTitleAndDescription.test.js +53 -0
- package/dist/utils/__tests/getTypes.test.d.ts +1 -0
- package/dist/utils/__tests/getTypes.test.js +62 -0
- package/dist/utils/__tests/mergeSchemas.test.d.ts +1 -0
- package/dist/utils/__tests/mergeSchemas.test.js +40 -0
- package/dist/utils/__tests/resolveSchema.test.js +43 -33
- package/dist/utils/__tests/solveReferences.test.d.ts +1 -0
- package/dist/utils/__tests/solveReferences.test.js +12 -0
- package/dist/utils/__tests/testSchemas.d.ts +4 -0
- package/dist/utils/__tests/testSchemas.js +67 -0
- package/dist/utils/createInitialValues.d.ts +4 -0
- package/dist/utils/createInitialValues.js +27 -0
- package/dist/utils/getStyle.d.ts +5 -0
- package/dist/utils/getStyle.js +18 -0
- package/dist/utils/getTypes.js +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/resolveSchema.d.ts +1 -1
- package/dist/utils/resolveSchema.js +88 -39
- package/dist/utils/solveReferences.d.ts +2 -0
- package/dist/utils/solveReferences.js +8 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -14,6 +14,26 @@ With yarn:
|
|
|
14
14
|
yarn add @nolwenture/formik-jsonschema-form
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Testing
|
|
18
|
+
|
|
19
|
+
Run unit tests:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
npm run test
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
To run storybook tests make sure you have Playwright installed:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
npx playwright install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run storybook tests:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
npm run test-storybook
|
|
35
|
+
```
|
|
36
|
+
|
|
17
37
|
## Usage
|
|
18
38
|
|
|
19
39
|
### Basic usage
|
|
@@ -400,7 +420,9 @@ const uiSchema = {
|
|
|
400
420
|
### Alternative fields
|
|
401
421
|
|
|
402
422
|
There are alternative fields available for certain types.
|
|
403
|
-
For `type: "string"` there is `DateField
|
|
423
|
+
For `type: "string"` there is `DateField` and `MultipleChoiceField`.
|
|
424
|
+
|
|
425
|
+
`DateField` has a "dd.mm.yyyy" date input field and a calendar date picker and will save the date input as "yyyy-mm-dd" string.
|
|
404
426
|
|
|
405
427
|
```tsx
|
|
406
428
|
const schema = {
|
|
@@ -414,6 +436,35 @@ const uiSchema = {
|
|
|
414
436
|
};
|
|
415
437
|
```
|
|
416
438
|
|
|
439
|
+
`MultipleChoiceField` takes an array of `oneOf` or `anyOf`. The choices provided inside an `oneOf`array will be presented on the UI as Radio button selectables and only one of the given values can be selected. The choices provided inside an `anyOf` array will be presented on the UI as Checkbox selectables and multiple values can be selected.
|
|
440
|
+
`MultipleChoiceField` comes with a small number of predefined styles, which are `inline`, `oneColumn`, `twoColumns` and `threeColumns`. When no style is given, the choice options are listed in a row under the title.
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
const schema = {
|
|
444
|
+
properties: {
|
|
445
|
+
feeling: {
|
|
446
|
+
type: "string",
|
|
447
|
+
title: "How are you feeling today?",
|
|
448
|
+
oneOf: [
|
|
449
|
+
{ const: "happy", title: "Happy" },
|
|
450
|
+
{ const: "creative", title: "Creative" },
|
|
451
|
+
{ const: "tired", title: "Tired" },
|
|
452
|
+
{ const: "nope", title: "Please don't even ask..." },
|
|
453
|
+
],
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const uiSchema = {
|
|
459
|
+
feeling: {
|
|
460
|
+
"ui:field": "MultipleChoiceField",
|
|
461
|
+
"ui:option": {
|
|
462
|
+
style: "twoColumns",
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
```
|
|
467
|
+
|
|
417
468
|
For `type: "boolean"` there is `RadioField` and `ToggleField`.
|
|
418
469
|
`RadioField` can take labels for the "true" and "false" options. If left empty, the default labels will be "true" and "false".
|
|
419
470
|
|
|
@@ -527,6 +578,130 @@ export const conditionsSchema = {
|
|
|
527
578
|
};
|
|
528
579
|
```
|
|
529
580
|
|
|
581
|
+
Another option to give conditional fields is using `dependencies`-object inside the main schema.
|
|
582
|
+
Dependencies can either be triggered by the conditional-giving property being present in the form values, or by the conditional-giving property having a certain value.
|
|
583
|
+
|
|
584
|
+
To have value-dependent conditionals, the `dependencies`-object needs to have an `oneOf`-array listing the possible conditional-giving values matching the enum-values of the conditional-giving field. Each case in in the `oneOf` array can either have no conditional fields, then the only key-value pair given inside `properties` is the matching conditional (see "no type"), or an unlimited amount of conditional fields listed after the matching conditional.
|
|
585
|
+
|
|
586
|
+
```tsx
|
|
587
|
+
export const refDefSchema: Schema = {
|
|
588
|
+
type: "object",
|
|
589
|
+
properties: {
|
|
590
|
+
creditCard: {
|
|
591
|
+
type: "number",
|
|
592
|
+
title: "Credit card number",
|
|
593
|
+
description:
|
|
594
|
+
"Giving any input to this field will trigger its dependent field 'billing_address'",
|
|
595
|
+
},
|
|
596
|
+
fieldType: {
|
|
597
|
+
type: "string",
|
|
598
|
+
description:
|
|
599
|
+
"Selecting an option in this field will render the correct dependent field from the provided oneOf-array",
|
|
600
|
+
enum: ["string", "object", "array"],
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
dependencies: {
|
|
604
|
+
creditCard: {
|
|
605
|
+
properties: {
|
|
606
|
+
billing_address: {
|
|
607
|
+
type: "object",
|
|
608
|
+
description: "This object depends on creditCard field input",
|
|
609
|
+
properties: {
|
|
610
|
+
street: { type: "string", title: "Street" },
|
|
611
|
+
city: { type: "string", title: "City" },
|
|
612
|
+
postalCode: { type: "number", title: "Postal code" },
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
required: ["billing_address"],
|
|
617
|
+
},
|
|
618
|
+
fieldType: {
|
|
619
|
+
oneOf: [
|
|
620
|
+
{
|
|
621
|
+
properties: {
|
|
622
|
+
fieldType: {
|
|
623
|
+
enum: ["no type"],
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
properties: {
|
|
629
|
+
fieldType: {
|
|
630
|
+
enum: ["string"],
|
|
631
|
+
},
|
|
632
|
+
stringField: {
|
|
633
|
+
type: "string",
|
|
634
|
+
title: "StringField",
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
properties: {
|
|
640
|
+
fieldType: {
|
|
641
|
+
enum: ["object"],
|
|
642
|
+
},
|
|
643
|
+
objectField: {
|
|
644
|
+
type: "object",
|
|
645
|
+
properties: {
|
|
646
|
+
objectString: {
|
|
647
|
+
type: "string",
|
|
648
|
+
title: "StringField in Object",
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
properties: {
|
|
656
|
+
fieldType: {
|
|
657
|
+
enum: ["array"],
|
|
658
|
+
},
|
|
659
|
+
arrayField: {
|
|
660
|
+
type: "array",
|
|
661
|
+
items: [{ type: "string", title: "StringField in fixed array" }],
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
],
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
Using `if-else-then` or `dependencies` depends mostly on the use case. For recursion, a combination of `$ref` and `dependencies`is recommended.
|
|
672
|
+
|
|
673
|
+
### Schema reference $ref
|
|
674
|
+
|
|
675
|
+
Schemas can be referenced. A referenced schema can be given to a property using `$ref` keyword. The definition for the referenced schema needs to be inside `definitions` object inside the main schema. The value-string given to $ref-key must always look like this:
|
|
676
|
+
`'#/definitions/{addMatchingKeyHere}'`
|
|
677
|
+
|
|
678
|
+
```tsx
|
|
679
|
+
const ageFieldDefinition: Schema = {
|
|
680
|
+
type: "number",
|
|
681
|
+
title: "Age",
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
export const refDefSchema: Schema = {
|
|
685
|
+
title: "References and Definitions",
|
|
686
|
+
type: "object",
|
|
687
|
+
properties: {
|
|
688
|
+
name: {
|
|
689
|
+
$ref: "#/definitions/name",
|
|
690
|
+
},
|
|
691
|
+
age: {
|
|
692
|
+
$ref: "#/definitions/age",
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
definitions: {
|
|
696
|
+
name: {
|
|
697
|
+
type: "string",
|
|
698
|
+
title: "Name",
|
|
699
|
+
},
|
|
700
|
+
age: ageFieldDefinition,
|
|
701
|
+
},
|
|
702
|
+
};
|
|
703
|
+
```
|
|
704
|
+
|
|
530
705
|
### Ordering fields
|
|
531
706
|
|
|
532
707
|
The order of fields can be set using `"ui:order": ["fieldName"]` inside the uiSchema.
|
package/dist/Form.js
CHANGED
|
@@ -30,11 +30,12 @@ export const Form = (props) => {
|
|
|
30
30
|
]);
|
|
31
31
|
const { isSubmitting } = useFormikContext();
|
|
32
32
|
const { schema, uiSchema = {}, className, children, fields = {}, templates = {}, components = {}, formContext = {}, useItemsAsAdditionalItems = false, hideUnmarkedAdditionalProperties = false, } = props;
|
|
33
|
+
const schemaDefinitions = schema.definitions;
|
|
33
34
|
const registry = {
|
|
34
35
|
fields: Object.assign(Object.assign({}, defaultFields), fields),
|
|
35
36
|
templates: Object.assign(Object.assign({}, defaultTemplates), templates),
|
|
36
37
|
components: Object.assign(Object.assign({}, defaultComponents), components),
|
|
37
|
-
definitions:
|
|
38
|
+
definitions: schemaDefinitions,
|
|
38
39
|
formContext,
|
|
39
40
|
useItemsAsAdditionalItems,
|
|
40
41
|
hideUnmarkedAdditionalProperties,
|
package/dist/Form.stories.d.ts
CHANGED
|
@@ -11,3 +11,5 @@ export declare const StylesCustomized: () => import("react/jsx-runtime").JSX.Ele
|
|
|
11
11
|
export declare const WithGeneratedSchema: Story;
|
|
12
12
|
export declare const WithRequiredInput: Story;
|
|
13
13
|
export declare const WithConditions: Story;
|
|
14
|
+
export declare const WithRefAndDefinitions: Story;
|
|
15
|
+
export declare const MultipleChoice: Story;
|
package/dist/Form.stories.js
CHANGED
|
@@ -25,6 +25,8 @@ import { Formik, useFormikContext } from "formik";
|
|
|
25
25
|
import { InfoFieldButton, SearchableMultiTagSelectField, } from "./styledComponents";
|
|
26
26
|
import { completeSchema, completeUiSchema, simpleSchema, simpleUiSchema, generatedSchema, requiredInputSchema, requiredInputUiSchema, conditionsSchema, conditionsUiSchema, } from "./sampleData";
|
|
27
27
|
import { Form, styledComponents } from "./";
|
|
28
|
+
import { refDefSchema, refDefUiSchema } from "./sampleData/refDefSchema";
|
|
29
|
+
import { multipleChoiceSchema, multipleChoiceUiSchema, } from "./sampleData/multipleChoiceSchema";
|
|
28
30
|
const meta = {
|
|
29
31
|
title: "Form",
|
|
30
32
|
component: Form,
|
|
@@ -46,6 +48,7 @@ export default meta;
|
|
|
46
48
|
export const Simple = {
|
|
47
49
|
args: {
|
|
48
50
|
schema: simpleSchema,
|
|
51
|
+
uiSchema: simpleUiSchema,
|
|
49
52
|
},
|
|
50
53
|
};
|
|
51
54
|
const InfoFieldButtonNew = styled(InfoFieldButton) `
|
|
@@ -183,3 +186,15 @@ export const WithConditions = {
|
|
|
183
186
|
hideUnmarkedAdditionalProperties: true,
|
|
184
187
|
},
|
|
185
188
|
};
|
|
189
|
+
export const WithRefAndDefinitions = {
|
|
190
|
+
args: {
|
|
191
|
+
schema: refDefSchema,
|
|
192
|
+
uiSchema: refDefUiSchema,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
export const MultipleChoice = {
|
|
196
|
+
args: {
|
|
197
|
+
schema: multipleChoiceSchema,
|
|
198
|
+
uiSchema: multipleChoiceUiSchema,
|
|
199
|
+
},
|
|
200
|
+
};
|
package/dist/Form.test.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import "@testing-library/jest-dom";
|
package/dist/Form.test.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import "@testing-library/jest-dom";
|
|
2
3
|
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import { Formik } from "formik";
|
|
3
5
|
import { Form } from "./Form";
|
|
4
6
|
import { simpleSchema } from "./sampleData";
|
|
5
7
|
test("renders title", () => {
|
|
6
|
-
render(_jsx(Form, { schema: simpleSchema }));
|
|
8
|
+
render(_jsx(Formik, { initialValues: {}, onSubmit: () => { }, children: _jsx(Form, { schema: simpleSchema }) }));
|
|
7
9
|
const titleElement = screen.getByText(/TITLE/i);
|
|
8
10
|
expect(titleElement).toBeInTheDocument();
|
|
9
11
|
});
|
|
@@ -93,7 +93,8 @@ export const ArrayField = (props) => {
|
|
|
93
93
|
// );
|
|
94
94
|
// Bacwards compatibility ends
|
|
95
95
|
const { disabled } = useExtractUiSchema(uiSchema);
|
|
96
|
-
|
|
96
|
+
const valueArray = Array.isArray(value) ? value : undefined;
|
|
97
|
+
return (_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) => (_jsxs(ArrayFieldItem, { children: [_jsx(SchemaField, { name: `${name}[${index + items.length}]`, schema: additionalItems && typeof additionalItems !== "boolean"
|
|
97
98
|
? additionalItems
|
|
98
99
|
: {}, uiSchema: Object.assign({ disabled }, ((uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[index + items.length]) || (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema.items))) }), _jsxs(ArrayFieldItemButtons, { children: [GetArrayFieldButton({
|
|
99
100
|
purpose: ArrayFieldButtonPurpose.REMOVE,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useMemo } from "react";
|
|
3
|
+
import { FormRegistry } from "../Form";
|
|
4
|
+
import { useExtractUiSchema, useSetDefault } from "../hooks";
|
|
5
|
+
import { getEnums } from "../utils/getEnums";
|
|
6
|
+
export const MultipleChoiceField = (props) => {
|
|
7
|
+
const { templates: { FieldTemplate }, components: { MultipleChoiceField }, } = useContext(FormRegistry);
|
|
8
|
+
useSetDefault(props);
|
|
9
|
+
const { name, uiSchema } = props;
|
|
10
|
+
const { props: inputProps, option } = useExtractUiSchema(uiSchema);
|
|
11
|
+
const enums = useMemo(() => getEnums(props), [props]);
|
|
12
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(MultipleChoiceField, { inputProps: inputProps, name: name, enums: enums, style: option === null || option === void 0 ? void 0 : option.style }) })));
|
|
13
|
+
};
|
|
@@ -53,7 +53,9 @@ export const ObjectField = (props) => {
|
|
|
53
53
|
return (_jsx(SchemaField, { required: required === null || required === void 0 ? void 0 : required.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[childName] }, childName));
|
|
54
54
|
})
|
|
55
55
|
.filter((x) => x), (!hideUnmarkedAdditionalProperties || additionalProperties) &&
|
|
56
|
-
Object.entries(additionalValues || {}).map(([childName
|
|
56
|
+
Object.entries(additionalValues || {}).map(([childName]) => (_jsxs(ObjectFieldItem, { children: [_jsx(SchemaField, { required: required === null || required === void 0 ? void 0 : required.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof schema.additionalProperties !== "boolean"
|
|
57
|
+
? schema.additionalProperties
|
|
58
|
+
: {}, uiSchema: uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[childName] }, childName), _jsx(ObjectFieldItemButtons, { children: getObjectFieldButton({
|
|
57
59
|
purpose: ObjectFieldButtonPurpose.REMOVE,
|
|
58
60
|
onClick: () => {
|
|
59
61
|
const _a = value, _b = childName, _removedProperty = _a[_b], newValue = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useContext } from "react";
|
|
3
|
-
import { getSchemaType } from "../utils";
|
|
2
|
+
import { useContext, useMemo } from "react";
|
|
3
|
+
import { getSchemaType, solveReferences } from "../utils";
|
|
4
4
|
import { useExtractUiSchema } from "../hooks";
|
|
5
5
|
import { FormRegistry } from "../Form";
|
|
6
6
|
import { COMPONENT_TYPES } from "./types";
|
|
@@ -23,7 +23,9 @@ const useFieldComponent = (schema, uiSchema, fields) => {
|
|
|
23
23
|
};
|
|
24
24
|
export const SchemaField = (props) => {
|
|
25
25
|
const { schema, uiSchema } = props;
|
|
26
|
-
const { fields } = useContext(FormRegistry);
|
|
27
|
-
const
|
|
28
|
-
|
|
26
|
+
const { fields, definitions } = useContext(FormRegistry);
|
|
27
|
+
const schemaSolvedForReferences = useMemo(() => solveReferences(schema, definitions), [schema, definitions]);
|
|
28
|
+
const FieldComponent = useFieldComponent(schemaSolvedForReferences, uiSchema, fields);
|
|
29
|
+
const newProps = Object.assign(Object.assign({}, props), { schema: schemaSolvedForReferences });
|
|
30
|
+
return _jsx(FieldComponent, Object.assign({}, newProps));
|
|
29
31
|
};
|
|
@@ -13,7 +13,7 @@ export const StringField = (props) => {
|
|
|
13
13
|
switch (enums === null || enums === void 0 ? void 0 : enums.type) {
|
|
14
14
|
case "select":
|
|
15
15
|
case "multiSelect":
|
|
16
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsxs(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", multiple: enums.type === "multiSelect"
|
|
16
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsxs(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", multiple: enums.type === "multiSelect", children: [enums.type !== "multiSelect" && (_jsx("option", Object.assign({ value: "", disabled: true,
|
|
17
17
|
hidden: true }, (_a = uiSchema["ui:placeholderOptionOptions"]) === null || _a === void 0 ? void 0 : _a.props, { children: placeholder }))), enums === null || enums === void 0 ? void 0 : enums.values.map((option, index) => (_jsx("option", { value: option.const, children: option.title }, index)))] })) })));
|
|
18
18
|
default:
|
|
19
19
|
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FieldInput, Object.assign({ type,
|
package/dist/fields/index.d.ts
CHANGED
package/dist/fields/index.js
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { StoryObj } from "@storybook/react";
|
|
2
|
+
import { AnyField } from "./../AnyField";
|
|
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 AnyField>;
|
|
@@ -0,0 +1,79 @@
|
|
|
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 { Formik, Form } from "formik";
|
|
13
|
+
import { AnyField } from "./../AnyField";
|
|
14
|
+
export default {
|
|
15
|
+
title: "Fields/AnyField",
|
|
16
|
+
component: AnyField,
|
|
17
|
+
decorators: [
|
|
18
|
+
(Story) => (_jsx(Formik, { initialValues: { dynamicField: "" }, onSubmit: (values) => console.log(values), children: _jsx(Form, { children: Story() }) })),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
export const Default = {
|
|
22
|
+
args: {
|
|
23
|
+
name: "dynamicField",
|
|
24
|
+
schema: {},
|
|
25
|
+
uiSchema: {},
|
|
26
|
+
},
|
|
27
|
+
play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
|
|
28
|
+
const canvas = within(canvasElement);
|
|
29
|
+
const select = canvasElement.querySelector("select");
|
|
30
|
+
// Null
|
|
31
|
+
yield userEvent.selectOptions(select, "null");
|
|
32
|
+
const allInputs = canvasElement.querySelectorAll("input");
|
|
33
|
+
expect(allInputs).toHaveLength(0);
|
|
34
|
+
// AnyField
|
|
35
|
+
yield userEvent.selectOptions(select, "any");
|
|
36
|
+
const anySelect = canvasElement.querySelectorAll("select");
|
|
37
|
+
const input = canvasElement.querySelector('input[name="dynamicField"]');
|
|
38
|
+
yield userEvent.type(input, "testString");
|
|
39
|
+
expect(input).toHaveValue("testString");
|
|
40
|
+
yield userEvent.selectOptions(anySelect[1], "boolean");
|
|
41
|
+
yield userEvent.click(input);
|
|
42
|
+
expect(input).toBeChecked;
|
|
43
|
+
// StringField
|
|
44
|
+
yield userEvent.selectOptions(select, "string");
|
|
45
|
+
yield userEvent.type(input, "testString");
|
|
46
|
+
expect(input).toHaveValue("testString");
|
|
47
|
+
// IntegerField
|
|
48
|
+
yield userEvent.selectOptions(select, "integer");
|
|
49
|
+
const integerInput = canvasElement.querySelector('input[name="dynamicField"]');
|
|
50
|
+
yield userEvent.type(integerInput, "321");
|
|
51
|
+
expect(integerInput).toHaveValue(321);
|
|
52
|
+
// NumberField
|
|
53
|
+
yield userEvent.selectOptions(select, "number");
|
|
54
|
+
const numberInput = canvasElement.querySelector('input[name="dynamicField"]');
|
|
55
|
+
yield userEvent.clear(numberInput);
|
|
56
|
+
yield userEvent.type(numberInput, "123");
|
|
57
|
+
expect(numberInput).toHaveValue(123);
|
|
58
|
+
// BooleanField
|
|
59
|
+
yield userEvent.selectOptions(select, "boolean");
|
|
60
|
+
const checkBox = canvasElement.querySelector('input[name="dynamicField"]');
|
|
61
|
+
yield userEvent.click(checkBox);
|
|
62
|
+
expect(checkBox).toBeChecked();
|
|
63
|
+
// ObjectField
|
|
64
|
+
// TODO Find out how to handle modal
|
|
65
|
+
// ArrayField
|
|
66
|
+
yield userEvent.selectOptions(select, "array");
|
|
67
|
+
const addButton = canvas.getByText("+");
|
|
68
|
+
yield userEvent.click(addButton);
|
|
69
|
+
yield userEvent.click(addButton);
|
|
70
|
+
const downArrow = canvas.queryAllByText("↓");
|
|
71
|
+
const inputFirst = canvasElement.querySelector('input[name="dynamicField[0]"]');
|
|
72
|
+
const inputSecond = canvasElement.querySelector('input[name="dynamicField[1]"]');
|
|
73
|
+
yield userEvent.type(inputFirst, "first");
|
|
74
|
+
yield userEvent.type(inputSecond, "second");
|
|
75
|
+
yield userEvent.click(downArrow[0]);
|
|
76
|
+
expect(inputFirst).toHaveValue("second");
|
|
77
|
+
expect(inputSecond).toHaveValue("first");
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { StoryObj } from "@storybook/react";
|
|
2
|
+
import { ArrayField } from "./../ArrayField";
|
|
3
|
+
declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react").ReactRenderer, import("../../types").FieldProps>;
|
|
4
|
+
export default _default;
|
|
5
|
+
export declare const BasicArray: StoryObj<typeof ArrayField>;
|
|
6
|
+
export declare const EnumArray: StoryObj<typeof ArrayField>;
|
|
7
|
+
export declare const ObjectArray: StoryObj<typeof ArrayField>;
|
|
@@ -0,0 +1,191 @@
|
|
|
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 { Formik, Form } from "formik";
|
|
13
|
+
import { ArrayField } from "./../ArrayField";
|
|
14
|
+
export default {
|
|
15
|
+
title: "Fields/ArrayField",
|
|
16
|
+
component: ArrayField,
|
|
17
|
+
decorators: [
|
|
18
|
+
(Story) => (_jsx(Formik, { initialValues: { itemsList: [] }, onSubmit: (values) => console.log(values), children: _jsx(Form, { children: Story() }) })),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
export const BasicArray = {
|
|
22
|
+
args: {
|
|
23
|
+
name: "itemsList",
|
|
24
|
+
schema: {
|
|
25
|
+
type: "array",
|
|
26
|
+
title: "Array",
|
|
27
|
+
description: "Array description",
|
|
28
|
+
additionalItems: { type: "boolean" },
|
|
29
|
+
},
|
|
30
|
+
uiSchema: {},
|
|
31
|
+
},
|
|
32
|
+
play: (_a) => __awaiter(void 0, [_a], void 0, function* ({ canvasElement }) {
|
|
33
|
+
const canvas = within(canvasElement);
|
|
34
|
+
// Add three items
|
|
35
|
+
const addButton = canvas.getByText("+");
|
|
36
|
+
yield userEvent.click(addButton);
|
|
37
|
+
yield userEvent.click(addButton);
|
|
38
|
+
yield userEvent.click(addButton);
|
|
39
|
+
const allDownArrows = canvas.queryAllByText("↓");
|
|
40
|
+
const allUpArrows = canvas.queryAllByText("↑");
|
|
41
|
+
const firstCheckbox = canvasElement.querySelector('input[name="itemsList[0]"]');
|
|
42
|
+
const secondCheckbox = canvasElement.querySelector('input[name="itemsList[1]"]');
|
|
43
|
+
const thirdCheckbox = canvasElement.querySelector('input[name="itemsList[2]"]');
|
|
44
|
+
const allLabelsAfterAdd = canvasElement.querySelectorAll('[name^="itemsList["]');
|
|
45
|
+
expect(allLabelsAfterAdd.length).toEqual(3);
|
|
46
|
+
// Check that moving items work
|
|
47
|
+
yield userEvent.click(firstCheckbox);
|
|
48
|
+
expect(firstCheckbox).toBeChecked();
|
|
49
|
+
expect(secondCheckbox).not.toBeChecked();
|
|
50
|
+
expect(thirdCheckbox).not.toBeChecked();
|
|
51
|
+
yield userEvent.click(allDownArrows[0]);
|
|
52
|
+
expect(firstCheckbox).not.toBeChecked();
|
|
53
|
+
expect(secondCheckbox).toBeChecked();
|
|
54
|
+
expect(thirdCheckbox).not.toBeChecked();
|
|
55
|
+
yield userEvent.click(allDownArrows[1]);
|
|
56
|
+
expect(firstCheckbox).not.toBeChecked();
|
|
57
|
+
expect(secondCheckbox).not.toBeChecked();
|
|
58
|
+
expect(thirdCheckbox).toBeChecked();
|
|
59
|
+
yield userEvent.click(allUpArrows[1]);
|
|
60
|
+
expect(firstCheckbox).not.toBeChecked();
|
|
61
|
+
expect(secondCheckbox).toBeChecked();
|
|
62
|
+
expect(thirdCheckbox).not.toBeChecked();
|
|
63
|
+
// Remove items
|
|
64
|
+
const removeButton = canvas.queryAllByText("-");
|
|
65
|
+
yield userEvent.click(removeButton[2]);
|
|
66
|
+
yield userEvent.click(removeButton[1]);
|
|
67
|
+
yield userEvent.click(removeButton[0]);
|
|
68
|
+
const allLabelsAfterRemove = canvasElement.querySelectorAll('[name^="itemsList["]');
|
|
69
|
+
expect(allLabelsAfterRemove.length).toEqual(0);
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
export const EnumArray = {
|
|
73
|
+
args: {
|
|
74
|
+
name: "itemsList",
|
|
75
|
+
schema: {
|
|
76
|
+
type: "array",
|
|
77
|
+
title: "Enum array",
|
|
78
|
+
description: "Enum array description",
|
|
79
|
+
additionalItems: {
|
|
80
|
+
type: "number",
|
|
81
|
+
enum: [1, 2, 3, 4],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
uiSchema: {},
|
|
85
|
+
},
|
|
86
|
+
play: (_b) => __awaiter(void 0, [_b], void 0, function* ({ canvasElement }) {
|
|
87
|
+
const canvas = within(canvasElement);
|
|
88
|
+
// Add three items
|
|
89
|
+
const addButton = canvas.getByText("+");
|
|
90
|
+
yield userEvent.click(addButton);
|
|
91
|
+
yield userEvent.click(addButton);
|
|
92
|
+
yield userEvent.click(addButton);
|
|
93
|
+
const allDownArrows = canvas.queryAllByText("↓");
|
|
94
|
+
const allUpArrows = canvas.queryAllByText("↑");
|
|
95
|
+
const firstSelect = canvasElement.querySelector('select[name="itemsList[0]"]');
|
|
96
|
+
const secondSelect = canvasElement.querySelector('select[name="itemsList[1]"]');
|
|
97
|
+
const thirdSelect = canvasElement.querySelector('select[name="itemsList[2]"]');
|
|
98
|
+
const allLabelsAfterAdd = canvasElement.querySelectorAll('[name^="itemsList["]');
|
|
99
|
+
expect(allLabelsAfterAdd.length).toEqual(3);
|
|
100
|
+
yield userEvent.selectOptions(firstSelect, "1");
|
|
101
|
+
yield userEvent.selectOptions(secondSelect, "2");
|
|
102
|
+
yield userEvent.selectOptions(thirdSelect, "3");
|
|
103
|
+
// Check that moving items work
|
|
104
|
+
yield expect(firstSelect).toHaveValue("1");
|
|
105
|
+
yield expect(secondSelect).toHaveValue("2");
|
|
106
|
+
yield expect(thirdSelect).toHaveValue("3");
|
|
107
|
+
yield userEvent.click(allDownArrows[0]);
|
|
108
|
+
yield expect(firstSelect).toHaveValue("2");
|
|
109
|
+
yield expect(secondSelect).toHaveValue("1");
|
|
110
|
+
yield expect(thirdSelect).toHaveValue("3");
|
|
111
|
+
yield userEvent.click(allDownArrows[1]);
|
|
112
|
+
yield expect(firstSelect).toHaveValue("2");
|
|
113
|
+
yield expect(secondSelect).toHaveValue("3");
|
|
114
|
+
yield expect(thirdSelect).toHaveValue("1");
|
|
115
|
+
yield userEvent.click(allUpArrows[1]);
|
|
116
|
+
yield expect(firstSelect).toHaveValue("2");
|
|
117
|
+
yield expect(secondSelect).toHaveValue("1");
|
|
118
|
+
yield expect(thirdSelect).toHaveValue("3");
|
|
119
|
+
// Remove items
|
|
120
|
+
const removeButton = canvas.queryAllByText("-");
|
|
121
|
+
yield userEvent.click(removeButton[2]);
|
|
122
|
+
yield userEvent.click(removeButton[1]);
|
|
123
|
+
yield userEvent.click(removeButton[0]);
|
|
124
|
+
const allLabelsAfterRemove = canvasElement.querySelectorAll('[name^="itemsList["]');
|
|
125
|
+
expect(allLabelsAfterRemove.length).toEqual(0);
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
export const ObjectArray = {
|
|
129
|
+
args: {
|
|
130
|
+
name: "itemsList",
|
|
131
|
+
schema: {
|
|
132
|
+
type: "array",
|
|
133
|
+
title: "Object array",
|
|
134
|
+
description: "Object array description",
|
|
135
|
+
additionalItems: {
|
|
136
|
+
type: "object",
|
|
137
|
+
title: "Item title",
|
|
138
|
+
description: "Item description",
|
|
139
|
+
properties: {
|
|
140
|
+
string: {
|
|
141
|
+
type: "string",
|
|
142
|
+
title: "String field",
|
|
143
|
+
description: "String description",
|
|
144
|
+
},
|
|
145
|
+
number: {
|
|
146
|
+
type: "number",
|
|
147
|
+
title: "Number field",
|
|
148
|
+
description: "Number description",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
uiSchema: {},
|
|
154
|
+
},
|
|
155
|
+
play: (_c) => __awaiter(void 0, [_c], void 0, function* ({ canvasElement }) {
|
|
156
|
+
const canvas = within(canvasElement);
|
|
157
|
+
// Add two items
|
|
158
|
+
const addButton = canvas.getByText("+");
|
|
159
|
+
yield userEvent.click(addButton);
|
|
160
|
+
yield userEvent.click(addButton);
|
|
161
|
+
const allDownArrows = canvas.queryAllByText("↓");
|
|
162
|
+
const allUpArrows = canvas.queryAllByText("↑");
|
|
163
|
+
const firstStringInput = canvasElement.querySelector('input[name="itemsList[0].string"]');
|
|
164
|
+
const secondStringInput = canvasElement.querySelector('input[name="itemsList[1].string"]');
|
|
165
|
+
const firstNumberInput = canvasElement.querySelector('input[name="itemsList[0].number"]');
|
|
166
|
+
const secondNumberInput = canvasElement.querySelector('input[name="itemsList[1].number"]');
|
|
167
|
+
const allLabelsAfterAdd = canvasElement.querySelectorAll('[name^="itemsList["]');
|
|
168
|
+
expect(allLabelsAfterAdd.length).toEqual(4);
|
|
169
|
+
yield userEvent.type(firstStringInput, "first");
|
|
170
|
+
yield userEvent.type(firstNumberInput, "1");
|
|
171
|
+
yield userEvent.type(secondStringInput, "second");
|
|
172
|
+
yield userEvent.type(secondNumberInput, "2");
|
|
173
|
+
// Check that moving items work
|
|
174
|
+
yield userEvent.click(allDownArrows[0]);
|
|
175
|
+
yield expect(firstStringInput).toHaveValue("second");
|
|
176
|
+
yield expect(firstNumberInput).toHaveValue(2);
|
|
177
|
+
yield expect(secondStringInput).toHaveValue("first");
|
|
178
|
+
yield expect(secondNumberInput).toHaveValue(1);
|
|
179
|
+
yield userEvent.click(allUpArrows[0]);
|
|
180
|
+
yield expect(firstStringInput).toHaveValue("first");
|
|
181
|
+
yield expect(firstNumberInput).toHaveValue(1);
|
|
182
|
+
yield expect(secondStringInput).toHaveValue("second");
|
|
183
|
+
yield expect(secondNumberInput).toHaveValue(2);
|
|
184
|
+
// Remove items
|
|
185
|
+
const removeButton = canvas.queryAllByText("-");
|
|
186
|
+
yield userEvent.click(removeButton[1]);
|
|
187
|
+
yield userEvent.click(removeButton[0]);
|
|
188
|
+
const allLabelsAfterRemove = canvasElement.querySelectorAll('[name^="itemsList["]');
|
|
189
|
+
expect(allLabelsAfterRemove.length).toEqual(0);
|
|
190
|
+
}),
|
|
191
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { StoryObj } from "@storybook/react";
|
|
2
|
+
import { BooleanField } from "./../BooleanField";
|
|
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 BooleanField>;
|