@rjsf/utils 6.0.0-alpha.0 → 6.0.0-beta.1
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/index.js +1281 -625
- package/dist/index.js.map +4 -4
- package/dist/utils.esm.js +1254 -598
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +1201 -570
- package/lib/ErrorSchemaBuilder.d.ts +8 -4
- package/lib/ErrorSchemaBuilder.js +10 -8
- package/lib/ErrorSchemaBuilder.js.map +1 -1
- package/lib/allowAdditionalItems.d.ts +1 -1
- package/lib/allowAdditionalItems.js +1 -1
- package/lib/allowAdditionalItems.js.map +1 -1
- package/lib/asNumber.js.map +1 -1
- package/lib/canExpand.d.ts +1 -1
- package/lib/canExpand.js +2 -2
- package/lib/canExpand.js.map +1 -1
- package/lib/constIsAjvDataReference.d.ts +9 -0
- package/lib/constIsAjvDataReference.js +15 -0
- package/lib/constIsAjvDataReference.js.map +1 -0
- package/lib/constants.d.ts +11 -3
- package/lib/constants.js +11 -3
- package/lib/constants.js.map +1 -1
- package/lib/createErrorHandler.d.ts +1 -1
- package/lib/createErrorHandler.js +2 -2
- package/lib/createErrorHandler.js.map +1 -1
- package/lib/createSchemaUtils.d.ts +3 -2
- package/lib/createSchemaUtils.js +56 -46
- package/lib/createSchemaUtils.js.map +1 -1
- package/lib/dataURItoBlob.js.map +1 -1
- package/lib/dateRangeOptions.d.ts +1 -1
- package/lib/dateRangeOptions.js +1 -1
- package/lib/dateRangeOptions.js.map +1 -1
- package/lib/deepEquals.js +1 -1
- package/lib/deepEquals.js.map +1 -1
- package/lib/englishStringTranslator.d.ts +1 -1
- package/lib/englishStringTranslator.js +1 -1
- package/lib/enumOptionsDeselectValue.d.ts +1 -1
- package/lib/enumOptionsDeselectValue.js +4 -4
- package/lib/enumOptionsDeselectValue.js.map +1 -1
- package/lib/enumOptionsIndexForValue.d.ts +1 -1
- package/lib/enumOptionsIndexForValue.js +1 -1
- package/lib/enumOptionsIndexForValue.js.map +1 -1
- package/lib/enumOptionsIsSelected.d.ts +1 -1
- package/lib/enumOptionsIsSelected.js +3 -3
- package/lib/enumOptionsIsSelected.js.map +1 -1
- package/lib/enumOptionsSelectValue.d.ts +1 -1
- package/lib/enumOptionsSelectValue.js +2 -2
- package/lib/enumOptionsSelectValue.js.map +1 -1
- package/lib/enumOptionsValueForIndex.d.ts +1 -1
- package/lib/enumOptionsValueForIndex.js.map +1 -1
- package/lib/enums.d.ts +2 -0
- package/lib/enums.js +2 -0
- package/lib/enums.js.map +1 -1
- package/lib/findSchemaDefinition.d.ts +1 -1
- package/lib/findSchemaDefinition.js +2 -2
- package/lib/findSchemaDefinition.js.map +1 -1
- package/lib/getChangedFields.d.ts +17 -0
- package/lib/getChangedFields.js +42 -0
- package/lib/getChangedFields.js.map +1 -0
- package/lib/getDateElementProps.d.ts +1 -1
- package/lib/getDateElementProps.js.map +1 -1
- package/lib/getDiscriminatorFieldFromSchema.d.ts +1 -1
- package/lib/getDiscriminatorFieldFromSchema.js +4 -3
- package/lib/getDiscriminatorFieldFromSchema.js.map +1 -1
- package/lib/getInputProps.d.ts +1 -1
- package/lib/getInputProps.js +4 -1
- package/lib/getInputProps.js.map +1 -1
- package/lib/getOptionMatchingSimpleDiscriminator.d.ts +1 -1
- package/lib/getOptionMatchingSimpleDiscriminator.js +2 -2
- package/lib/getOptionMatchingSimpleDiscriminator.js.map +1 -1
- package/lib/getSchemaType.d.ts +2 -1
- package/lib/getSchemaType.js +3 -2
- package/lib/getSchemaType.js.map +1 -1
- package/lib/getSubmitButtonOptions.d.ts +1 -1
- package/lib/getSubmitButtonOptions.js +2 -2
- package/lib/getSubmitButtonOptions.js.map +1 -1
- package/lib/getTemplate.d.ts +1 -1
- package/lib/getTemplate.js +9 -0
- package/lib/getTemplate.js.map +1 -1
- package/lib/getTestIds.d.ts +17 -0
- package/lib/getTestIds.js +34 -0
- package/lib/getTestIds.js.map +1 -0
- package/lib/getUiOptions.d.ts +1 -1
- package/lib/getUiOptions.js +2 -2
- package/lib/getUiOptions.js.map +1 -1
- package/lib/getWidget.d.ts +1 -1
- package/lib/getWidget.js +3 -3
- package/lib/getWidget.js.map +1 -1
- package/lib/guessType.d.ts +1 -1
- package/lib/guessType.js.map +1 -1
- package/lib/hasWidget.d.ts +1 -1
- package/lib/hasWidget.js +1 -1
- package/lib/hasWidget.js.map +1 -1
- package/lib/hashForSchema.d.ts +23 -1
- package/lib/hashForSchema.js +24 -6
- package/lib/hashForSchema.js.map +1 -1
- package/lib/idGenerators.d.ts +8 -1
- package/lib/idGenerators.js +11 -2
- package/lib/idGenerators.js.map +1 -1
- package/lib/index.d.ts +63 -60
- package/lib/index.js +63 -60
- package/lib/index.js.map +1 -1
- package/lib/isConstant.d.ts +1 -1
- package/lib/isConstant.js +1 -1
- package/lib/isCustomWidget.d.ts +1 -1
- package/lib/isCustomWidget.js +1 -1
- package/lib/isFixedItems.d.ts +1 -1
- package/lib/isFixedItems.js +1 -1
- package/lib/isObject.d.ts +2 -2
- package/lib/isObject.js +11 -4
- package/lib/isObject.js.map +1 -1
- package/lib/lookupFromFormContext.d.ts +11 -0
- package/lib/lookupFromFormContext.js +20 -0
- package/lib/lookupFromFormContext.js.map +1 -0
- package/lib/mergeDefaultsWithFormData.d.ts +8 -2
- package/lib/mergeDefaultsWithFormData.js +39 -10
- package/lib/mergeDefaultsWithFormData.js.map +1 -1
- package/lib/mergeObjects.d.ts +1 -1
- package/lib/mergeObjects.js +1 -1
- package/lib/mergeObjects.js.map +1 -1
- package/lib/mergeSchemas.d.ts +1 -1
- package/lib/mergeSchemas.js +4 -4
- package/lib/mergeSchemas.js.map +1 -1
- package/lib/optionsList.d.ts +9 -7
- package/lib/optionsList.js +30 -19
- package/lib/optionsList.js.map +1 -1
- package/lib/orderProperties.js.map +1 -1
- package/lib/pad.js.map +1 -1
- package/lib/parseDateString.d.ts +1 -1
- package/lib/parseDateString.js +1 -1
- package/lib/parseDateString.js.map +1 -1
- package/lib/parser/ParserValidator.d.ts +1 -1
- package/lib/parser/ParserValidator.js +6 -6
- package/lib/parser/ParserValidator.js.map +1 -1
- package/lib/parser/index.d.ts +2 -2
- package/lib/parser/index.js +1 -1
- package/lib/parser/schemaParser.d.ts +2 -2
- package/lib/parser/schemaParser.js +6 -6
- package/lib/parser/schemaParser.js.map +1 -1
- package/lib/rangeSpec.d.ts +2 -2
- package/lib/rangeSpec.js.map +1 -1
- package/lib/replaceStringParameters.js.map +1 -1
- package/lib/schema/findFieldInSchema.d.ts +19 -0
- package/lib/schema/findFieldInSchema.js +61 -0
- package/lib/schema/findFieldInSchema.js.map +1 -0
- package/lib/schema/findSelectedOptionInXxxOf.d.ts +16 -0
- package/lib/schema/findSelectedOptionInXxxOf.js +34 -0
- package/lib/schema/findSelectedOptionInXxxOf.js.map +1 -0
- package/lib/schema/getClosestMatchingOption.d.ts +5 -3
- package/lib/schema/getClosestMatchingOption.js +28 -20
- package/lib/schema/getClosestMatchingOption.js.map +1 -1
- package/lib/schema/getDefaultFormState.d.ts +60 -13
- package/lib/schema/getDefaultFormState.js +304 -166
- package/lib/schema/getDefaultFormState.js.map +1 -1
- package/lib/schema/getDisplayLabel.d.ts +3 -2
- package/lib/schema/getDisplayLabel.js +10 -9
- package/lib/schema/getDisplayLabel.js.map +1 -1
- package/lib/schema/getFirstMatchingOption.d.ts +1 -1
- package/lib/schema/getFirstMatchingOption.js +70 -2
- package/lib/schema/getFirstMatchingOption.js.map +1 -1
- package/lib/schema/getFromSchema.d.ts +14 -0
- package/lib/schema/getFromSchema.js +39 -0
- package/lib/schema/getFromSchema.js.map +1 -0
- package/lib/schema/index.d.ts +15 -14
- package/lib/schema/index.js +15 -14
- package/lib/schema/index.js.map +1 -1
- package/lib/schema/isFilesArray.d.ts +3 -2
- package/lib/schema/isFilesArray.js +5 -4
- package/lib/schema/isFilesArray.js.map +1 -1
- package/lib/schema/isMultiSelect.d.ts +3 -2
- package/lib/schema/isMultiSelect.js +4 -3
- package/lib/schema/isMultiSelect.js.map +1 -1
- package/lib/schema/isSelect.d.ts +3 -2
- package/lib/schema/isSelect.js +5 -4
- package/lib/schema/isSelect.js.map +1 -1
- package/lib/schema/retrieveSchema.d.ts +28 -11
- package/lib/schema/retrieveSchema.js +142 -66
- package/lib/schema/retrieveSchema.js.map +1 -1
- package/lib/schema/sanitizeDataForNewSchema.d.ts +3 -2
- package/lib/schema/sanitizeDataForNewSchema.js +12 -11
- package/lib/schema/sanitizeDataForNewSchema.js.map +1 -1
- package/lib/schema/toIdSchema.d.ts +3 -2
- package/lib/schema/toIdSchema.js +30 -27
- package/lib/schema/toIdSchema.js.map +1 -1
- package/lib/schema/toPathSchema.d.ts +3 -2
- package/lib/schema/toPathSchema.js +22 -20
- package/lib/schema/toPathSchema.js.map +1 -1
- package/lib/schemaRequiresTrueValue.d.ts +1 -1
- package/lib/schemaRequiresTrueValue.js.map +1 -1
- package/lib/shouldRender.js +1 -1
- package/lib/toConstant.d.ts +1 -1
- package/lib/toConstant.js +1 -1
- package/lib/toConstant.js.map +1 -1
- package/lib/toDateString.d.ts +1 -1
- package/lib/toErrorList.d.ts +1 -1
- package/lib/toErrorList.js +2 -2
- package/lib/toErrorList.js.map +1 -1
- package/lib/toErrorSchema.d.ts +1 -1
- package/lib/toErrorSchema.js +2 -2
- package/lib/toErrorSchema.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +160 -131
- package/lib/unwrapErrorHandler.d.ts +1 -1
- package/lib/unwrapErrorHandler.js +1 -1
- package/lib/unwrapErrorHandler.js.map +1 -1
- package/lib/utcToLocal.js +1 -1
- package/lib/utcToLocal.js.map +1 -1
- package/lib/validationDataMerge.d.ts +1 -1
- package/lib/validationDataMerge.js +3 -3
- package/lib/validationDataMerge.js.map +1 -1
- package/lib/withIdRefPrefix.d.ts +1 -1
- package/lib/withIdRefPrefix.js +2 -2
- package/lib/withIdRefPrefix.js.map +1 -1
- package/package.json +36 -26
- package/src/ErrorSchemaBuilder.ts +15 -8
- package/src/canExpand.ts +2 -2
- package/src/constIsAjvDataReference.ts +17 -0
- package/src/constants.ts +12 -3
- package/src/createSchemaUtils.ts +140 -50
- package/src/dataURItoBlob.ts +1 -1
- package/src/dateRangeOptions.ts +1 -1
- package/src/enumOptionsDeselectValue.ts +4 -5
- package/src/enumOptionsIndexForValue.ts +1 -1
- package/src/enumOptionsIsSelected.ts +4 -5
- package/src/enumOptionsSelectValue.ts +1 -1
- package/src/enumOptionsValueForIndex.ts +1 -1
- package/src/enums.ts +2 -0
- package/src/findSchemaDefinition.ts +2 -2
- package/src/getChangedFields.ts +40 -0
- package/src/getDateElementProps.ts +2 -2
- package/src/getDiscriminatorFieldFromSchema.ts +2 -1
- package/src/getInputProps.ts +6 -2
- package/src/getOptionMatchingSimpleDiscriminator.ts +2 -2
- package/src/getSchemaType.ts +3 -2
- package/src/getSubmitButtonOptions.ts +1 -1
- package/src/getTemplate.ts +12 -1
- package/src/getTestIds.ts +40 -0
- package/src/getUiOptions.ts +2 -2
- package/src/getWidget.tsx +2 -2
- package/src/hasWidget.ts +1 -1
- package/src/hashForSchema.ts +26 -6
- package/src/idGenerators.ts +10 -0
- package/src/index.ts +21 -2
- package/src/isCustomWidget.ts +1 -1
- package/src/isObject.ts +12 -5
- package/src/labelValue.ts +2 -2
- package/src/lookupFromFormContext.ts +26 -0
- package/src/mergeDefaultsWithFormData.ts +54 -9
- package/src/mergeObjects.ts +24 -21
- package/src/optionsList.ts +31 -22
- package/src/parser/ParserValidator.ts +5 -5
- package/src/parser/schemaParser.ts +6 -6
- package/src/schema/findFieldInSchema.ts +138 -0
- package/src/schema/findSelectedOptionInXxxOf.ts +53 -0
- package/src/schema/getClosestMatchingOption.ts +38 -11
- package/src/schema/getDefaultFormState.ts +447 -191
- package/src/schema/getDisplayLabel.ts +7 -4
- package/src/schema/getFirstMatchingOption.ts +79 -4
- package/src/schema/getFromSchema.ts +100 -0
- package/src/schema/index.ts +6 -4
- package/src/schema/isFilesArray.ts +18 -3
- package/src/schema/isMultiSelect.ts +10 -4
- package/src/schema/isSelect.ts +5 -3
- package/src/schema/retrieveSchema.ts +256 -75
- package/src/schema/sanitizeDataForNewSchema.ts +52 -11
- package/src/schema/toIdSchema.ts +69 -43
- package/src/schema/toPathSchema.ts +49 -16
- package/src/toErrorList.ts +2 -2
- package/src/types.ts +266 -174
- package/src/validationDataMerge.ts +1 -1
- package/src/withIdRefPrefix.ts +1 -1
- package/LICENSE.md +0 -201
- package/lib/schema/getMatchingOption.d.ts +0 -14
- package/lib/schema/getMatchingOption.js +0 -85
- package/lib/schema/getMatchingOption.js.map +0 -1
- package/lib/schema/mergeValidationData.d.ts +0 -14
- package/lib/schema/mergeValidationData.js +0 -28
- package/lib/schema/mergeValidationData.js.map +0 -1
- package/src/schema/getMatchingOption.ts +0 -103
- package/src/schema/mergeValidationData.ts +0 -38
|
@@ -2,6 +2,7 @@ import get from 'lodash/get';
|
|
|
2
2
|
|
|
3
3
|
import isObject from './isObject';
|
|
4
4
|
import { GenericObjectType } from '../src';
|
|
5
|
+
import isNil from 'lodash/isNil';
|
|
5
6
|
|
|
6
7
|
/** Merges the `defaults` object of type `T` into the `formData` of type `T`
|
|
7
8
|
*
|
|
@@ -12,42 +13,86 @@ import { GenericObjectType } from '../src';
|
|
|
12
13
|
* are deeply merged; additional entries from the defaults are ignored unless `mergeExtraArrayDefaults` is true, in
|
|
13
14
|
* which case the extras are appended onto the end of the form data
|
|
14
15
|
* - when the array is not set in form data, the default is copied over
|
|
15
|
-
* - scalars are overwritten/set by form data
|
|
16
|
+
* - scalars are overwritten/set by form data unless undefined and there is a default AND `defaultSupercedesUndefined`
|
|
17
|
+
* is true
|
|
16
18
|
*
|
|
17
19
|
* @param [defaults] - The defaults to merge
|
|
18
20
|
* @param [formData] - The form data into which the defaults will be merged
|
|
19
21
|
* @param [mergeExtraArrayDefaults=false] - If true, any additional default array entries are appended onto the formData
|
|
22
|
+
* @param [defaultSupercedesUndefined=false] - If true, an explicit undefined value will be overwritten by the default value
|
|
23
|
+
* @param [overrideFormDataWithDefaults=false] - If true, the default value will overwrite the form data value. If the value
|
|
24
|
+
* doesn't exist in the default, we take it from formData and in the case where the value is set to undefined in formData.
|
|
25
|
+
* This is useful when we have already merged formData with defaults and want to add an additional field from formData
|
|
26
|
+
* that does not exist in defaults.
|
|
20
27
|
* @returns - The resulting merged form data with defaults
|
|
21
28
|
*/
|
|
22
29
|
export default function mergeDefaultsWithFormData<T = any>(
|
|
23
30
|
defaults?: T,
|
|
24
31
|
formData?: T,
|
|
25
|
-
mergeExtraArrayDefaults = false
|
|
32
|
+
mergeExtraArrayDefaults = false,
|
|
33
|
+
defaultSupercedesUndefined = false,
|
|
34
|
+
overrideFormDataWithDefaults = false,
|
|
26
35
|
): T | undefined {
|
|
27
36
|
if (Array.isArray(formData)) {
|
|
28
37
|
const defaultsArray = Array.isArray(defaults) ? defaults : [];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
|
|
39
|
+
// If overrideFormDataWithDefaults is true, we want to override the formData with the defaults
|
|
40
|
+
const overrideArray = overrideFormDataWithDefaults ? defaultsArray : formData;
|
|
41
|
+
const overrideOppositeArray = overrideFormDataWithDefaults ? formData : defaultsArray;
|
|
42
|
+
|
|
43
|
+
const mapped = overrideArray.map((value, idx) => {
|
|
44
|
+
// We want to explicitly make sure that the value is NOT undefined since null, 0 and empty space are valid values
|
|
45
|
+
if (overrideOppositeArray[idx] !== undefined) {
|
|
46
|
+
return mergeDefaultsWithFormData<any>(
|
|
47
|
+
defaultsArray[idx],
|
|
48
|
+
formData[idx],
|
|
49
|
+
mergeExtraArrayDefaults,
|
|
50
|
+
defaultSupercedesUndefined,
|
|
51
|
+
overrideFormDataWithDefaults,
|
|
52
|
+
);
|
|
32
53
|
}
|
|
33
54
|
return value;
|
|
34
55
|
});
|
|
56
|
+
|
|
35
57
|
// Merge any extra defaults when mergeExtraArrayDefaults is true
|
|
36
|
-
|
|
37
|
-
|
|
58
|
+
// Or when overrideFormDataWithDefaults is true and the default array is shorter than the formData array
|
|
59
|
+
if ((mergeExtraArrayDefaults || overrideFormDataWithDefaults) && mapped.length < overrideOppositeArray.length) {
|
|
60
|
+
mapped.push(...overrideOppositeArray.slice(mapped.length));
|
|
38
61
|
}
|
|
39
62
|
return mapped as unknown as T;
|
|
40
63
|
}
|
|
41
64
|
if (isObject(formData)) {
|
|
42
65
|
const acc: { [key in keyof T]: any } = Object.assign({}, defaults); // Prevent mutation of source object.
|
|
43
66
|
return Object.keys(formData as GenericObjectType).reduce((acc, key) => {
|
|
67
|
+
const keyValue = get(formData, key);
|
|
68
|
+
const keyExistsInDefaults = isObject(defaults) && key in (defaults as GenericObjectType);
|
|
69
|
+
const keyExistsInFormData = key in (formData as GenericObjectType);
|
|
44
70
|
acc[key as keyof T] = mergeDefaultsWithFormData<T>(
|
|
45
71
|
defaults ? get(defaults, key) : {},
|
|
46
|
-
|
|
47
|
-
mergeExtraArrayDefaults
|
|
72
|
+
keyValue,
|
|
73
|
+
mergeExtraArrayDefaults,
|
|
74
|
+
defaultSupercedesUndefined,
|
|
75
|
+
// overrideFormDataWithDefaults can be true only when the key value exists in defaults
|
|
76
|
+
// Or if the key value doesn't exist in formData
|
|
77
|
+
overrideFormDataWithDefaults && (keyExistsInDefaults || !keyExistsInFormData),
|
|
48
78
|
);
|
|
49
79
|
return acc;
|
|
50
80
|
}, acc);
|
|
51
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* If the defaultSupercedesUndefined flag is true
|
|
85
|
+
* And formData is set to undefined or null and defaults are defined
|
|
86
|
+
* Or if formData is a number and is NaN return defaults
|
|
87
|
+
* Or if overrideFormDataWithDefaults flag is true and formData is set to not undefined/null return defaults
|
|
88
|
+
*/
|
|
89
|
+
if (
|
|
90
|
+
(defaultSupercedesUndefined &&
|
|
91
|
+
((!isNil(defaults) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) ||
|
|
92
|
+
(overrideFormDataWithDefaults && !isNil(formData))
|
|
93
|
+
) {
|
|
94
|
+
return defaults;
|
|
95
|
+
}
|
|
96
|
+
|
|
52
97
|
return formData;
|
|
53
98
|
}
|
package/src/mergeObjects.ts
CHANGED
|
@@ -13,27 +13,30 @@ import { GenericObjectType } from './types';
|
|
|
13
13
|
export default function mergeObjects(
|
|
14
14
|
obj1: GenericObjectType,
|
|
15
15
|
obj2: GenericObjectType,
|
|
16
|
-
concatArrays: boolean | 'preventDuplicates' = false
|
|
16
|
+
concatArrays: boolean | 'preventDuplicates' = false,
|
|
17
17
|
) {
|
|
18
|
-
return Object.keys(obj2).reduce(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
return Object.keys(obj2).reduce(
|
|
19
|
+
(acc, key) => {
|
|
20
|
+
const left = obj1 ? obj1[key] : {},
|
|
21
|
+
right = obj2[key];
|
|
22
|
+
if (obj1 && key in obj1 && isObject(right)) {
|
|
23
|
+
acc[key] = mergeObjects(left, right, concatArrays);
|
|
24
|
+
} else if (concatArrays && Array.isArray(left) && Array.isArray(right)) {
|
|
25
|
+
let toMerge = right;
|
|
26
|
+
if (concatArrays === 'preventDuplicates') {
|
|
27
|
+
toMerge = right.reduce((result, value) => {
|
|
28
|
+
if (!left.includes(value)) {
|
|
29
|
+
result.push(value);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}, []);
|
|
33
|
+
}
|
|
34
|
+
acc[key] = left.concat(toMerge);
|
|
35
|
+
} else {
|
|
36
|
+
acc[key] = right;
|
|
32
37
|
}
|
|
33
|
-
acc
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return acc;
|
|
38
|
-
}, Object.assign({}, obj1)); // Prevent mutation of source object.
|
|
38
|
+
return acc;
|
|
39
|
+
},
|
|
40
|
+
Object.assign({}, obj1),
|
|
41
|
+
); // Prevent mutation of source object.
|
|
39
42
|
}
|
package/src/optionsList.ts
CHANGED
|
@@ -1,40 +1,34 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
|
|
3
|
+
import { CONST_KEY, DEFAULT_KEY, PROPERTIES_KEY } from './constants';
|
|
4
|
+
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema';
|
|
5
|
+
import getUiOptions from './getUiOptions';
|
|
1
6
|
import toConstant from './toConstant';
|
|
2
7
|
import { RJSFSchema, EnumOptionsType, StrictRJSFSchema, FormContextType, UiSchema } from './types';
|
|
3
|
-
import getUiOptions from './getUiOptions';
|
|
4
8
|
|
|
5
9
|
/** Gets the list of options from the `schema`. If the schema has an enum list, then those enum values are returned. The
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* label will be the same as the `value`.
|
|
11
|
+
*
|
|
12
|
+
* If the schema has a `oneOf` or `anyOf`, then the value is the list of either:
|
|
13
|
+
* - The `const` values from the schema if present
|
|
14
|
+
* - If the schema has a discriminator and the label using either the `schema.title` or the value. If a `uiSchema` is
|
|
15
|
+
* provided, and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of
|
|
16
|
+
* objects containing `ui:title` then the UI schema values will replace the values from the schema.
|
|
11
17
|
*
|
|
12
18
|
* @param schema - The schema from which to extract the options list
|
|
13
19
|
* @param [uiSchema] - The optional uiSchema from which to get alternate labels for the options
|
|
14
20
|
* @returns - The list of options from the schema
|
|
15
21
|
*/
|
|
16
|
-
export default function optionsList<S extends StrictRJSFSchema = RJSFSchema,
|
|
22
|
+
export default function optionsList<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
17
23
|
schema: S,
|
|
18
|
-
uiSchema?: UiSchema<T, S, F
|
|
24
|
+
uiSchema?: UiSchema<T, S, F>,
|
|
19
25
|
): EnumOptionsType<S>[] | undefined {
|
|
20
|
-
// TODO flip generics to move T first in v6
|
|
21
|
-
const schemaWithEnumNames = schema as S & { enumNames?: string[] };
|
|
22
26
|
if (schema.enum) {
|
|
23
27
|
let enumNames: string[] | undefined;
|
|
24
28
|
if (uiSchema) {
|
|
25
29
|
const { enumNames: uiEnumNames } = getUiOptions<T, S, F>(uiSchema);
|
|
26
30
|
enumNames = uiEnumNames;
|
|
27
31
|
}
|
|
28
|
-
if (!enumNames && schemaWithEnumNames.enumNames) {
|
|
29
|
-
// enumNames was deprecated in v5 and is intentionally omitted from the RJSFSchema type.
|
|
30
|
-
// Cast the type to include enumNames so the feature still works.
|
|
31
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
32
|
-
console.warn(
|
|
33
|
-
'The "enumNames" property in the schema is deprecated and will be removed in a future major release. Use the "ui:enumNames" property in the uiSchema instead.'
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
enumNames = schemaWithEnumNames.enumNames;
|
|
37
|
-
}
|
|
38
32
|
return schema.enum.map((value, i) => {
|
|
39
33
|
const label = enumNames?.[i] || String(value);
|
|
40
34
|
return { label, value };
|
|
@@ -49,13 +43,28 @@ export default function optionsList<S extends StrictRJSFSchema = RJSFSchema, T =
|
|
|
49
43
|
altSchemas = schema.oneOf;
|
|
50
44
|
altUiSchemas = uiSchema?.oneOf;
|
|
51
45
|
}
|
|
46
|
+
// See if there is a discriminator path specified in the schema, and if so, use it as the selectorField, otherwise
|
|
47
|
+
// pull one from the uiSchema
|
|
48
|
+
let selectorField = getDiscriminatorFieldFromSchema<S>(schema);
|
|
49
|
+
if (uiSchema) {
|
|
50
|
+
const { optionsSchemaSelector = selectorField } = getUiOptions<T, S, F>(uiSchema);
|
|
51
|
+
selectorField = optionsSchemaSelector;
|
|
52
|
+
}
|
|
52
53
|
return (
|
|
53
54
|
altSchemas &&
|
|
54
55
|
altSchemas.map((aSchemaDef, index) => {
|
|
55
56
|
const { title } = getUiOptions<T, S, F>(altUiSchemas?.[index]);
|
|
56
57
|
const aSchema = aSchemaDef as S;
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
let value: EnumOptionsType<S>['value'];
|
|
59
|
+
let label = title;
|
|
60
|
+
if (selectorField) {
|
|
61
|
+
const innerSchema: S = get(aSchema, [PROPERTIES_KEY, selectorField], {}) as S;
|
|
62
|
+
value = get(innerSchema, DEFAULT_KEY, get(innerSchema, CONST_KEY));
|
|
63
|
+
label = label || innerSchema?.title || aSchema.title || String(value);
|
|
64
|
+
} else {
|
|
65
|
+
value = toConstant(aSchema);
|
|
66
|
+
label = label || aSchema.title || String(value);
|
|
67
|
+
}
|
|
59
68
|
return {
|
|
60
69
|
schema: aSchema,
|
|
61
70
|
label,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
|
|
4
3
|
import { ID_KEY } from '../constants';
|
|
5
4
|
import hashForSchema from '../hashForSchema';
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
ValidationData,
|
|
16
15
|
ValidatorType,
|
|
17
16
|
} from '../types';
|
|
17
|
+
import deepEquals from '../deepEquals';
|
|
18
18
|
|
|
19
19
|
/** The type of the map of schema hash to schema
|
|
20
20
|
*/
|
|
@@ -67,11 +67,11 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
|
|
|
67
67
|
const existing = this.schemaMap[key];
|
|
68
68
|
if (!existing) {
|
|
69
69
|
this.schemaMap[key] = identifiedSchema;
|
|
70
|
-
} else if (!
|
|
70
|
+
} else if (!deepEquals(existing, identifiedSchema)) {
|
|
71
71
|
console.error('existing schema:', JSON.stringify(existing, null, 2));
|
|
72
72
|
console.error('new schema:', JSON.stringify(identifiedSchema, null, 2));
|
|
73
73
|
throw new Error(
|
|
74
|
-
`Two different schemas exist with the same key ${key}! What a bad coincidence. If possible, try adding an $id to one of the schemas
|
|
74
|
+
`Two different schemas exist with the same key ${key}! What a bad coincidence. If possible, try adding an $id to one of the schemas`,
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -91,7 +91,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
|
|
|
91
91
|
* @throws - Error when the given `rootSchema` differs from the root schema provided during construction
|
|
92
92
|
*/
|
|
93
93
|
isValid(schema: S, _formData: T, rootSchema: S): boolean {
|
|
94
|
-
if (!
|
|
94
|
+
if (!deepEquals(rootSchema, this.rootSchema)) {
|
|
95
95
|
throw new Error('Unexpectedly calling isValid() with a rootSchema that differs from the construction rootSchema');
|
|
96
96
|
}
|
|
97
97
|
this.addSchema(schema, hashForSchema<S>(schema));
|
|
@@ -131,7 +131,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
|
|
|
131
131
|
_schema: S,
|
|
132
132
|
_customValidate?: CustomValidator<T, S, F>,
|
|
133
133
|
_transformErrors?: ErrorTransformer<T, S, F>,
|
|
134
|
-
_uiSchema?: UiSchema<T, S, F
|
|
134
|
+
_uiSchema?: UiSchema<T, S, F>,
|
|
135
135
|
): ValidationData<T> {
|
|
136
136
|
throw new Error('Unexpectedly calling the `validateFormData()` method during schema parsing');
|
|
137
137
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import forEach from 'lodash/forEach';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
|
|
4
3
|
import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types';
|
|
5
|
-
import {
|
|
4
|
+
import { ITEMS_KEY, PROPERTIES_KEY } from '../constants';
|
|
6
5
|
import ParserValidator, { SchemaMap } from './ParserValidator';
|
|
7
|
-
import {
|
|
6
|
+
import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema';
|
|
7
|
+
import deepEquals from '../deepEquals';
|
|
8
8
|
|
|
9
9
|
/** Recursive function used to parse the given `schema` belonging to the `rootSchema`. The `validator` is used to
|
|
10
10
|
* capture the sub-schemas that the `isValid()` function is called with. For each schema returned by the
|
|
@@ -20,11 +20,11 @@ function parseSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
20
20
|
validator: ParserValidator<T, S, F>,
|
|
21
21
|
recurseList: S[],
|
|
22
22
|
rootSchema: S,
|
|
23
|
-
schema: S
|
|
23
|
+
schema: S,
|
|
24
24
|
) {
|
|
25
25
|
const schemas = retrieveSchemaInternal<T, S, F>(validator, schema, rootSchema, undefined, true);
|
|
26
26
|
schemas.forEach((schema) => {
|
|
27
|
-
const sameSchemaIndex = recurseList.findIndex((item) =>
|
|
27
|
+
const sameSchemaIndex = recurseList.findIndex((item) => deepEquals(item, schema));
|
|
28
28
|
if (sameSchemaIndex === -1) {
|
|
29
29
|
recurseList.push(schema);
|
|
30
30
|
const allOptions = resolveAnyOrOneOfSchemas<T, S, F>(validator, schema, rootSchema, true);
|
|
@@ -49,7 +49,7 @@ function parseSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
49
49
|
* @returns - The `SchemaMap` of all schemas that were parsed
|
|
50
50
|
*/
|
|
51
51
|
export default function schemaParser<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
52
|
-
rootSchema: S
|
|
52
|
+
rootSchema: S,
|
|
53
53
|
): SchemaMap<S> {
|
|
54
54
|
const validator = new ParserValidator<T, S, F>(rootSchema);
|
|
55
55
|
const recurseList: S[] = [];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import has from 'lodash/has';
|
|
3
|
+
|
|
4
|
+
import findSelectedOptionInXxxOf from './findSelectedOptionInXxxOf';
|
|
5
|
+
import getFromSchema from './getFromSchema';
|
|
6
|
+
import { ANY_OF_KEY, ONE_OF_KEY, PROPERTIES_KEY, REQUIRED_KEY } from '../constants';
|
|
7
|
+
import {
|
|
8
|
+
Experimental_CustomMergeAllOf,
|
|
9
|
+
FormContextType,
|
|
10
|
+
FoundFieldType,
|
|
11
|
+
RJSFSchema,
|
|
12
|
+
StrictRJSFSchema,
|
|
13
|
+
ValidatorType,
|
|
14
|
+
} from '../types';
|
|
15
|
+
|
|
16
|
+
/** Unique schema that represents no schema was found, exported for testing purposes */
|
|
17
|
+
export const NOT_FOUND_SCHEMA = { title: '!@#$_UNKNOWN_$#@!' };
|
|
18
|
+
|
|
19
|
+
/** Finds the field specified by the `path` within the root or recursed `schema`. If there is no field for the specified
|
|
20
|
+
* `path`, then the default `{ field: undefined, isRequired: undefined }` is returned. It determines whether a leaf
|
|
21
|
+
* field is in the `required` list for its parent and if so, it is marked as required on return.
|
|
22
|
+
*
|
|
23
|
+
* @param validator - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs
|
|
24
|
+
* @param rootSchema - The root schema that will be forwarded to all the APIs
|
|
25
|
+
// * @param schema - The node within the JSON schema in which to search
|
|
26
|
+
* @param path - The keys in the path to the desired field
|
|
27
|
+
* @param [formData={}] - The form data that is used to determine which anyOf/oneOf option to descend
|
|
28
|
+
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
|
|
29
|
+
* @returns - An object that contains the field and its required state. If no field can be found then
|
|
30
|
+
* `{ field: undefined, isRequired: undefined }` is returned.
|
|
31
|
+
*/
|
|
32
|
+
export default function findFieldInSchema<
|
|
33
|
+
T = undefined,
|
|
34
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
35
|
+
F extends FormContextType = any,
|
|
36
|
+
>(
|
|
37
|
+
validator: ValidatorType<T, S, F>,
|
|
38
|
+
rootSchema: S,
|
|
39
|
+
schema: S,
|
|
40
|
+
path: string | string[],
|
|
41
|
+
formData: T = {} as T,
|
|
42
|
+
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
43
|
+
): FoundFieldType<S> {
|
|
44
|
+
const pathList = Array.isArray(path) ? [...path] : path.split('.');
|
|
45
|
+
let parentField = schema;
|
|
46
|
+
|
|
47
|
+
// store the desired field into a variable and removing it from the `pathList`
|
|
48
|
+
const fieldName = pathList.pop()!;
|
|
49
|
+
|
|
50
|
+
if (pathList.length) {
|
|
51
|
+
// drilling into the schema for each sub-path and taking into account of the any/oneOfs
|
|
52
|
+
pathList.forEach((subPath) => {
|
|
53
|
+
parentField = getFromSchema<T, S, F>(
|
|
54
|
+
validator,
|
|
55
|
+
rootSchema,
|
|
56
|
+
parentField,
|
|
57
|
+
[PROPERTIES_KEY, subPath],
|
|
58
|
+
{} as S,
|
|
59
|
+
experimental_customMergeAllOf,
|
|
60
|
+
);
|
|
61
|
+
if (has(parentField, ONE_OF_KEY)) {
|
|
62
|
+
// if this sub-path has a `oneOf` then use the formData to drill into the schema with the selected option
|
|
63
|
+
parentField = findSelectedOptionInXxxOf(
|
|
64
|
+
validator,
|
|
65
|
+
rootSchema,
|
|
66
|
+
parentField,
|
|
67
|
+
fieldName,
|
|
68
|
+
ONE_OF_KEY,
|
|
69
|
+
get(formData, subPath),
|
|
70
|
+
experimental_customMergeAllOf,
|
|
71
|
+
)!;
|
|
72
|
+
} else if (has(parentField, ANY_OF_KEY)) {
|
|
73
|
+
// if this sub-path has a `anyOf` then use the formData to drill into the schema with the selected option
|
|
74
|
+
parentField = findSelectedOptionInXxxOf(
|
|
75
|
+
validator,
|
|
76
|
+
rootSchema,
|
|
77
|
+
parentField,
|
|
78
|
+
fieldName,
|
|
79
|
+
ANY_OF_KEY,
|
|
80
|
+
get(formData, subPath),
|
|
81
|
+
experimental_customMergeAllOf,
|
|
82
|
+
)!;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (has(parentField, ONE_OF_KEY)) {
|
|
88
|
+
// When oneOf is in the root schema, use the formData to drill into the schema with the selected option
|
|
89
|
+
parentField = findSelectedOptionInXxxOf(
|
|
90
|
+
validator,
|
|
91
|
+
rootSchema,
|
|
92
|
+
parentField,
|
|
93
|
+
fieldName,
|
|
94
|
+
ONE_OF_KEY,
|
|
95
|
+
formData,
|
|
96
|
+
experimental_customMergeAllOf,
|
|
97
|
+
)!;
|
|
98
|
+
} else if (has(parentField, ANY_OF_KEY)) {
|
|
99
|
+
// When anyOf is in the root schema, use the formData to drill into the schema with the selected option
|
|
100
|
+
parentField = findSelectedOptionInXxxOf(
|
|
101
|
+
validator,
|
|
102
|
+
rootSchema,
|
|
103
|
+
parentField,
|
|
104
|
+
fieldName,
|
|
105
|
+
ANY_OF_KEY,
|
|
106
|
+
formData,
|
|
107
|
+
experimental_customMergeAllOf,
|
|
108
|
+
)!;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// taking the most updated `parentField`, get our desired field
|
|
112
|
+
let field: S | undefined = getFromSchema<T, S, F>(
|
|
113
|
+
validator,
|
|
114
|
+
rootSchema,
|
|
115
|
+
parentField,
|
|
116
|
+
[PROPERTIES_KEY, fieldName],
|
|
117
|
+
NOT_FOUND_SCHEMA as S,
|
|
118
|
+
experimental_customMergeAllOf,
|
|
119
|
+
);
|
|
120
|
+
if (field === NOT_FOUND_SCHEMA) {
|
|
121
|
+
field = undefined;
|
|
122
|
+
}
|
|
123
|
+
// check to see if our desired field is in the `required` list for its parent
|
|
124
|
+
const requiredArray = getFromSchema<T, S, F>(
|
|
125
|
+
validator,
|
|
126
|
+
rootSchema,
|
|
127
|
+
parentField,
|
|
128
|
+
REQUIRED_KEY,
|
|
129
|
+
[] as T,
|
|
130
|
+
experimental_customMergeAllOf,
|
|
131
|
+
);
|
|
132
|
+
let isRequired: boolean | undefined;
|
|
133
|
+
if (field && Array.isArray(requiredArray)) {
|
|
134
|
+
isRequired = requiredArray.includes(fieldName);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { field, isRequired };
|
|
138
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
|
|
4
|
+
import { CONST_KEY, DEFAULT_KEY, PROPERTIES_KEY } from '../constants';
|
|
5
|
+
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
|
|
6
|
+
import retrieveSchema from './retrieveSchema';
|
|
7
|
+
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
|
|
8
|
+
|
|
9
|
+
/** Finds the option inside the `schema['any/oneOf']` list which has the `properties[selectorField].default` or
|
|
10
|
+
* `properties[selectorField].const` that matches the `formData[selectorField]` value. For the purposes of this
|
|
11
|
+
* function, `selectorField` is either `schema.discriminator.propertyName` or `fallbackField`. The `LayoutGridField`
|
|
12
|
+
* works directly with schemas in a recursive manner, making this faster than `getFirstMatchingOption()`.
|
|
13
|
+
*
|
|
14
|
+
* @param validator - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs
|
|
15
|
+
* @param rootSchema - The root schema that will be forwarded to all the APIs
|
|
16
|
+
* @param schema - The schema element in which to search for the selected anyOf/oneOf option
|
|
17
|
+
* @param fallbackField - The field to use as a backup selector field if the schema does not have a required field
|
|
18
|
+
* @param xxx - Either `anyOf` or `oneOf`, defines which value is being sought
|
|
19
|
+
* @param [formData={}] - The form data that is used to determine which anyOf/oneOf option to descend
|
|
20
|
+
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
|
|
21
|
+
* @returns - The anyOf/oneOf option that matches the selector field in the schema or undefined if nothing is selected
|
|
22
|
+
*/
|
|
23
|
+
export default function findSelectedOptionInXxxOf<
|
|
24
|
+
T = any,
|
|
25
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
26
|
+
F extends FormContextType = any,
|
|
27
|
+
>(
|
|
28
|
+
validator: ValidatorType<T, S, F>,
|
|
29
|
+
rootSchema: S,
|
|
30
|
+
schema: S,
|
|
31
|
+
fallbackField: string,
|
|
32
|
+
xxx: 'anyOf' | 'oneOf',
|
|
33
|
+
formData: T = {} as T,
|
|
34
|
+
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
35
|
+
): S | undefined {
|
|
36
|
+
if (Array.isArray(schema[xxx])) {
|
|
37
|
+
const discriminator = getDiscriminatorFieldFromSchema<S>(schema);
|
|
38
|
+
const selectorField = discriminator || fallbackField;
|
|
39
|
+
const xxxOfs = schema[xxx]!.map((xxxOf) =>
|
|
40
|
+
retrieveSchema<T, S, F>(validator, xxxOf as S, rootSchema, formData, experimental_customMergeAllOf),
|
|
41
|
+
);
|
|
42
|
+
const data = get(formData, selectorField);
|
|
43
|
+
if (data !== undefined) {
|
|
44
|
+
return xxxOfs.find((xxx) => {
|
|
45
|
+
return isEqual(
|
|
46
|
+
get(xxx, [PROPERTIES_KEY, selectorField, DEFAULT_KEY], get(xxx, [PROPERTIES_KEY, selectorField, CONST_KEY])),
|
|
47
|
+
data,
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
@@ -10,7 +10,7 @@ import getFirstMatchingOption from './getFirstMatchingOption';
|
|
|
10
10
|
import retrieveSchema, { resolveAllReferences } from './retrieveSchema';
|
|
11
11
|
import { ONE_OF_KEY, REF_KEY, JUNK_OPTION_ID, ANY_OF_KEY } from '../constants';
|
|
12
12
|
import guessType from '../guessType';
|
|
13
|
-
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
|
|
13
|
+
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
|
|
14
14
|
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
|
|
15
15
|
import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator';
|
|
16
16
|
|
|
@@ -45,13 +45,15 @@ export const JUNK_OPTION: StrictRJSFSchema = {
|
|
|
45
45
|
* @param rootSchema - The root JSON schema of the entire form
|
|
46
46
|
* @param schema - The schema for which the score is being calculated
|
|
47
47
|
* @param formData - The form data associated with the schema, used to calculate the score
|
|
48
|
+
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
|
|
48
49
|
* @returns - The score a schema against the formData
|
|
49
50
|
*/
|
|
50
51
|
export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
51
52
|
validator: ValidatorType<T, S, F>,
|
|
52
53
|
rootSchema: S,
|
|
53
54
|
schema?: S,
|
|
54
|
-
formData
|
|
55
|
+
formData?: any,
|
|
56
|
+
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
55
57
|
): number {
|
|
56
58
|
let totalScore = 0;
|
|
57
59
|
if (schema) {
|
|
@@ -64,8 +66,23 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
64
66
|
return score;
|
|
65
67
|
}
|
|
66
68
|
if (has(value, REF_KEY)) {
|
|
67
|
-
const newSchema = retrieveSchema<T, S, F>(
|
|
68
|
-
|
|
69
|
+
const newSchema = retrieveSchema<T, S, F>(
|
|
70
|
+
validator,
|
|
71
|
+
value as S,
|
|
72
|
+
rootSchema,
|
|
73
|
+
formValue,
|
|
74
|
+
experimental_customMergeAllOf,
|
|
75
|
+
);
|
|
76
|
+
return (
|
|
77
|
+
score +
|
|
78
|
+
calculateIndexScore<T, S, F>(
|
|
79
|
+
validator,
|
|
80
|
+
rootSchema,
|
|
81
|
+
newSchema,
|
|
82
|
+
formValue || {},
|
|
83
|
+
experimental_customMergeAllOf,
|
|
84
|
+
)
|
|
85
|
+
);
|
|
69
86
|
}
|
|
70
87
|
if ((has(value, ONE_OF_KEY) || has(value, ANY_OF_KEY)) && formValue) {
|
|
71
88
|
const key = has(value, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
|
|
@@ -78,12 +95,20 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
78
95
|
formValue,
|
|
79
96
|
get(value, key) as S[],
|
|
80
97
|
-1,
|
|
81
|
-
discriminator
|
|
98
|
+
discriminator,
|
|
99
|
+
experimental_customMergeAllOf,
|
|
82
100
|
)
|
|
83
101
|
);
|
|
84
102
|
}
|
|
85
103
|
if (value.type === 'object') {
|
|
86
|
-
|
|
104
|
+
if (isObject(formValue)) {
|
|
105
|
+
// If the structure is matching then give it a little boost in score
|
|
106
|
+
score += 1;
|
|
107
|
+
}
|
|
108
|
+
return (
|
|
109
|
+
score +
|
|
110
|
+
calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue, experimental_customMergeAllOf)
|
|
111
|
+
);
|
|
87
112
|
}
|
|
88
113
|
if (value.type === guessType(formValue)) {
|
|
89
114
|
// If the types match, then we bump the score by one
|
|
@@ -102,7 +127,7 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
102
127
|
}
|
|
103
128
|
return score;
|
|
104
129
|
},
|
|
105
|
-
0
|
|
130
|
+
0,
|
|
106
131
|
);
|
|
107
132
|
} else if (isString(schema.type) && schema.type === guessType(formData)) {
|
|
108
133
|
totalScore += 1;
|
|
@@ -131,19 +156,21 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
131
156
|
* @param [selectedOption=-1] - The index of the currently selected option, defaulted to -1 if not specified
|
|
132
157
|
* @param [discriminatorField] - The optional name of the field within the options object whose value is used to
|
|
133
158
|
* determine which option is selected
|
|
159
|
+
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
|
|
134
160
|
* @returns - The index of the option that is the closest match to the `formData` or the `selectedOption` if no match
|
|
135
161
|
*/
|
|
136
162
|
export default function getClosestMatchingOption<
|
|
137
163
|
T = any,
|
|
138
164
|
S extends StrictRJSFSchema = RJSFSchema,
|
|
139
|
-
F extends FormContextType = any
|
|
165
|
+
F extends FormContextType = any,
|
|
140
166
|
>(
|
|
141
167
|
validator: ValidatorType<T, S, F>,
|
|
142
168
|
rootSchema: S,
|
|
143
169
|
formData: T | undefined,
|
|
144
170
|
options: S[],
|
|
145
171
|
selectedOption = -1,
|
|
146
|
-
discriminatorField?: string
|
|
172
|
+
discriminatorField?: string,
|
|
173
|
+
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
147
174
|
): number {
|
|
148
175
|
// First resolve any refs in the options
|
|
149
176
|
const resolvedOptions = options.map((option) => {
|
|
@@ -181,14 +208,14 @@ export default function getClosestMatchingOption<
|
|
|
181
208
|
(scoreData: BestType, index: number) => {
|
|
182
209
|
const { bestScore } = scoreData;
|
|
183
210
|
const option = resolvedOptions[index];
|
|
184
|
-
const score = calculateIndexScore(validator, rootSchema, option, formData);
|
|
211
|
+
const score = calculateIndexScore(validator, rootSchema, option, formData, experimental_customMergeAllOf);
|
|
185
212
|
scoreCount.add(score);
|
|
186
213
|
if (score > bestScore) {
|
|
187
214
|
return { bestIndex: index, bestScore: score };
|
|
188
215
|
}
|
|
189
216
|
return scoreData;
|
|
190
217
|
},
|
|
191
|
-
{ bestIndex: selectedOption, bestScore: 0 }
|
|
218
|
+
{ bestIndex: selectedOption, bestScore: 0 },
|
|
192
219
|
);
|
|
193
220
|
// if all scores are the same go with selectedOption
|
|
194
221
|
if (scoreCount.size === 1 && selectedOption >= 0) {
|