@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.
@@ -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
  };
@@ -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
- return (_jsx(FieldTemplate, Object.assign({}, props, { children: _jsx(MultipleChoiceField, { inputProps: inputProps, name: name, enums: enums, style: style }) })));
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
  };
@@ -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, { "data-fjsf-field": childName, 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));
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, { "data-fjsf-field": childName, required: required === null || required === void 0 ? void 0 : required.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof schema.additionalProperties !== "boolean"
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
- return (_jsx(FieldTemplate, Object.assign({}, props, { 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: {
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", children: [_jsx("option", Object.assign({ value: "",
16
- // If both are true, the first enum will be "selected"
17
- // and leads to annoying and misleading UX.
18
- // These defaults will lead to an empty, "nothing selected" component.
19
- disabled: false,
20
- 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)))] })) })));
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;
@@ -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
- 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, { "data-fjsf-enum-value": typeof v === "string" || typeof v === "number" ? v : v.const, children: typeof v === "string" || typeof v === "number" ? v : v.title })] }, typeof v === "string" || typeof v === "number" ? v : v.const))) }));
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: wrappertProps, title: titleProps, description: descriptionProps, help: helpProps, error: errorProps, } = {}, } = useExtractUiSchema(uiSchema);
15
- return (_jsxs(ArrayFieldWrapper, Object.assign({}, wrappertProps, { 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 }))] })));
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({}, wrapperProps, { 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 }))] })));
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
- //TODO: This is not right
13
- props: { wrapper: wrapperProps, title: titleProps, description: descriptionProps, help: helpProps, error: errorProps, } = {}, } = useExtractUiSchema(uiSchema);
14
- return (_jsxs(FieldWrapper, Object.assign({}, wrapperProps, { 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 }))] })));
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
- return (_jsxs(ObjectFieldWrapper, Object.assign({}, wrapperProps, { 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 }))] })));
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: Enum;
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
- export declare function createValidationSchema(schema: Schema, definitions?: Schema, condition?: Condition): Yup.ObjectSchema<{}, Yup.AnyObject, {}, "">;
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
- return condition
148
- ? getYupType(type).when(condition.dependentKeys, {
149
- is: condition.conditionFn,
150
- then: () => yupSchema,
151
- })
152
- : yupSchema;
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
- return validationSchema.concat(createValidationSchema(solvedSchemas.schema, schemaDefinitions, conditionalValidation.condition));
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
  }
@@ -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" | "string" | "number" | "boolean" | "array" | "null";
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nolwenture/formik-jsonschema-form",
3
- "version": "0.22.4",
3
+ "version": "0.23.0",
4
4
  "description": "Formik form generation from json schema",
5
5
  "author": "Antti Vikman <antti.vikman@nolwenture.com>",
6
6
  "contributors": [