@nolwenture/formik-jsonschema-form 0.22.4 → 0.23.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/dist/fields/ArrayField.js +1 -1
- package/dist/fields/BooleanField.js +1 -1
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/FileUploadField.js +1 -1
- package/dist/fields/MultipleChoiceField.js +17 -2
- package/dist/fields/NullField.js +2 -2
- package/dist/fields/ObjectField.js +3 -3
- package/dist/fields/RadioField.js +1 -1
- package/dist/fields/SearchMultiTagSelectField.js +1 -1
- package/dist/fields/SearchableSelectField.js +5 -2
- package/dist/fields/StringField.js +14 -7
- package/dist/fields/TextAreaField.js +1 -1
- package/dist/fields/ToggleField.js +1 -1
- package/dist/sampleData/multipleChoiceSchema.js +12 -0
- package/dist/styledComponents.d.ts +1 -1
- package/dist/styledComponents.js +28 -2
- package/dist/templates/ArrayFieldTemplate.js +21 -3
- package/dist/templates/CollapsibleObjectFieldTemplate.js +21 -3
- package/dist/templates/FieldTemplate.js +22 -5
- package/dist/templates/ObjectFieldTemplate.js +21 -3
- package/dist/types.d.ts +18 -10
- package/dist/utils/__tests/createValidationSchema.test.js +152 -0
- package/dist/utils/createValidationSchema.d.ts +5 -1
- package/dist/utils/createValidationSchema.js +34 -10
- package/dist/utils/getTypes.d.ts +1 -1
- package/package.json +1 -1
|
@@ -98,7 +98,7 @@ export const ArrayField = (props) => {
|
|
|
98
98
|
// Backwards compatibility ends
|
|
99
99
|
const { disabled } = useExtractUiSchema(uiSchema);
|
|
100
100
|
const valueArray = Array.isArray(value) ? value : undefined;
|
|
101
|
-
return schema.anyOf !== undefined ? (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", multiple: (enums === null || enums === void 0 ? void 0 : enums.type) === "multiSelect", children: enums === null || enums === void 0 ? void 0 : enums.values.map((option, index) => (_jsx("option", { value: option.const, children: option.title }, index))) })) }))) : (_jsxs(ArrayFieldTemplate, Object.assign({}, props, { children: [_jsxs(ArrayFieldList, { children: [(items || []).map((itemSchema, index) => (_jsx(ArrayFieldItem, { children: _jsx(SchemaField, { name: `${name}[${index}]`, schema: typeof itemSchema !== "boolean" ? itemSchema : {}, uiSchema: (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[index]) || (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema.items),
|
|
101
|
+
return schema.anyOf !== undefined ? (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-array": name, children: _jsx(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", multiple: (enums === null || enums === void 0 ? void 0 : enums.type) === "multiSelect", children: enums === null || enums === void 0 ? void 0 : enums.values.map((option, index) => (_jsx("option", { value: option.const, children: option.title }, index))) })) }))) : (_jsxs(ArrayFieldTemplate, Object.assign({}, props, { "data-fjsf-array": name, children: [_jsxs(ArrayFieldList, { children: [(items || []).map((itemSchema, index) => (_jsx(ArrayFieldItem, { children: _jsx(SchemaField, { name: `${name}[${index}]`, schema: typeof itemSchema !== "boolean" ? itemSchema : {}, uiSchema: (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[index]) || (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema.items),
|
|
102
102
|
// "true" will prevent validation for required array fields
|
|
103
103
|
// since a value always exists in the array
|
|
104
104
|
forceDefaultValue: false }) }, index))), ((valueArray === null || valueArray === void 0 ? void 0 : valueArray.slice(items.length)) || []).map((_, index) => {
|
|
@@ -7,5 +7,5 @@ export const BooleanField = (props) => {
|
|
|
7
7
|
useSetDefault(props);
|
|
8
8
|
const { name, uiSchema } = props;
|
|
9
9
|
const { label, props: inputProps } = useExtractUiSchema(uiSchema);
|
|
10
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsxs(BooleanFieldWrapper, { children: [_jsx(FieldInput, Object.assign({}, inputProps, { name: name, type: "checkbox" })), label] }) })));
|
|
10
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-boolean": name, children: _jsxs(BooleanFieldWrapper, { children: [_jsx(FieldInput, Object.assign({}, inputProps, { name: name, type: "checkbox" })), label] }) })));
|
|
11
11
|
};
|
package/dist/fields/DateField.js
CHANGED
|
@@ -7,5 +7,5 @@ export const DateField = (props) => {
|
|
|
7
7
|
useSetDefault(props);
|
|
8
8
|
const { name, uiSchema } = props;
|
|
9
9
|
const { props: inputProps } = useExtractUiSchema(uiSchema);
|
|
10
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FieldInput, Object.assign({}, inputProps, { name: name, type: "date" })) })));
|
|
10
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-string": name, children: _jsx(FieldInput, Object.assign({}, inputProps, { name: name, type: "date" })) })));
|
|
11
11
|
};
|
|
@@ -66,5 +66,5 @@ export const FileUploadField = (props) => {
|
|
|
66
66
|
setFieldValue(name, null);
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FileUploadField, Object.assign({}, inputProps, { name: name, value: value, handleRemoveFile: handleRemoveFile, uploadedFile: uploadedFile, setFieldValue: setFieldValue, handleFileUpload: handleFileUpload, uploadProgress: uploadProgress, removeButton: uiSchema["ui:removeButton"], uploadButtonText: uiSchema["ui:uploadButtonText"], infoText: uiSchema["ui:infoText"] })) })));
|
|
69
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-object": name, children: _jsx(FileUploadField, Object.assign({}, inputProps, { name: name, value: value, handleRemoveFile: handleRemoveFile, uploadedFile: uploadedFile, setFieldValue: setFieldValue, handleFileUpload: handleFileUpload, uploadProgress: uploadProgress, removeButton: uiSchema["ui:removeButton"], uploadButtonText: uiSchema["ui:uploadButtonText"], infoText: uiSchema["ui:infoText"] })) })));
|
|
70
70
|
};
|
|
@@ -3,11 +3,26 @@ import { useContext, useMemo } from "react";
|
|
|
3
3
|
import { FormRegistry } from "../Form";
|
|
4
4
|
import { useExtractUiSchema, useSetDefault } from "../hooks";
|
|
5
5
|
import { getEnums } from "../utils/getEnums";
|
|
6
|
+
import { getIn, useFormikContext } from "formik";
|
|
6
7
|
export const MultipleChoiceField = (props) => {
|
|
7
8
|
const { templates: { FieldTemplate }, components: { MultipleChoiceField }, } = useContext(FormRegistry);
|
|
8
9
|
useSetDefault(props);
|
|
9
|
-
const { name, uiSchema } = props;
|
|
10
|
+
const { name, uiSchema, schema } = props;
|
|
10
11
|
const { props: inputProps, style } = useExtractUiSchema(uiSchema);
|
|
11
12
|
const enums = useMemo(() => getEnums(props), [props]);
|
|
12
|
-
|
|
13
|
+
const { values } = useFormikContext();
|
|
14
|
+
const value = getIn(values, name);
|
|
15
|
+
const form = useFormikContext();
|
|
16
|
+
const fieldDataAttribute = (() => {
|
|
17
|
+
switch (schema === null || schema === void 0 ? void 0 : schema.type) {
|
|
18
|
+
case "array":
|
|
19
|
+
return { "data-fjsf-array": name };
|
|
20
|
+
case "number":
|
|
21
|
+
return { "data-fjsf-number": name };
|
|
22
|
+
case "string":
|
|
23
|
+
default:
|
|
24
|
+
return { "data-fjsf-string": name };
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, fieldDataAttribute, { children: _jsx(MultipleChoiceField, { inputProps: inputProps, name: name, enums: enums, style: style, value: value, form: form }) })));
|
|
13
28
|
};
|
package/dist/fields/NullField.js
CHANGED
|
@@ -3,6 +3,6 @@ import { useContext } from "react";
|
|
|
3
3
|
import { FormRegistry } from "../Form";
|
|
4
4
|
export const NullField = (props) => {
|
|
5
5
|
const { templates: { FieldTemplate }, } = useContext(FormRegistry);
|
|
6
|
-
const { name } = props;
|
|
7
|
-
return _jsx(FieldTemplate, Object.assign({}, props));
|
|
6
|
+
const { name, uiSchema } = props;
|
|
7
|
+
return _jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-null": name }));
|
|
8
8
|
};
|
|
@@ -50,15 +50,15 @@ export const ObjectField = (props) => {
|
|
|
50
50
|
? CollapsibleObjectFieldTemplate
|
|
51
51
|
: ObjectFieldTemplate;
|
|
52
52
|
const orderedProperties = orderByUiSchema(uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema["ui:order"], Object.keys(properties || {}));
|
|
53
|
-
return (_jsxs(FieldTemplateComponent, Object.assign({}, props, { children: [(orderedProperties !== null && orderedProperties !== void 0 ? orderedProperties : Object.keys(properties || {}))
|
|
53
|
+
return (_jsxs(FieldTemplateComponent, Object.assign({}, props, { "data-fjsf-object": name, children: [(orderedProperties !== null && orderedProperties !== void 0 ? orderedProperties : Object.keys(properties || {}))
|
|
54
54
|
.map((childName) => {
|
|
55
55
|
const childSchema = properties === null || properties === void 0 ? void 0 : properties[childName];
|
|
56
56
|
if (!childSchema)
|
|
57
57
|
return undefined;
|
|
58
|
-
return (_jsx(SchemaField, {
|
|
58
|
+
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));
|
|
59
59
|
})
|
|
60
60
|
.filter((x) => x), (!hideUnmarkedAdditionalProperties || additionalProperties) &&
|
|
61
|
-
Object.entries(additionalValues || {}).map(([childName]) => (_jsxs(ObjectFieldItem, { children: [_jsx(SchemaField, {
|
|
61
|
+
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"
|
|
62
62
|
? schema.additionalProperties
|
|
63
63
|
: {}, uiSchema: uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema[childName] }, childName), _jsx(ObjectFieldItemButtons, { children: getObjectFieldButton({
|
|
64
64
|
purpose: ObjectFieldButtonPurpose.REMOVE,
|
|
@@ -7,5 +7,5 @@ export const RadioField = (props) => {
|
|
|
7
7
|
useSetDefault(props);
|
|
8
8
|
const { name, uiSchema } = props;
|
|
9
9
|
const { label, props: inputProps } = useExtractUiSchema(uiSchema);
|
|
10
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(RadioButtonField, { inputProps: inputProps, label: label, name: name }) })));
|
|
10
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-boolean": name, children: _jsx(RadioButtonField, { inputProps: inputProps, label: label, name: name }) })));
|
|
11
11
|
};
|
|
@@ -10,5 +10,5 @@ export const SearchMultiTagSelectField = (props) => {
|
|
|
10
10
|
const { name, uiSchema } = props;
|
|
11
11
|
const { placeholder, emptySearchMessage, props: inputProps, } = useExtractUiSchema(uiSchema);
|
|
12
12
|
const enums = useMemo(() => getEnums(props), [props]);
|
|
13
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(SearchableMultiTagSelectField, Object.assign({}, inputProps, { name: name, placeholder: placeholder, emptySearchMessage: emptySearchMessage, enums: enums })) })));
|
|
13
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-array": name, children: _jsx(SearchableMultiTagSelectField, Object.assign({}, inputProps, { name: name, placeholder: placeholder, emptySearchMessage: emptySearchMessage, enums: enums })) })));
|
|
14
14
|
};
|
|
@@ -32,7 +32,7 @@ export const SearchableSelectField = (props) => {
|
|
|
32
32
|
}, [isActive]);
|
|
33
33
|
return (_jsx(SearchListOption, Object.assign({ ref: ref }, rest, { children: children })));
|
|
34
34
|
};
|
|
35
|
-
const { name, uiSchema, onChange, onSelect } = props;
|
|
35
|
+
const { name, uiSchema, schema, onChange, onSelect } = props;
|
|
36
36
|
const { props: inputProps, placeholder, clearButtonText, expandText, notFoundMessage, hoverColors = { backgroundColor: "", color: "" }, hideClearButton = false, dropdown, } = useExtractUiSchema(uiSchema);
|
|
37
37
|
const dropdownOptions = Object.assign({ show: false, height: "", width: "", perPage: 20 }, dropdown);
|
|
38
38
|
const [{ value }, __, { setValue }] = useField(name);
|
|
@@ -119,7 +119,10 @@ export const SearchableSelectField = (props) => {
|
|
|
119
119
|
const filteredOptions = options.filter((option) => { var _a, _b; return ((_b = (_a = option.title) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : "").includes(searchTerm.toLowerCase()); });
|
|
120
120
|
const showMoreOption = displayedItems.length < filteredOptions.length &&
|
|
121
121
|
displayedItems.length >= dropdownOptions.perPage;
|
|
122
|
-
|
|
122
|
+
const fieldDataAttribute = schema.type === "string"
|
|
123
|
+
? { "data-fjsf-string": name }
|
|
124
|
+
: { "data-fjsf-number": name };
|
|
125
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, fieldDataAttribute, { children: _jsxs(SearchableSelectContainer, { ref: containerRef, onBlur: handleBlur, children: [_jsxs(SearchInputContainer, { children: [_jsx(SearchInput, Object.assign({}, inputProps, { name: name, type: "text", placeholder: placeholder, value: searchTerm, onChange: handleSearchChange, onFocus: handleFocus })), searchTerm && !hideClearButton && (_jsx(SearchClearButton, { onClick: handleClear, children: clearButtonText !== null && clearButtonText !== void 0 ? clearButtonText : "x" }))] }), showOptions && (_jsxs(SearchDropdownContainer, { role: "dropdown-list", ref: moreRef, style: {
|
|
123
126
|
maxHeight: dropdownOptions.height,
|
|
124
127
|
width: dropdownOptions.width,
|
|
125
128
|
}, children: [displayedItems.length > 0 ? (displayedItems.map((item, index) => (_jsx(ListOption, { isActive: index === selectedItemIndex, className: index === selectedItemIndex ? "selected-item-active" : "", style: index === selectedItemIndex
|
|
@@ -3,6 +3,7 @@ import { useContext, useMemo } from "react";
|
|
|
3
3
|
import { FormRegistry } from "../Form";
|
|
4
4
|
import { getEnums } from "../utils/getEnums";
|
|
5
5
|
import { useExtractUiSchema, useSetDefault } from "../hooks";
|
|
6
|
+
import { useFormikContext } from "formik";
|
|
6
7
|
export const StringField = (props) => {
|
|
7
8
|
var _a;
|
|
8
9
|
const { templates: { FieldTemplate }, components: { FieldInput }, } = useContext(FormRegistry);
|
|
@@ -10,16 +11,22 @@ export const StringField = (props) => {
|
|
|
10
11
|
const { name, uiSchema = {}, schema } = props;
|
|
11
12
|
const { type, placeholder, props: inputProps } = useExtractUiSchema(uiSchema);
|
|
12
13
|
const enums = useMemo(() => getEnums(props), [props]);
|
|
14
|
+
const form = useFormikContext();
|
|
15
|
+
const fieldDataAttribute = schema.type === "string"
|
|
16
|
+
? { "data-fjsf-string": name }
|
|
17
|
+
: { "data-fjsf-number": name };
|
|
13
18
|
switch (enums === null || enums === void 0 ? void 0 : enums.type) {
|
|
14
19
|
case "select":
|
|
15
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsxs(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, fieldDataAttribute, { children: _jsxs(FieldInput, Object.assign({ type }, inputProps, { name: name, formikAs: "select", onChange: (e) => {
|
|
21
|
+
const selectedOption = enums === null || enums === void 0 ? void 0 : enums.values.find((option) => option.const.toString() === e.target.value);
|
|
22
|
+
const selectedValue = selectedOption
|
|
23
|
+
? selectedOption.const
|
|
24
|
+
: e.target.value;
|
|
25
|
+
form === null || form === void 0 ? void 0 : form.setFieldValue(name, selectedValue);
|
|
26
|
+
}, children: [_jsx("option", Object.assign({ value: "", disabled: false,
|
|
27
|
+
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.toString(), children: option.title }, index)))] })) })));
|
|
21
28
|
default:
|
|
22
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(FieldInput, Object.assign({ type,
|
|
29
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, fieldDataAttribute, { children: _jsx(FieldInput, Object.assign({ type,
|
|
23
30
|
placeholder }, inputProps, { name: name })) })));
|
|
24
31
|
}
|
|
25
32
|
};
|
|
@@ -7,5 +7,5 @@ export const TextAreaField = (props) => {
|
|
|
7
7
|
useSetDefault(props);
|
|
8
8
|
const { name, uiSchema } = props;
|
|
9
9
|
const { props: inputProps } = useExtractUiSchema(uiSchema);
|
|
10
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(TextAreaInputField, { inputProps: inputProps, name: name }) })));
|
|
10
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-string": name, children: _jsx(TextAreaInputField, { inputProps: inputProps, name: name }) })));
|
|
11
11
|
};
|
|
@@ -7,5 +7,5 @@ export const ToggleField = (props) => {
|
|
|
7
7
|
useSetDefault(props);
|
|
8
8
|
const { name, uiSchema } = props;
|
|
9
9
|
const { props: inputProps } = useExtractUiSchema(uiSchema);
|
|
10
|
-
return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(ToggleSwitchField, Object.assign({}, inputProps, { name: name })) })));
|
|
10
|
+
return (_jsx(FieldTemplate, Object.assign({}, props, { "data-fjsf-boolean": name, children: _jsx(ToggleSwitchField, Object.assign({}, inputProps, { name: name })) })));
|
|
11
11
|
};
|
|
@@ -12,6 +12,17 @@ export const multipleChoiceSchema = {
|
|
|
12
12
|
{ const: "windy", title: "Windy" },
|
|
13
13
|
],
|
|
14
14
|
},
|
|
15
|
+
schoolAge: {
|
|
16
|
+
type: "number",
|
|
17
|
+
title: "At what age do you start school in your country? (no style given)",
|
|
18
|
+
oneOf: [
|
|
19
|
+
{ const: 1, title: "5" },
|
|
20
|
+
{ const: 2, title: "6" },
|
|
21
|
+
{ const: 3, title: "7" },
|
|
22
|
+
{ const: 4, title: "some other age" },
|
|
23
|
+
],
|
|
24
|
+
default: 2,
|
|
25
|
+
},
|
|
15
26
|
feeling: {
|
|
16
27
|
type: "string",
|
|
17
28
|
title: "How are you feeling today? (style: inline)",
|
|
@@ -85,6 +96,7 @@ export const multipleChoiceUiSchema = {
|
|
|
85
96
|
weather: {
|
|
86
97
|
"ui:field": "MultipleChoiceField",
|
|
87
98
|
},
|
|
99
|
+
schoolAge: { "ui:field": "MultipleChoiceField" },
|
|
88
100
|
feeling: {
|
|
89
101
|
"ui:field": "MultipleChoiceField",
|
|
90
102
|
"ui:options": {
|
|
@@ -151,7 +151,7 @@ export declare const MultipleChoiceFieldLabel: import("styled-components/dist/ty
|
|
|
151
151
|
ref?: ((instance: HTMLSpanElement | null) => void | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | React.RefObject<HTMLSpanElement> | null | undefined;
|
|
152
152
|
}, never>> & string;
|
|
153
153
|
export declare const MultipleChoiceField: {
|
|
154
|
-
({ inputProps, name, className, enums, style, }: MultipleChoiceFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
154
|
+
({ inputProps, name, className, enums, style, value, form, }: MultipleChoiceFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
155
155
|
styles: {
|
|
156
156
|
MultipleChoiceFieldElementStyle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("styled-components").FastOmit<React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, never>, "ref"> & {
|
|
157
157
|
ref?: ((instance: HTMLLabelElement | null) => void | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | React.RefObject<HTMLLabelElement> | null | undefined;
|
package/dist/styledComponents.js
CHANGED
|
@@ -285,8 +285,34 @@ export const MultipleChoiceFieldContainer = styled(RadioButtonFieldContainer) ``
|
|
|
285
285
|
export const MultipleChoiceFieldElement = styled(RadioButtonFieldElement) ``;
|
|
286
286
|
export const MultipleChoiceFieldInput = styled(RadioButtonFieldInput) ``;
|
|
287
287
|
export const MultipleChoiceFieldLabel = styled(ButtonLabel) ``;
|
|
288
|
-
export const MultipleChoiceField = ({ inputProps, name, className, enums, style, }) => {
|
|
289
|
-
|
|
288
|
+
export const MultipleChoiceField = ({ inputProps, name, className, enums, style, value, form, }) => {
|
|
289
|
+
const isMultiSelect = enums.type === "multiSelect";
|
|
290
|
+
const currentValue = isMultiSelect
|
|
291
|
+
? Array.isArray(value)
|
|
292
|
+
? value
|
|
293
|
+
: []
|
|
294
|
+
: value;
|
|
295
|
+
const isChecked = (optionValue) => {
|
|
296
|
+
if (isMultiSelect && Array.isArray(currentValue)) {
|
|
297
|
+
return currentValue.includes(optionValue);
|
|
298
|
+
}
|
|
299
|
+
return currentValue === optionValue;
|
|
300
|
+
};
|
|
301
|
+
const handleChange = (optionValue) => {
|
|
302
|
+
if (isMultiSelect && Array.isArray(currentValue)) {
|
|
303
|
+
const newValue = isChecked(optionValue)
|
|
304
|
+
? currentValue.filter((v) => v !== optionValue)
|
|
305
|
+
: [...currentValue, optionValue];
|
|
306
|
+
form === null || form === void 0 ? void 0 : form.setFieldValue(name, newValue);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
form === null || form === void 0 ? void 0 : form.setFieldValue(name, optionValue);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
return (_jsx(MultipleChoiceFieldContainer, { className: className, style: getStyle(style !== null && style !== void 0 ? style : ""), children: enums.values.map((v) => {
|
|
313
|
+
const optionValue = v.const;
|
|
314
|
+
return (_jsxs(MultipleChoiceFieldElement, { "data-fjsf-enum-value": optionValue, children: [_jsx(MultipleChoiceFieldInput, Object.assign({}, inputProps, { name: name, type: isMultiSelect ? "checkbox" : "radio", value: optionValue !== undefined ? optionValue.toString() : "", checked: isChecked(optionValue), onChange: () => handleChange(optionValue) })), _jsx(MultipleChoiceFieldLabel, { children: v.title })] }, optionValue !== undefined ? optionValue.toString() : "undefined"));
|
|
315
|
+
}) }));
|
|
290
316
|
};
|
|
291
317
|
MultipleChoiceField.styles = {
|
|
292
318
|
MultipleChoiceFieldElementStyle: MultipleChoiceFieldElement,
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { useContext } from "react";
|
|
3
14
|
import { FormRegistry } from "../Form";
|
|
@@ -6,11 +17,18 @@ import { useExtractUiSchema } from "../hooks";
|
|
|
6
17
|
import { RequiredIndicator } from "../styledComponents";
|
|
7
18
|
export const ArrayFieldTemplate = (props) => {
|
|
8
19
|
const { components: { ArrayFieldWrapper, ArrayFieldTitle, FieldDescription, ErrorMessage, FieldHelp, InfoFieldButton, }, } = useContext(FormRegistry);
|
|
9
|
-
const { name, uiSchema, children } = props;
|
|
20
|
+
const { name, uiSchema, children } = props, otherProps = __rest(props, ["name", "uiSchema", "children"]);
|
|
10
21
|
const title = getTitle(props);
|
|
11
22
|
const description = getDescription(props);
|
|
12
23
|
const { help, info,
|
|
13
24
|
//TODO: This is not right
|
|
14
|
-
props: { wrapper:
|
|
15
|
-
|
|
25
|
+
props: { wrapper: wrapperProps = {}, title: titleProps, description: descriptionProps, help: helpProps, error: errorProps, } = {}, } = useExtractUiSchema(uiSchema);
|
|
26
|
+
const dataAttributes = Object.keys(otherProps)
|
|
27
|
+
.filter((key) => key.startsWith("data-"))
|
|
28
|
+
.reduce((acc, key) => {
|
|
29
|
+
acc[key] = otherProps[key];
|
|
30
|
+
return acc;
|
|
31
|
+
}, {});
|
|
32
|
+
const mergedWrapperProps = Object.assign(Object.assign({}, wrapperProps), dataAttributes);
|
|
33
|
+
return (_jsxs(ArrayFieldWrapper, Object.assign({}, mergedWrapperProps, { children: [title && (_jsxs(ArrayFieldTitle, Object.assign({}, titleProps, { htmlFor: name, children: [title, (props === null || props === void 0 ? void 0 : props.required) && _jsx(RequiredIndicator, { children: "*" })] }))), info && _jsx(InfoFieldButton, Object.assign({}, info)), description && (_jsx(FieldDescription, Object.assign({}, descriptionProps, { children: description }))), children, help && _jsx(FieldHelp, Object.assign({}, helpProps, { children: help })), _jsx(ErrorMessage, Object.assign({}, errorProps, { name: name }))] })));
|
|
16
34
|
};
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { useContext, useState } from "react";
|
|
3
14
|
import { FormRegistry } from "../Form";
|
|
@@ -6,15 +17,22 @@ import { useExtractUiSchema } from "../hooks";
|
|
|
6
17
|
import { CollapsibleChildWrapper, CollapsibleToggle, Icon, RequiredIndicator, } from "../styledComponents";
|
|
7
18
|
export const CollapsibleObjectFieldTemplate = (props) => {
|
|
8
19
|
const { components: { FieldDescription, CollapsibleObjectFieldTitle, CollapsibleObjectFieldWrapper, FieldHelp, InfoFieldButton, }, } = useContext(FormRegistry);
|
|
9
|
-
const { children, name, uiSchema } = props;
|
|
20
|
+
const { children, name, uiSchema } = props, otherProps = __rest(props, ["children", "name", "uiSchema"]);
|
|
10
21
|
const title = getTitle(props);
|
|
11
22
|
const description = getDescription(props);
|
|
12
23
|
const { help, info,
|
|
13
24
|
//TODO: This is not right
|
|
14
|
-
props: { wrapper: wrapperProps, title: titleProps, description: descriptionProps, help: helpProps, isCollapsedOnLoad = true, openIcon, collapsedIcon, ariaLabel,
|
|
25
|
+
props: { wrapper: wrapperProps = {}, title: titleProps, description: descriptionProps, help: helpProps, isCollapsedOnLoad = true, openIcon, collapsedIcon, ariaLabel,
|
|
15
26
|
// error: errorProps,
|
|
16
27
|
} = {}, } = useExtractUiSchema(uiSchema);
|
|
28
|
+
const dataAttributes = Object.keys(otherProps)
|
|
29
|
+
.filter((key) => key.startsWith("data-"))
|
|
30
|
+
.reduce((acc, key) => {
|
|
31
|
+
acc[key] = otherProps[key];
|
|
32
|
+
return acc;
|
|
33
|
+
}, {});
|
|
34
|
+
const mergedWrapperProps = Object.assign(Object.assign({}, wrapperProps), dataAttributes);
|
|
17
35
|
const [collapsed, setCollapsed] = useState(isCollapsedOnLoad);
|
|
18
36
|
const hide = collapsed;
|
|
19
|
-
return (_jsxs(CollapsibleObjectFieldWrapper, Object.assign({},
|
|
37
|
+
return (_jsxs(CollapsibleObjectFieldWrapper, Object.assign({}, mergedWrapperProps, { children: [title && (_jsx(CollapsibleToggle, { onClick: () => setCollapsed(!collapsed), ariaExpanded: !hide, ariaLabel: ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : name, children: _jsxs(CollapsibleObjectFieldTitle, Object.assign({ style: { display: "flex", alignItems: "center" } }, titleProps, { children: [title, (props === null || props === void 0 ? void 0 : props.required) && _jsx(RequiredIndicator, { children: "*" }), _jsx(Icon, { children: hide ? collapsedIcon !== null && collapsedIcon !== void 0 ? collapsedIcon : "▸" : openIcon !== null && openIcon !== void 0 ? openIcon : "▾" })] })) })), info && _jsx(InfoFieldButton, Object.assign({}, info)), description && (_jsx(FieldDescription, Object.assign({}, descriptionProps, { children: description }))), !hide && _jsx(CollapsibleChildWrapper, { children: children }), help && _jsx(FieldHelp, Object.assign({}, helpProps, { children: help }))] })));
|
|
20
38
|
};
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { useContext } from "react";
|
|
3
14
|
import { FormRegistry } from "../Form";
|
|
@@ -5,11 +16,17 @@ import { getDescription, getTitle } from "../utils";
|
|
|
5
16
|
import { useExtractUiSchema } from "../hooks";
|
|
6
17
|
export const FieldTemplate = (props) => {
|
|
7
18
|
const { components: { FieldWrapper, FieldTitle, FieldDescription, ErrorMessage, FieldHelp, RequiredIndicator, InfoFieldButton, }, } = useContext(FormRegistry);
|
|
8
|
-
const { name, uiSchema, children } = props;
|
|
19
|
+
const { name, uiSchema, children } = props, otherProps = __rest(props, ["name", "uiSchema", "children"]);
|
|
9
20
|
const title = getTitle(props);
|
|
10
21
|
const description = getDescription(props);
|
|
11
|
-
const { help, info,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
const { help, info, props: { wrapper: wrapperProps = {}, title: titleProps, description: descriptionProps, help: helpProps, error: errorProps, } = {}, } = useExtractUiSchema(uiSchema);
|
|
23
|
+
const dataAttributes = Object.keys(otherProps)
|
|
24
|
+
.filter((key) => key.startsWith("data-"))
|
|
25
|
+
.reduce((acc, key) => {
|
|
26
|
+
acc[key] = otherProps[key];
|
|
27
|
+
return acc;
|
|
28
|
+
}, {});
|
|
29
|
+
// Merge the wrapperProps with the filtered dataAttributes
|
|
30
|
+
const mergedWrapperProps = Object.assign(Object.assign({}, wrapperProps), dataAttributes);
|
|
31
|
+
return (_jsxs(FieldWrapper, Object.assign({}, mergedWrapperProps, { children: [title && (_jsxs(FieldTitle, Object.assign({}, titleProps, { htmlFor: name, children: [title, props.required && _jsx(RequiredIndicator, { children: "*" })] }))), info && _jsx(InfoFieldButton, Object.assign({}, info)), description && (_jsx(FieldDescription, Object.assign({}, descriptionProps, { children: description }))), children, help && _jsx(FieldHelp, Object.assign({}, helpProps, { children: help })), _jsx(ErrorMessage, Object.assign({}, errorProps, { name: name }))] })));
|
|
15
32
|
};
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { useContext } from "react";
|
|
3
14
|
import { FormRegistry } from "../Form";
|
|
@@ -6,13 +17,20 @@ import { useExtractUiSchema } from "../hooks";
|
|
|
6
17
|
import { RequiredIndicator } from "../styledComponents";
|
|
7
18
|
export const ObjectFieldTemplate = (props) => {
|
|
8
19
|
const { components: { FieldDescription, ObjectFieldTitle, ObjectFieldWrapper, FieldHelp, InfoFieldButton, }, } = useContext(FormRegistry);
|
|
9
|
-
const { children, uiSchema } = props;
|
|
20
|
+
const { children, uiSchema } = props, otherProps = __rest(props, ["children", "uiSchema"]);
|
|
10
21
|
const title = getTitle(props);
|
|
11
22
|
const description = getDescription(props);
|
|
12
23
|
const { help, info,
|
|
13
24
|
//TODO: This is not right
|
|
14
|
-
props: { wrapper: wrapperProps, title: titleProps, description: descriptionProps, help: helpProps,
|
|
25
|
+
props: { wrapper: wrapperProps = {}, title: titleProps, description: descriptionProps, help: helpProps,
|
|
15
26
|
// error: errorProps,
|
|
16
27
|
} = {}, } = useExtractUiSchema(uiSchema);
|
|
17
|
-
|
|
28
|
+
const dataAttributes = Object.keys(otherProps)
|
|
29
|
+
.filter((key) => key.startsWith("data-"))
|
|
30
|
+
.reduce((acc, key) => {
|
|
31
|
+
acc[key] = otherProps[key];
|
|
32
|
+
return acc;
|
|
33
|
+
}, {});
|
|
34
|
+
const mergedWrapperProps = Object.assign(Object.assign({}, wrapperProps), dataAttributes);
|
|
35
|
+
return (_jsxs(ObjectFieldWrapper, Object.assign({}, mergedWrapperProps, { children: [title && (_jsxs(ObjectFieldTitle, Object.assign({}, titleProps, { children: [title, (props === null || props === void 0 ? void 0 : props.required) && _jsx(RequiredIndicator, { children: "*" })] }))), info && _jsx(InfoFieldButton, Object.assign({}, info)), description && (_jsx(FieldDescription, Object.assign({}, descriptionProps, { children: description }))), children, help && _jsx(FieldHelp, Object.assign({}, helpProps, { children: help }))] })));
|
|
18
36
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FormikErrors } from "formik";
|
|
1
|
+
import { FormikContextType, FormikErrors } from "formik";
|
|
2
2
|
import type { JSONSchema7, JSONSchema7Definition } from "json-schema";
|
|
3
3
|
import React, { MouseEventHandler } from "react";
|
|
4
4
|
export type FormProps<T> = React.PropsWithChildren<{
|
|
@@ -73,6 +73,7 @@ export type Field = React.FunctionComponent<FieldProps>;
|
|
|
73
73
|
export type FieldTemplateProps = React.PropsWithChildren<{
|
|
74
74
|
name: string;
|
|
75
75
|
schema: Schema;
|
|
76
|
+
[key: string]: any;
|
|
76
77
|
uiSchema?: UiSchema;
|
|
77
78
|
hidden?: boolean;
|
|
78
79
|
required?: boolean;
|
|
@@ -82,6 +83,7 @@ export type FieldTemplateProps = React.PropsWithChildren<{
|
|
|
82
83
|
export type ArrayFieldTemplateProps = React.PropsWithChildren<{
|
|
83
84
|
name: string;
|
|
84
85
|
schema: Schema;
|
|
86
|
+
[key: string]: any;
|
|
85
87
|
uiSchema?: UiSchema;
|
|
86
88
|
hidden?: boolean;
|
|
87
89
|
required?: boolean;
|
|
@@ -93,9 +95,11 @@ export type ObjectFieldTemplateProps = {
|
|
|
93
95
|
uiSchema?: UiSchema;
|
|
94
96
|
required?: boolean;
|
|
95
97
|
children: React.ReactNode;
|
|
98
|
+
[key: string]: any;
|
|
96
99
|
};
|
|
97
100
|
export type CollapsibleObjectFieldTemplateProps = {
|
|
98
101
|
schema: Schema;
|
|
102
|
+
[key: string]: any;
|
|
99
103
|
uiSchema?: UiSchema;
|
|
100
104
|
required?: boolean;
|
|
101
105
|
children: React.ReactNode;
|
|
@@ -191,19 +195,20 @@ export type TextAreaInputFieldProps = {
|
|
|
191
195
|
name: string;
|
|
192
196
|
className?: string;
|
|
193
197
|
};
|
|
194
|
-
type Enum = {
|
|
195
|
-
type: string;
|
|
196
|
-
values: {
|
|
197
|
-
const: number | string;
|
|
198
|
-
title: string;
|
|
199
|
-
}[] | number[] | string[];
|
|
200
|
-
};
|
|
201
198
|
export type MultipleChoiceFieldProps = {
|
|
199
|
+
name: string;
|
|
202
200
|
inputProps?: any;
|
|
203
|
-
name?: React.ReactNode;
|
|
204
201
|
className?: string;
|
|
205
|
-
enums:
|
|
202
|
+
enums: {
|
|
203
|
+
type: string;
|
|
204
|
+
values: {
|
|
205
|
+
const: string | number;
|
|
206
|
+
title: string;
|
|
207
|
+
}[];
|
|
208
|
+
};
|
|
206
209
|
style?: string;
|
|
210
|
+
value?: string | number;
|
|
211
|
+
form?: FormikContextType<unknown>;
|
|
207
212
|
};
|
|
208
213
|
export type ToggleSwitchFieldProps = {
|
|
209
214
|
inputProps: any;
|
|
@@ -288,4 +293,7 @@ export type FileUploadFieldProps = {
|
|
|
288
293
|
uploadButtonText?: string;
|
|
289
294
|
infoText?: string;
|
|
290
295
|
};
|
|
296
|
+
export type FormValues = {
|
|
297
|
+
[key: string]: string | number | boolean | number[] | string[] | FormValues | FormValues[];
|
|
298
|
+
};
|
|
291
299
|
export {};
|
|
@@ -159,6 +159,77 @@ const ifThenElseExampleSchema = {
|
|
|
159
159
|
},
|
|
160
160
|
],
|
|
161
161
|
};
|
|
162
|
+
const allOfExample = {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: {
|
|
165
|
+
customerType: { type: "string" },
|
|
166
|
+
},
|
|
167
|
+
allOf: [
|
|
168
|
+
{
|
|
169
|
+
if: { properties: { customerType: { const: "BUSINESS" } } },
|
|
170
|
+
then: {
|
|
171
|
+
properties: {
|
|
172
|
+
businessName: { type: "string", title: "Company name" },
|
|
173
|
+
code: { type: "string", title: "Code" },
|
|
174
|
+
notRequired: { type: "string", title: "Not required" },
|
|
175
|
+
address: { $ref: "#/definitions/address" },
|
|
176
|
+
},
|
|
177
|
+
required: ["businessName", "code", "address"],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
if: { properties: { customerType: { const: "INDIVIDUAL" } } },
|
|
182
|
+
then: {
|
|
183
|
+
properties: {
|
|
184
|
+
firstName: { type: "string", title: "First name" },
|
|
185
|
+
code: { type: "number", title: "Code" },
|
|
186
|
+
address: { $ref: "#/definitions/address" },
|
|
187
|
+
},
|
|
188
|
+
required: ["firstName", "code", "address"],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
if: { properties: { customerType: { const: "UNKNOWN" } } },
|
|
193
|
+
then: {
|
|
194
|
+
properties: {
|
|
195
|
+
newCustomer: { type: "boolean", title: "New customer" },
|
|
196
|
+
code: { type: "boolean", title: "Code" },
|
|
197
|
+
extraField: { type: "number", title: "Extra field" },
|
|
198
|
+
address: { $ref: "#/definitions/address" },
|
|
199
|
+
},
|
|
200
|
+
required: ["newCustomer", "code", "address"],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
definitions: {
|
|
205
|
+
address: {
|
|
206
|
+
type: "object",
|
|
207
|
+
required: ["streetName", "city", "country"],
|
|
208
|
+
properties: {
|
|
209
|
+
streetName: {
|
|
210
|
+
type: "string",
|
|
211
|
+
title: "Street name",
|
|
212
|
+
},
|
|
213
|
+
city: {
|
|
214
|
+
type: "string",
|
|
215
|
+
title: "Town/City",
|
|
216
|
+
},
|
|
217
|
+
postcode: {
|
|
218
|
+
type: "string",
|
|
219
|
+
title: "Postcode",
|
|
220
|
+
},
|
|
221
|
+
state: {
|
|
222
|
+
type: "string",
|
|
223
|
+
title: "State/Province/Region",
|
|
224
|
+
},
|
|
225
|
+
country: {
|
|
226
|
+
type: "string",
|
|
227
|
+
title: "Country",
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
162
233
|
const nestedRequiredConditionalExampleSchema = {
|
|
163
234
|
type: "object",
|
|
164
235
|
properties: {
|
|
@@ -189,6 +260,17 @@ const nestedRequiredConditionalExampleSchema = {
|
|
|
189
260
|
],
|
|
190
261
|
};
|
|
191
262
|
describe("Create Validation Schema", () => {
|
|
263
|
+
function listErrors(schema, data) {
|
|
264
|
+
var _a;
|
|
265
|
+
const yupSchema = createValidationSchema(schema);
|
|
266
|
+
try {
|
|
267
|
+
yupSchema.validateSync(data, { abortEarly: false });
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
const error = err;
|
|
271
|
+
return (_a = error === null || error === void 0 ? void 0 : error.inner) === null || _a === void 0 ? void 0 : _a.map((aa) => aa.path);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
192
274
|
it("creates yup validationSchema based on given JSON schema", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
193
275
|
const result = createValidationSchema(exampleSchema);
|
|
194
276
|
const expected = result.isValidSync({
|
|
@@ -356,6 +438,76 @@ describe("Create Validation Schema", () => {
|
|
|
356
438
|
expect(firstTrueInvalid3).toEqual(false);
|
|
357
439
|
expect(firstTrueValid).toEqual(true);
|
|
358
440
|
});
|
|
441
|
+
it("generates validation for if-then", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
442
|
+
const faultyBusiness = listErrors(allOfExample, {
|
|
443
|
+
customerType: "BUSINESS",
|
|
444
|
+
});
|
|
445
|
+
expect(faultyBusiness).toEqual(expect.arrayContaining([
|
|
446
|
+
"address.country",
|
|
447
|
+
"address.city",
|
|
448
|
+
"address.streetName",
|
|
449
|
+
"businessName",
|
|
450
|
+
"code",
|
|
451
|
+
]));
|
|
452
|
+
const business = listErrors(allOfExample, {
|
|
453
|
+
customerType: "BUSINESS",
|
|
454
|
+
businessName: ".",
|
|
455
|
+
code: ".",
|
|
456
|
+
address: {
|
|
457
|
+
streetName: ".",
|
|
458
|
+
city: ".",
|
|
459
|
+
country: ".",
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
expect(business).toEqual(undefined);
|
|
463
|
+
const faultyIndividual = listErrors(allOfExample, {
|
|
464
|
+
customerType: "INDIVIDUAL",
|
|
465
|
+
});
|
|
466
|
+
expect(faultyIndividual).toEqual(expect.arrayContaining([
|
|
467
|
+
"address.country",
|
|
468
|
+
"address.city",
|
|
469
|
+
"address.streetName",
|
|
470
|
+
"firstName",
|
|
471
|
+
"code",
|
|
472
|
+
]));
|
|
473
|
+
const individual = listErrors(allOfExample, {
|
|
474
|
+
customerType: "INDIVIDUAL",
|
|
475
|
+
firstName: ".",
|
|
476
|
+
code: 123,
|
|
477
|
+
address: {
|
|
478
|
+
streetName: ".",
|
|
479
|
+
city: ".",
|
|
480
|
+
country: ".",
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
expect(individual).toEqual(undefined);
|
|
484
|
+
const faultyUnknown = listErrors(allOfExample, {
|
|
485
|
+
customerType: "UNKNOWN",
|
|
486
|
+
});
|
|
487
|
+
expect(faultyUnknown).toEqual(expect.arrayContaining([
|
|
488
|
+
"address.country",
|
|
489
|
+
"address.city",
|
|
490
|
+
"address.streetName",
|
|
491
|
+
"newCustomer",
|
|
492
|
+
"code",
|
|
493
|
+
]));
|
|
494
|
+
const unknown = listErrors(allOfExample, {
|
|
495
|
+
customerType: "UNKNOWN",
|
|
496
|
+
newCustomer: true,
|
|
497
|
+
code: false,
|
|
498
|
+
address: {
|
|
499
|
+
streetName: ".",
|
|
500
|
+
city: ".",
|
|
501
|
+
country: ".",
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
expect(unknown).toEqual(undefined);
|
|
505
|
+
const yupSchema = createValidationSchema(allOfExample);
|
|
506
|
+
// Makes sure the context resets between function calls.
|
|
507
|
+
// eslint-disable-next-line
|
|
508
|
+
// @ts-expect-error
|
|
509
|
+
expect(yupSchema.fields.code.deps.length).toEqual(3);
|
|
510
|
+
}));
|
|
359
511
|
it("generates validation for conditional objects with nested validations", () => {
|
|
360
512
|
const result = createValidationSchema(nestedRequiredConditionalExampleSchema);
|
|
361
513
|
const isValidWhenConditionNotNet = result.isValidSync({
|
|
@@ -4,5 +4,9 @@ type Condition = {
|
|
|
4
4
|
dependentKeys: string[];
|
|
5
5
|
conditionFn: (...value: any[]) => boolean;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
type ConditionsContext = {
|
|
8
|
+
condition: Condition;
|
|
9
|
+
yupSchema: Yup.Schema<any, any, any, "">;
|
|
10
|
+
};
|
|
11
|
+
export declare function createValidationSchema(schema: Schema, definitions?: Schema, conditionsContext?: Record<string, ConditionsContext[]>, condition?: Condition): Yup.ObjectSchema<{}, Yup.AnyObject, {}, "">;
|
|
8
12
|
export {};
|
|
@@ -9,9 +9,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { solveReferences } from "./solveReferences";
|
|
13
12
|
import * as Yup from "yup";
|
|
14
13
|
import { resolveOneOfValue } from "./resolveSchema";
|
|
14
|
+
import { solveReferences } from "./solveReferences";
|
|
15
15
|
function getConditionalValidations(schema) {
|
|
16
16
|
const { dependencies, allOf } = schema;
|
|
17
17
|
let conditionalValidations = [];
|
|
@@ -115,7 +115,7 @@ function getYupType(type) {
|
|
|
115
115
|
throw new Error("Unsupported schema type");
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
export function createValidationSchema(schema, definitions = {}, condition) {
|
|
118
|
+
export function createValidationSchema(schema, definitions = {}, conditionsContext = {}, condition) {
|
|
119
119
|
var _a;
|
|
120
120
|
if (!(schema === null || schema === void 0 ? void 0 : schema.properties)) {
|
|
121
121
|
return Yup.object().shape({});
|
|
@@ -123,10 +123,11 @@ export function createValidationSchema(schema, definitions = {}, condition) {
|
|
|
123
123
|
const schemaDefinitions = Object.assign(Object.assign({}, definitions), ((_a = schema.definitions) !== null && _a !== void 0 ? _a : {}));
|
|
124
124
|
const requiredFields = schema.required;
|
|
125
125
|
function getValue(schema, key) {
|
|
126
|
+
var _a;
|
|
126
127
|
let type = schema.type;
|
|
127
128
|
let yupSchema;
|
|
128
129
|
if (schema.properties) {
|
|
129
|
-
yupSchema = createValidationSchema(schema, schemaDefinitions);
|
|
130
|
+
yupSchema = createValidationSchema(schema, schemaDefinitions, conditionsContext);
|
|
130
131
|
type = type !== null && type !== void 0 ? type : "object";
|
|
131
132
|
}
|
|
132
133
|
else {
|
|
@@ -144,12 +145,29 @@ export function createValidationSchema(schema, definitions = {}, condition) {
|
|
|
144
145
|
yupSchema = yupSchema["required"]("Required");
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
if (!condition) {
|
|
149
|
+
return yupSchema;
|
|
150
|
+
}
|
|
151
|
+
const existing = (_a = conditionsContext[key]) !== null && _a !== void 0 ? _a : [];
|
|
152
|
+
conditionsContext[key] = [...existing, { yupSchema, condition }];
|
|
153
|
+
// TODO: The latest given type for duplicate field is used.
|
|
154
|
+
// This might become a problem at some point, but testing has not
|
|
155
|
+
// shown any problems so far.
|
|
156
|
+
const yupType = getYupType(type).when(condition.dependentKeys, {
|
|
157
|
+
is: condition.conditionFn,
|
|
158
|
+
then: () => yupSchema,
|
|
159
|
+
});
|
|
160
|
+
// If there are existing conditions for the field with same name
|
|
161
|
+
// for example definition or duplicate field, add those clauses in.
|
|
162
|
+
// Otherwise the validation would be overwritten by the latest one
|
|
163
|
+
// breaking the earlier validations.
|
|
164
|
+
return existing.reduce((result, context) => {
|
|
165
|
+
const { condition: ctxCondition, yupSchema: ctxYupSchema } = context;
|
|
166
|
+
return result.when(ctxCondition.dependentKeys, {
|
|
167
|
+
is: ctxCondition.conditionFn,
|
|
168
|
+
then: () => ctxYupSchema,
|
|
169
|
+
});
|
|
170
|
+
}, yupType);
|
|
153
171
|
}
|
|
154
172
|
const baseValidationSchema = Object.keys(schema.properties).reduce((validationSchema, key) => {
|
|
155
173
|
var _a;
|
|
@@ -159,6 +177,12 @@ export function createValidationSchema(schema, definitions = {}, condition) {
|
|
|
159
177
|
}, Yup.object().shape({}));
|
|
160
178
|
return getConditionalValidations(schema).reduce((validationSchema, conditionalValidation) => {
|
|
161
179
|
const solvedSchemas = solveReferences(conditionalValidation.schema, schemaDefinitions, {}, {});
|
|
162
|
-
|
|
180
|
+
/*
|
|
181
|
+
"Only schemas of the same type can be concatenated. concat is not
|
|
182
|
+
a "merge" function in the sense that all settings from the provided schema,
|
|
183
|
+
override ones in the base, including type, presence and nullability."
|
|
184
|
+
Source: https://github.com/jquense/yup?tab=readme-ov-file#schemaconcatschema-schema-schema
|
|
185
|
+
*/
|
|
186
|
+
return validationSchema.concat(createValidationSchema(solvedSchemas.schema, schemaDefinitions, conditionsContext, conditionalValidation.condition));
|
|
163
187
|
}, baseValidationSchema);
|
|
164
188
|
}
|
package/dist/utils/getTypes.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Schema } from "../types";
|
|
2
2
|
export declare const isObject: (object: any) => boolean;
|
|
3
3
|
export declare const getDefaultValueForType: (type: Schema["type"]) => {} | null;
|
|
4
|
-
export declare const guessType: (value: any) => "object" | "
|
|
4
|
+
export declare const guessType: (value: any) => "object" | "number" | "string" | "boolean" | "array" | "null";
|
|
5
5
|
export declare const getSchemaType: (schema: Schema) => import("json-schema").JSONSchema7TypeName | import("json-schema").JSONSchema7TypeName[] | undefined;
|