@nolwenture/formik-jsonschema-form 0.7.0 → 0.8.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 +52 -1
- package/dist/Form.stories.d.ts +1 -0
- package/dist/Form.stories.js +8 -0
- package/dist/Form.test.d.ts +1 -1
- package/dist/Form.test.js +3 -1
- package/dist/fields/ArrayField.js +1 -1
- package/dist/fields/MultipleChoiceField.d.ts +3 -0
- package/dist/fields/MultipleChoiceField.js +15 -0
- package/dist/fields/ObjectField.js +3 -1
- 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 +1673 -0
- package/dist/fields/stories/SchemaField.stories.js +39 -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/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/multipleChoiceSchema.d.ts +3 -0
- package/dist/sampleData/multipleChoiceSchema.js +112 -0
- package/dist/sampleData/requiredInputSchema.js +0 -5
- package/dist/styledComponents.d.ts +2 -1
- package/dist/styledComponents.js +15 -9
- package/dist/types.d.ts +15 -0
- 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 +28 -28
- 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 +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
export default {
|
|
15
|
+
title: "Fields/SchemaField",
|
|
16
|
+
component: SchemaField,
|
|
17
|
+
decorators: [
|
|
18
|
+
(Story) => (_jsx(Formik, { initialValues: {}, onSubmit: () => { }, children: _jsx(Form, { children: Story() }) })),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
const Template = {
|
|
22
|
+
args: {
|
|
23
|
+
name: "myField",
|
|
24
|
+
schema: {
|
|
25
|
+
type: "string",
|
|
26
|
+
},
|
|
27
|
+
uiSchema: {},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
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 }) {
|
|
31
|
+
const input = canvasElement.querySelector('input[name="myField"]');
|
|
32
|
+
yield userEvent.type(input, "Hello World");
|
|
33
|
+
expect(input).toHaveValue("Hello World");
|
|
34
|
+
}) });
|
|
35
|
+
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 }) {
|
|
36
|
+
const input = canvasElement.querySelector('input[name="myField"]');
|
|
37
|
+
yield userEvent.type(input, "123");
|
|
38
|
+
expect(input).toHaveValue(123);
|
|
39
|
+
}) });
|
|
@@ -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: "Components/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: "Components/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: "Components/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
|
+
};
|
|
@@ -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
|
-
|
|
16
|
-
|
|
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 = {} } =
|
|
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,112 @@
|
|
|
1
|
+
export const multipleChoiceSchema = {
|
|
2
|
+
title: "TITLE",
|
|
3
|
+
required: ["lastName", "requiredNestedObject", "requiredArray"],
|
|
4
|
+
properties: {
|
|
5
|
+
weather: {
|
|
6
|
+
type: "string",
|
|
7
|
+
title: "What is the weather like today? (no style given)",
|
|
8
|
+
oneOf: [
|
|
9
|
+
{ const: "sunny", title: "Sunny" },
|
|
10
|
+
{ const: "cloudy", title: "Cloudy" },
|
|
11
|
+
{ const: "rainy", title: "Rainy" },
|
|
12
|
+
{ const: "windy", title: "Windy" },
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
feeling: {
|
|
16
|
+
type: "string",
|
|
17
|
+
title: "How are you feeling today? (style: inline)",
|
|
18
|
+
oneOf: [
|
|
19
|
+
{ const: "happy", title: "Happy" },
|
|
20
|
+
{ const: "creative", title: "Creative" },
|
|
21
|
+
{ const: "tired", title: "Tired" },
|
|
22
|
+
{ const: "nope", title: "Please don't even ask..." },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
food: {
|
|
26
|
+
type: "string",
|
|
27
|
+
title: "What would you like to eat today? (style: oneColumn)",
|
|
28
|
+
anyOf: [
|
|
29
|
+
{ const: "breakfast", title: "Breakfast" },
|
|
30
|
+
{ const: "secondBreakfast", title: "What about second breakfast?" },
|
|
31
|
+
{ const: "elevenses", title: "Elevenses" },
|
|
32
|
+
{ const: "luncheon", title: "Luncheon" },
|
|
33
|
+
{ const: "afternoonTea", title: "Afternoon tea" },
|
|
34
|
+
{ const: "dinner", title: "Dinner" },
|
|
35
|
+
{ const: "supper", title: "Supper" },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
drinks: {
|
|
39
|
+
type: "string",
|
|
40
|
+
title: "What would you like to drink today? (style: twoColumns)",
|
|
41
|
+
anyOf: [
|
|
42
|
+
{ const: "water", title: "Water" },
|
|
43
|
+
{ const: "coffee", title: "Coffee" },
|
|
44
|
+
{ const: "tea", title: "Tea" },
|
|
45
|
+
{ const: "milk", title: "Milk" },
|
|
46
|
+
{ const: "nonMilk", title: "Non-dairy milk" },
|
|
47
|
+
{ const: "juice", title: "Juice" },
|
|
48
|
+
{ const: "beer", title: "Beer" },
|
|
49
|
+
{ const: "wine", title: "Wine" },
|
|
50
|
+
{ const: "cocktail", title: "Cocktail" },
|
|
51
|
+
{ const: "mocktail", title: "Mocktail" },
|
|
52
|
+
{ const: "somethingElse", title: "Something else" },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
flowers: {
|
|
56
|
+
type: "string",
|
|
57
|
+
title: "Pick some flowers (style: threeColumns)",
|
|
58
|
+
anyOf: [
|
|
59
|
+
{ const: "lily", title: "Lily" },
|
|
60
|
+
{ const: "rose", title: "Rose" },
|
|
61
|
+
{ const: "iris", title: "Iris" },
|
|
62
|
+
{ const: "tulip", title: "Tulip" },
|
|
63
|
+
{ const: "dahlia", title: "Dahlia" },
|
|
64
|
+
{ const: "daisy", title: "Daisy" },
|
|
65
|
+
{ const: "sunflower", title: "Sunflower" },
|
|
66
|
+
{ const: "orchid", title: "Orchid" },
|
|
67
|
+
{ const: "carnation", title: "Carnation" },
|
|
68
|
+
{ const: "poppy", title: "Poppy" },
|
|
69
|
+
{ const: "gerbera", title: "Gerbera" },
|
|
70
|
+
{ const: "camellia", title: "Camellia" },
|
|
71
|
+
{ const: "aster", title: "Aster" },
|
|
72
|
+
{ const: "marigold", title: "Marigold" },
|
|
73
|
+
{ const: "calendula", title: "Calendula" },
|
|
74
|
+
{ const: "lilac", title: "Lilac" },
|
|
75
|
+
{ const: "primrose", title: "Primrose" },
|
|
76
|
+
{ const: "daffodil", title: "Daffodil" },
|
|
77
|
+
{ const: "oleander", title: "Oleander" },
|
|
78
|
+
{ const: "buttercups", title: "Buttercups" },
|
|
79
|
+
{ const: "lotus", title: "Lotus" },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
export const multipleChoiceUiSchema = {
|
|
85
|
+
weather: {
|
|
86
|
+
"ui:field": "MultipleChoiceField",
|
|
87
|
+
},
|
|
88
|
+
feeling: {
|
|
89
|
+
"ui:field": "MultipleChoiceField",
|
|
90
|
+
"ui:option": {
|
|
91
|
+
style: "inline",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
food: {
|
|
95
|
+
"ui:field": "MultipleChoiceField",
|
|
96
|
+
"ui:option": {
|
|
97
|
+
style: "oneColumn",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
drinks: {
|
|
101
|
+
"ui:field": "MultipleChoiceField",
|
|
102
|
+
"ui:option": {
|
|
103
|
+
style: "twoColumns",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
flowers: {
|
|
107
|
+
"ui:field": "MultipleChoiceField",
|
|
108
|
+
"ui:option": {
|
|
109
|
+
style: "threeColumns",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
};
|
|
@@ -4,7 +4,6 @@ export const requiredInputSchema = {
|
|
|
4
4
|
properties: {
|
|
5
5
|
firstName: { type: "string", title: "First name" },
|
|
6
6
|
lastName: { type: "string", title: "Last name" },
|
|
7
|
-
feeling: { type: "boolean", title: "Feeling happy today?" },
|
|
8
7
|
vibes: { type: "boolean", title: "Turn on the good vibes" },
|
|
9
8
|
arrayOfObject: {
|
|
10
9
|
type: "array",
|
|
@@ -62,10 +61,6 @@ export const requiredInputSchema = {
|
|
|
62
61
|
},
|
|
63
62
|
};
|
|
64
63
|
export const requiredInputUiSchema = {
|
|
65
|
-
feeling: {
|
|
66
|
-
"ui:field": "RadioField",
|
|
67
|
-
"ui:label": { true: "yes", false: "no" },
|
|
68
|
-
},
|
|
69
64
|
vibes: {
|
|
70
65
|
"ui:field": "ToggleField",
|
|
71
66
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="hoist-non-react-statics" />
|
|
2
|
-
import type { ArrayFieldButtonProps, InfoFieldButtonProps, ObjectFieldButtonProps, RadioButtonFieldProps, ToggleSwitchFieldProps, SelectFieldProps } from "./types";
|
|
2
|
+
import type { ArrayFieldButtonProps, InfoFieldButtonProps, ObjectFieldButtonProps, RadioButtonFieldProps, ToggleSwitchFieldProps, SelectFieldProps, MultipleChoiceFieldProps } from "./types";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import * as formik from "formik";
|
|
5
5
|
export declare const Form: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<Omit<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>, "ref"> & React.RefAttributes<HTMLFormElement>, "ref"> & {
|
|
@@ -166,6 +166,7 @@ export declare const RadioButtonField: {
|
|
|
166
166
|
}, never>> & Omit<import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>>, keyof React.Component<any, {}, any>>;
|
|
167
167
|
};
|
|
168
168
|
};
|
|
169
|
+
export declare const MultipleChoiceField: ({ inputProps, name, className, enums, style, }: MultipleChoiceFieldProps) => import("react/jsx-runtime").JSX.Element;
|
|
169
170
|
export declare const MultiSelectContainer: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
170
171
|
export declare const MultiSelectInput: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<Omit<import("styled-components/dist/types").FastOmit<Omit<Omit<any, "as">, "ref"> & {
|
|
171
172
|
ref?: unknown;
|
package/dist/styledComponents.js
CHANGED
|
@@ -15,6 +15,7 @@ import styled from "styled-components";
|
|
|
15
15
|
import * as formik from "formik";
|
|
16
16
|
import { FormRegistry } from "./Form";
|
|
17
17
|
import { ArrayFieldButtonPurpose, ObjectFieldButtonPurpose } from "./types";
|
|
18
|
+
import { getStyle } from "./utils/getStyle";
|
|
18
19
|
export const Form = styled(formik.Form) ``;
|
|
19
20
|
export const FormButtonWrapper = styled.div ``;
|
|
20
21
|
export const FormButton = styled.button ``;
|
|
@@ -289,6 +290,15 @@ export const RadioButtonField = ({ label, inputProps, name, className, }) => {
|
|
|
289
290
|
var _a, _b;
|
|
290
291
|
return (_jsxs(RadioButtonFieldContainer, { className: className, children: [_jsxs(RadioButtonFieldElement, { children: [_jsx(RadioButtonFieldInput, Object.assign({}, inputProps, { name: name, type: "radio", value: "true" })), _jsx(ButtonLabelTrue, { children: (_a = label === null || label === void 0 ? void 0 : label.true) !== null && _a !== void 0 ? _a : "true" })] }), _jsxs(RadioButtonFieldElement, { children: [_jsx(RadioButtonFieldInput, Object.assign({}, inputProps, { name: name, type: "radio", value: "false" })), _jsx(ButtonLabelFalse, { children: (_b = label === null || label === void 0 ? void 0 : label.false) !== null && _b !== void 0 ? _b : "false" })] })] }));
|
|
291
292
|
};
|
|
293
|
+
const MultipleChoiceFieldContainer = styled(RadioButtonFieldContainer) ``;
|
|
294
|
+
const MultipleChoiceFieldElement = styled(RadioButtonFieldElement) ``;
|
|
295
|
+
const MultipleChoiceFieldInput = styled(RadioButtonFieldInput) ``;
|
|
296
|
+
const MultipleChoiceFieldLabel = styled(ButtonLabel) ``;
|
|
297
|
+
export const MultipleChoiceField = ({ inputProps, name, className, enums, style, }) => {
|
|
298
|
+
console.log("style", style);
|
|
299
|
+
console.log("function", getStyle(style !== null && style !== void 0 ? style : ""));
|
|
300
|
+
return (_jsx(MultipleChoiceFieldContainer, { className: className, style: getStyle(style !== null && style !== void 0 ? style : ""), children: enums.values.map((v) => (_jsxs(MultipleChoiceFieldElement, { children: [_jsx(MultipleChoiceFieldInput, Object.assign({}, inputProps, { name: name, type: enums.type === "multiSelect" ? "checkbox" : "radio", value: typeof v === "string" || typeof v === "number" ? v : v.const })), _jsx(MultipleChoiceFieldLabel, { children: typeof v === "string" || typeof v === "number" ? v : v.title })] }, typeof v === "string" || typeof v === "number" ? v : v.const))) }));
|
|
301
|
+
};
|
|
292
302
|
RadioButtonField.styles = {
|
|
293
303
|
RadioButtonFieldContainerStyle: RadioButtonFieldContainer,
|
|
294
304
|
RadioButtonFieldElementStyle: RadioButtonFieldElement,
|
|
@@ -352,22 +362,18 @@ export const SearchableMultiTagSelectField = ({ name, placeholder, enums, emptyS
|
|
|
352
362
|
const hasValue = enums === null || enums === void 0 ? void 0 : enums.values.map((value) => value.title.toLowerCase().includes(search.toLowerCase()));
|
|
353
363
|
return hasValue === null || hasValue === void 0 ? void 0 : hasValue.includes(true);
|
|
354
364
|
};
|
|
355
|
-
return (_jsx(formik.Field, { className: className, name: name, multiple: true, children: ({ field, form: {
|
|
365
|
+
return (_jsx(formik.Field, { className: className, name: name, multiple: true, children: ({ field, form: { setFieldValue }, meta, }) => {
|
|
356
366
|
var _a;
|
|
357
367
|
return (_jsxs(MultiSelectContainer, { className: className, children: [_jsx(FieldInput, Object.assign({ type: "hidden" }, field)), _jsx(MultiSelectInput, { className: className, type: "text", onChange: (e) => {
|
|
358
368
|
setSearch(e.currentTarget.value);
|
|
359
369
|
}, placeholder: placeholder, value: search }), _jsx(MultiSelectBadgeContainer, { children: ((_a = field.value) !== null && _a !== void 0 ? _a : []).map((value) => {
|
|
360
370
|
var _a;
|
|
361
|
-
return (_jsxs(MultiSelectBadge, { children: [(_a = enums === null || enums === void 0 ? void 0 : enums.values.find((option) => option.const === value)) === null || _a === void 0 ? void 0 : _a.title, _jsx(MultiSelectBadgeCloseElement, { onClick: () =>
|
|
362
|
-
[name]: field.value.filter((v) => v !== value),
|
|
363
|
-
}), children: closeElement !== null && closeElement !== void 0 ? closeElement : "x" })] }, value));
|
|
371
|
+
return (_jsxs(MultiSelectBadge, { children: [(_a = enums === null || enums === void 0 ? void 0 : enums.values.find((option) => option.const === value)) === null || _a === void 0 ? void 0 : _a.title, _jsx(MultiSelectBadgeCloseElement, { onClick: () => setFieldValue(name, field.value.filter((v) => v !== value)), children: closeElement !== null && closeElement !== void 0 ? closeElement : "x" })] }, value));
|
|
364
372
|
}) }), search.length > 0 && (_jsx(MultiSelectOptionList, { children: searchIncludedInEnum() ? (enums === null || enums === void 0 ? void 0 : enums.values.filter((item) => { var _a; return !((_a = field.value) !== null && _a !== void 0 ? _a : []).includes(item.const); }).filter((value) => value.title.toLowerCase().includes(search.toLowerCase())).map((option, index) => (_jsx(MultiSelectOptionListItem, { value: option.const, onClick: () => {
|
|
365
373
|
var _a;
|
|
366
|
-
|
|
367
|
-
[
|
|
368
|
-
|
|
369
|
-
],
|
|
370
|
-
});
|
|
374
|
+
setFieldValue(name, [
|
|
375
|
+
...new Set([...((_a = field.value) !== null && _a !== void 0 ? _a : []), option.const]),
|
|
376
|
+
]);
|
|
371
377
|
setSearch("");
|
|
372
378
|
}, children: option.title }, index)))) : (_jsx(MultiSelectOptionListWithoutItem, { children: emptySearchMessage !== null && emptySearchMessage !== void 0 ? emptySearchMessage : "No match found" })) })), meta.touched && meta.error && (_jsx("div", { className: "error", children: meta.error }))] }));
|
|
373
379
|
} }));
|
package/dist/types.d.ts
CHANGED
|
@@ -127,6 +127,7 @@ export type ComponentsType = {
|
|
|
127
127
|
FieldInput: React.FunctionComponent<any>;
|
|
128
128
|
RequiredIndicator: React.FunctionComponent<any>;
|
|
129
129
|
RadioButtonField: React.FunctionComponent<any>;
|
|
130
|
+
MultipleChoiceField: React.FunctionComponent<any>;
|
|
130
131
|
ToggleSwitchField: React.FunctionComponent<any>;
|
|
131
132
|
SearchableMultiTagSelectField: React.FunctionComponent<any>;
|
|
132
133
|
};
|
|
@@ -169,6 +170,20 @@ export type RadioButtonFieldProps = {
|
|
|
169
170
|
name?: React.ReactNode;
|
|
170
171
|
className?: string;
|
|
171
172
|
};
|
|
173
|
+
type Enum = {
|
|
174
|
+
type: string;
|
|
175
|
+
values: {
|
|
176
|
+
const: number | string;
|
|
177
|
+
title: string;
|
|
178
|
+
}[] | number[] | string[];
|
|
179
|
+
};
|
|
180
|
+
export type MultipleChoiceFieldProps = {
|
|
181
|
+
inputProps?: any;
|
|
182
|
+
name?: React.ReactNode;
|
|
183
|
+
className?: string;
|
|
184
|
+
enums: Enum;
|
|
185
|
+
style?: string;
|
|
186
|
+
};
|
|
172
187
|
export type ToggleSwitchFieldProps = {
|
|
173
188
|
inputProps: any;
|
|
174
189
|
name: React.ReactNode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|