@rjsf/utils 5.23.2 → 5.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +217 -130
- package/dist/index.js.map +4 -4
- package/dist/utils.esm.js +217 -130
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +209 -122
- package/lib/constIsAjvDataReference.d.ts +9 -0
- package/lib/constIsAjvDataReference.js +15 -0
- package/lib/constIsAjvDataReference.js.map +1 -0
- package/lib/enumOptionsDeselectValue.js +3 -3
- package/lib/enumOptionsDeselectValue.js.map +1 -1
- package/lib/enumOptionsIsSelected.js +3 -3
- package/lib/enumOptionsIsSelected.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/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/mergeDefaultsWithFormData.d.ts +5 -1
- package/lib/mergeDefaultsWithFormData.js +31 -8
- package/lib/mergeDefaultsWithFormData.js.map +1 -1
- package/lib/parser/ParserValidator.js +3 -3
- package/lib/parser/ParserValidator.js.map +1 -1
- package/lib/parser/schemaParser.js +4 -4
- package/lib/parser/schemaParser.js.map +1 -1
- package/lib/schema/getDefaultFormState.d.ts +17 -2
- package/lib/schema/getDefaultFormState.js +85 -31
- package/lib/schema/getDefaultFormState.js.map +1 -1
- package/lib/schema/retrieveSchema.js +7 -4
- package/lib/schema/retrieveSchema.js.map +1 -1
- package/lib/schema/toIdSchema.js +2 -2
- package/lib/schema/toIdSchema.js.map +1 -1
- package/lib/schema/toPathSchema.js +3 -3
- package/lib/schema/toPathSchema.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/constIsAjvDataReference.ts +17 -0
- package/src/enumOptionsDeselectValue.ts +3 -4
- package/src/enumOptionsIsSelected.ts +3 -4
- package/src/getChangedFields.ts +40 -0
- package/src/index.ts +2 -0
- package/src/mergeDefaultsWithFormData.ts +42 -10
- package/src/parser/ParserValidator.ts +3 -3
- package/src/parser/schemaParser.ts +4 -4
- package/src/schema/getDefaultFormState.ts +126 -31
- package/src/schema/retrieveSchema.ts +8 -5
- package/src/schema/toIdSchema.ts +2 -2
- package/src/schema/toPathSchema.ts +3 -3
|
@@ -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
|
*
|
|
@@ -19,47 +20,78 @@ import { GenericObjectType } from '../src';
|
|
|
19
20
|
* @param [formData] - The form data into which the defaults will be merged
|
|
20
21
|
* @param [mergeExtraArrayDefaults=false] - If true, any additional default array entries are appended onto the formData
|
|
21
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.
|
|
22
27
|
* @returns - The resulting merged form data with defaults
|
|
23
28
|
*/
|
|
24
29
|
export default function mergeDefaultsWithFormData<T = any>(
|
|
25
30
|
defaults?: T,
|
|
26
31
|
formData?: T,
|
|
27
32
|
mergeExtraArrayDefaults = false,
|
|
28
|
-
defaultSupercedesUndefined = false
|
|
33
|
+
defaultSupercedesUndefined = false,
|
|
34
|
+
overrideFormDataWithDefaults = false
|
|
29
35
|
): T | undefined {
|
|
30
36
|
if (Array.isArray(formData)) {
|
|
31
37
|
const defaultsArray = Array.isArray(defaults) ? defaults : [];
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
if (overrideOppositeArray[idx]) {
|
|
34
45
|
return mergeDefaultsWithFormData<any>(
|
|
35
46
|
defaultsArray[idx],
|
|
36
|
-
|
|
47
|
+
formData[idx],
|
|
37
48
|
mergeExtraArrayDefaults,
|
|
38
|
-
defaultSupercedesUndefined
|
|
49
|
+
defaultSupercedesUndefined,
|
|
50
|
+
overrideFormDataWithDefaults
|
|
39
51
|
);
|
|
40
52
|
}
|
|
41
53
|
return value;
|
|
42
54
|
});
|
|
55
|
+
|
|
43
56
|
// Merge any extra defaults when mergeExtraArrayDefaults is true
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
// Or when overrideFormDataWithDefaults is true and the default array is shorter than the formData array
|
|
58
|
+
if ((mergeExtraArrayDefaults || overrideFormDataWithDefaults) && mapped.length < overrideOppositeArray.length) {
|
|
59
|
+
mapped.push(...overrideOppositeArray.slice(mapped.length));
|
|
46
60
|
}
|
|
47
61
|
return mapped as unknown as T;
|
|
48
62
|
}
|
|
49
63
|
if (isObject(formData)) {
|
|
50
64
|
const acc: { [key in keyof T]: any } = Object.assign({}, defaults); // Prevent mutation of source object.
|
|
51
65
|
return Object.keys(formData as GenericObjectType).reduce((acc, key) => {
|
|
66
|
+
const keyValue = get(formData, key);
|
|
67
|
+
const keyExistsInDefaults = isObject(defaults) && key in (defaults as GenericObjectType);
|
|
68
|
+
const keyExistsInFormData = key in (formData as GenericObjectType);
|
|
52
69
|
acc[key as keyof T] = mergeDefaultsWithFormData<T>(
|
|
53
70
|
defaults ? get(defaults, key) : {},
|
|
54
|
-
|
|
71
|
+
keyValue,
|
|
55
72
|
mergeExtraArrayDefaults,
|
|
56
|
-
defaultSupercedesUndefined
|
|
73
|
+
defaultSupercedesUndefined,
|
|
74
|
+
// overrideFormDataWithDefaults can be true only when the key value exists in defaults
|
|
75
|
+
// Or if the key value doesn't exist in formData
|
|
76
|
+
overrideFormDataWithDefaults && (keyExistsInDefaults || !keyExistsInFormData)
|
|
57
77
|
);
|
|
58
78
|
return acc;
|
|
59
79
|
}, acc);
|
|
60
80
|
}
|
|
61
|
-
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* If the defaultSupercedesUndefined flag is true
|
|
84
|
+
* And formData is set to undefined or null and defaults are defined
|
|
85
|
+
* Or if formData is a number and is NaN return defaults
|
|
86
|
+
* Or if overrideFormDataWithDefaults flag is true and formData is set to not undefined/null return defaults
|
|
87
|
+
*/
|
|
88
|
+
if (
|
|
89
|
+
(defaultSupercedesUndefined &&
|
|
90
|
+
((!isNil(defaults) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) ||
|
|
91
|
+
(overrideFormDataWithDefaults && !isNil(formData))
|
|
92
|
+
) {
|
|
62
93
|
return defaults;
|
|
63
94
|
}
|
|
95
|
+
|
|
64
96
|
return formData;
|
|
65
97
|
}
|
|
@@ -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,7 +67,7 @@ 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(
|
|
@@ -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));
|
|
@@ -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
|
|
@@ -24,7 +24,7 @@ function parseSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
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);
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
|
+
import { JSONSchema7Object } from 'json-schema';
|
|
3
4
|
|
|
4
5
|
import {
|
|
6
|
+
ALL_OF_KEY,
|
|
5
7
|
ANY_OF_KEY,
|
|
6
8
|
CONST_KEY,
|
|
7
9
|
DEFAULT_KEY,
|
|
8
10
|
DEPENDENCIES_KEY,
|
|
9
|
-
PROPERTIES_KEY,
|
|
10
11
|
ONE_OF_KEY,
|
|
12
|
+
PROPERTIES_KEY,
|
|
11
13
|
REF_KEY,
|
|
12
|
-
ALL_OF_KEY,
|
|
13
14
|
} from '../constants';
|
|
14
15
|
import findSchemaDefinition from '../findSchemaDefinition';
|
|
15
16
|
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
@@ -30,8 +31,12 @@ import {
|
|
|
30
31
|
ValidatorType,
|
|
31
32
|
} from '../types';
|
|
32
33
|
import isMultiSelect from './isMultiSelect';
|
|
34
|
+
import isSelect from './isSelect';
|
|
33
35
|
import retrieveSchema, { resolveDependencies } from './retrieveSchema';
|
|
34
|
-
import
|
|
36
|
+
import isConstant from '../isConstant';
|
|
37
|
+
import constIsAjvDataReference from '../constIsAjvDataReference';
|
|
38
|
+
import optionsList from '../optionsList';
|
|
39
|
+
import deepEquals from '../deepEquals';
|
|
35
40
|
|
|
36
41
|
const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null'];
|
|
37
42
|
|
|
@@ -115,18 +120,17 @@ function maybeAddDefaultToObject<T = any>(
|
|
|
115
120
|
// Or if the schema has a const property defined, then we should always return the computedDefault since it's coming from the const.
|
|
116
121
|
obj[key] = computedDefault;
|
|
117
122
|
} else if (emptyObjectFields !== 'skipDefaults') {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const isSelfOrParentRequired = isParentRequired === undefined ? requiredFields.includes(key) : isParentRequired;
|
|
123
|
+
// If isParentRequired is undefined, then we are at the root level of the schema so defer to the requiredness of
|
|
124
|
+
// the field key itself in the `requiredField` list
|
|
125
|
+
const isSelfOrParentRequired = isParentRequired === undefined ? requiredFields.includes(key) : isParentRequired;
|
|
122
126
|
|
|
127
|
+
if (isObject(computedDefault)) {
|
|
123
128
|
// If emptyObjectFields 'skipEmptyDefaults' store computedDefault if it's a non-empty object(e.g. not {})
|
|
124
129
|
if (emptyObjectFields === 'skipEmptyDefaults') {
|
|
125
130
|
if (!isEmpty(computedDefault)) {
|
|
126
131
|
obj[key] = computedDefault;
|
|
127
132
|
}
|
|
128
|
-
}
|
|
129
|
-
// Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions
|
|
133
|
+
} // Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions
|
|
130
134
|
// Condition 1: If computedDefault is not empty or if the key is a required field
|
|
131
135
|
// Condition 2: If the parent object is required or emptyObjectFields is not 'populateRequiredDefaults'
|
|
132
136
|
else if (
|
|
@@ -138,11 +142,12 @@ function maybeAddDefaultToObject<T = any>(
|
|
|
138
142
|
} else if (
|
|
139
143
|
// Store computedDefault if it's a defined primitive (e.g., true) and satisfies certain conditions
|
|
140
144
|
// Condition 1: computedDefault is not undefined
|
|
141
|
-
// Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults)
|
|
145
|
+
// Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults)
|
|
146
|
+
// Or if isSelfOrParentRequired is 'true' and the key is a required field
|
|
142
147
|
computedDefault !== undefined &&
|
|
143
148
|
(emptyObjectFields === 'populateAllDefaults' ||
|
|
144
149
|
emptyObjectFields === 'skipEmptyDefaults' ||
|
|
145
|
-
requiredFields.includes(key))
|
|
150
|
+
(isSelfOrParentRequired && requiredFields.includes(key)))
|
|
146
151
|
) {
|
|
147
152
|
obj[key] = computedDefault;
|
|
148
153
|
}
|
|
@@ -169,6 +174,10 @@ interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema>
|
|
|
169
174
|
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>;
|
|
170
175
|
/** Optional flag, if true, indicates this schema was required in the parent schema. */
|
|
171
176
|
required?: boolean;
|
|
177
|
+
/** Optional flag, if true, It will merge defaults into formData.
|
|
178
|
+
* The formData should take precedence unless it's not valid. This is useful when for example the value from formData does not exist in the schema 'enum' property, in such cases we take the value from the defaults because the value from the formData is not valid.
|
|
179
|
+
*/
|
|
180
|
+
shouldMergeDefaultsIntoFormData?: boolean;
|
|
172
181
|
}
|
|
173
182
|
|
|
174
183
|
/** Computes the defaults for the current `schema` given the `rawFormData` and `parentDefaults` if any. This drills into
|
|
@@ -193,6 +202,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
193
202
|
experimental_defaultFormStateBehavior = undefined,
|
|
194
203
|
experimental_customMergeAllOf = undefined,
|
|
195
204
|
required,
|
|
205
|
+
shouldMergeDefaultsIntoFormData = false,
|
|
196
206
|
} = computeDefaultsProps;
|
|
197
207
|
const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
|
|
198
208
|
const schema: S = isObject(rawSchema) ? rawSchema : ({} as S);
|
|
@@ -203,8 +213,12 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
203
213
|
let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior;
|
|
204
214
|
let updatedRecurseList = _recurseList;
|
|
205
215
|
|
|
206
|
-
if (
|
|
207
|
-
|
|
216
|
+
if (
|
|
217
|
+
schema[CONST_KEY] &&
|
|
218
|
+
experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' &&
|
|
219
|
+
!constIsAjvDataReference(schema)
|
|
220
|
+
) {
|
|
221
|
+
defaults = schema[CONST_KEY] as unknown as T;
|
|
208
222
|
} else if (isObject(defaults) && isObject(schema.default)) {
|
|
209
223
|
// For object defaults, only override parent defaults that are defined in
|
|
210
224
|
// schema.default.
|
|
@@ -245,6 +259,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
245
259
|
parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined,
|
|
246
260
|
rawFormData: formData as T,
|
|
247
261
|
required,
|
|
262
|
+
shouldMergeDefaultsIntoFormData,
|
|
248
263
|
})
|
|
249
264
|
) as T[];
|
|
250
265
|
} else if (ONE_OF_KEY in schema) {
|
|
@@ -260,13 +275,16 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
260
275
|
experimental_dfsb_to_compute?.constAsDefaults === 'skipOneOf'
|
|
261
276
|
) {
|
|
262
277
|
// If we are in a oneOf of a primitive type, then we want to pass constAsDefaults as 'never' for the recursion
|
|
263
|
-
experimental_dfsb_to_compute = {
|
|
278
|
+
experimental_dfsb_to_compute = {
|
|
279
|
+
...experimental_dfsb_to_compute,
|
|
280
|
+
constAsDefaults: 'never',
|
|
281
|
+
};
|
|
264
282
|
}
|
|
265
283
|
schemaToCompute = oneOf![
|
|
266
284
|
getClosestMatchingOption<T, S, F>(
|
|
267
285
|
validator,
|
|
268
286
|
rootSchema,
|
|
269
|
-
|
|
287
|
+
rawFormData,
|
|
270
288
|
oneOf as S[],
|
|
271
289
|
0,
|
|
272
290
|
discriminator,
|
|
@@ -284,7 +302,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
284
302
|
getClosestMatchingOption<T, S, F>(
|
|
285
303
|
validator,
|
|
286
304
|
rootSchema,
|
|
287
|
-
|
|
305
|
+
rawFormData,
|
|
288
306
|
anyOf as S[],
|
|
289
307
|
0,
|
|
290
308
|
discriminator,
|
|
@@ -304,6 +322,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
304
322
|
parentDefaults: defaults as T | undefined,
|
|
305
323
|
rawFormData: formData as T,
|
|
306
324
|
required,
|
|
325
|
+
shouldMergeDefaultsIntoFormData,
|
|
307
326
|
});
|
|
308
327
|
}
|
|
309
328
|
|
|
@@ -314,7 +333,68 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
314
333
|
|
|
315
334
|
const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults);
|
|
316
335
|
|
|
317
|
-
|
|
336
|
+
let defaultsWithFormData = defaultBasedOnSchemaType ?? defaults;
|
|
337
|
+
// if shouldMergeDefaultsIntoFormData is true, then merge the defaults into the formData.
|
|
338
|
+
if (shouldMergeDefaultsIntoFormData) {
|
|
339
|
+
const { arrayMinItems = {} } = experimental_defaultFormStateBehavior || {};
|
|
340
|
+
const { mergeExtraDefaults } = arrayMinItems;
|
|
341
|
+
|
|
342
|
+
const matchingFormData = ensureFormDataMatchingSchema(
|
|
343
|
+
validator,
|
|
344
|
+
schema,
|
|
345
|
+
rootSchema,
|
|
346
|
+
rawFormData,
|
|
347
|
+
experimental_defaultFormStateBehavior
|
|
348
|
+
);
|
|
349
|
+
if (!isObject(rawFormData)) {
|
|
350
|
+
defaultsWithFormData = mergeDefaultsWithFormData<T>(
|
|
351
|
+
defaultsWithFormData as T,
|
|
352
|
+
matchingFormData as T,
|
|
353
|
+
mergeExtraDefaults,
|
|
354
|
+
true
|
|
355
|
+
) as T;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return defaultsWithFormData;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Ensure that the formData matches the given schema. If it's not matching in the case of a selectField, we change it to match the schema.
|
|
364
|
+
*
|
|
365
|
+
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
|
|
366
|
+
* @param schema - The schema for which the formData state is desired
|
|
367
|
+
* @param rootSchema - The root schema, used to primarily to look up `$ref`s
|
|
368
|
+
* @param formData - The current formData
|
|
369
|
+
* @param experimental_defaultFormStateBehavior - Optional configuration object, if provided, allows users to override default form state behavior
|
|
370
|
+
* @returns - valid formData that matches schema
|
|
371
|
+
*/
|
|
372
|
+
export function ensureFormDataMatchingSchema<
|
|
373
|
+
T = any,
|
|
374
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
375
|
+
F extends FormContextType = any
|
|
376
|
+
>(
|
|
377
|
+
validator: ValidatorType<T, S, F>,
|
|
378
|
+
schema: S,
|
|
379
|
+
rootSchema: S,
|
|
380
|
+
formData: T | undefined,
|
|
381
|
+
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior
|
|
382
|
+
): T | T[] | undefined {
|
|
383
|
+
const isSelectField = !isConstant(schema) && isSelect(validator, schema, rootSchema);
|
|
384
|
+
let validFormData: T | T[] | undefined = formData;
|
|
385
|
+
if (isSelectField) {
|
|
386
|
+
const getOptionsList = optionsList(schema);
|
|
387
|
+
const isValid = getOptionsList?.some((option) => deepEquals(option.value, formData));
|
|
388
|
+
validFormData = isValid ? formData : undefined;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Override the formData with the const if the constAsDefaults is set to always
|
|
392
|
+
const constTakesPrecedence = schema[CONST_KEY] && experimental_defaultFormStateBehavior?.constAsDefaults === 'always';
|
|
393
|
+
if (constTakesPrecedence) {
|
|
394
|
+
validFormData = schema.const as T;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return validFormData;
|
|
318
398
|
}
|
|
319
399
|
|
|
320
400
|
/** Computes the default value for objects.
|
|
@@ -336,6 +416,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
336
416
|
experimental_defaultFormStateBehavior = undefined,
|
|
337
417
|
experimental_customMergeAllOf = undefined,
|
|
338
418
|
required,
|
|
419
|
+
shouldMergeDefaultsIntoFormData,
|
|
339
420
|
}: ComputeDefaultsProps<T, S> = {},
|
|
340
421
|
defaults?: T | T[] | undefined
|
|
341
422
|
): T {
|
|
@@ -357,7 +438,8 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
357
438
|
const hasParentConst = isObject(parentConst) && (parentConst as JSONSchema7Object)[key] !== undefined;
|
|
358
439
|
const hasConst =
|
|
359
440
|
((isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst) &&
|
|
360
|
-
experimental_defaultFormStateBehavior?.constAsDefaults !== 'never'
|
|
441
|
+
experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' &&
|
|
442
|
+
!constIsAjvDataReference(propertySchema);
|
|
361
443
|
// Compute the defaults for this node, with the parent defaults we might
|
|
362
444
|
// have from a previous run: defaults[key].
|
|
363
445
|
const computedDefault = computeDefaults<T, S, F>(validator, propertySchema, {
|
|
@@ -369,6 +451,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
369
451
|
parentDefaults: get(defaults, [key]),
|
|
370
452
|
rawFormData: get(formData, [key]),
|
|
371
453
|
required: retrievedSchema.required?.includes(key),
|
|
454
|
+
shouldMergeDefaultsIntoFormData,
|
|
372
455
|
});
|
|
373
456
|
maybeAddDefaultToObject<T>(
|
|
374
457
|
acc,
|
|
@@ -413,6 +496,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
413
496
|
parentDefaults: get(defaults, [key]),
|
|
414
497
|
rawFormData: get(formData, [key]),
|
|
415
498
|
required: retrievedSchema.required?.includes(key),
|
|
499
|
+
shouldMergeDefaultsIntoFormData,
|
|
416
500
|
});
|
|
417
501
|
// Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop
|
|
418
502
|
maybeAddDefaultToObject<T>(
|
|
@@ -447,6 +531,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
447
531
|
experimental_defaultFormStateBehavior = undefined,
|
|
448
532
|
experimental_customMergeAllOf = undefined,
|
|
449
533
|
required,
|
|
534
|
+
shouldMergeDefaultsIntoFormData,
|
|
450
535
|
}: ComputeDefaultsProps<T, S> = {},
|
|
451
536
|
defaults?: T | T[] | undefined
|
|
452
537
|
): T | T[] | undefined {
|
|
@@ -474,6 +559,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
474
559
|
experimental_customMergeAllOf,
|
|
475
560
|
parentDefaults: item,
|
|
476
561
|
required,
|
|
562
|
+
shouldMergeDefaultsIntoFormData,
|
|
477
563
|
});
|
|
478
564
|
}) as T[];
|
|
479
565
|
}
|
|
@@ -493,6 +579,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
493
579
|
rawFormData: item,
|
|
494
580
|
parentDefaults: get(defaults, [idx]),
|
|
495
581
|
required,
|
|
582
|
+
shouldMergeDefaultsIntoFormData,
|
|
496
583
|
});
|
|
497
584
|
}) as T[];
|
|
498
585
|
|
|
@@ -541,6 +628,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
541
628
|
experimental_defaultFormStateBehavior,
|
|
542
629
|
experimental_customMergeAllOf,
|
|
543
630
|
required,
|
|
631
|
+
shouldMergeDefaultsIntoFormData,
|
|
544
632
|
})
|
|
545
633
|
) as T[];
|
|
546
634
|
// then fill up the rest with either the item default or empty, up to minItems
|
|
@@ -607,26 +695,33 @@ export default function getDefaultFormState<
|
|
|
607
695
|
throw new Error('Invalid schema: ' + theSchema);
|
|
608
696
|
}
|
|
609
697
|
const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, formData, experimental_customMergeAllOf);
|
|
698
|
+
|
|
699
|
+
// Get the computed defaults with 'shouldMergeDefaultsIntoFormData' set to true to merge defaults into formData.
|
|
700
|
+
// This is done when for example the value from formData does not exist in the schema 'enum' property, in such
|
|
701
|
+
// cases we take the value from the defaults because the value from the formData is not valid.
|
|
610
702
|
const defaults = computeDefaults<T, S, F>(validator, schema, {
|
|
611
703
|
rootSchema,
|
|
612
704
|
includeUndefinedValues,
|
|
613
705
|
experimental_defaultFormStateBehavior,
|
|
614
706
|
experimental_customMergeAllOf,
|
|
615
707
|
rawFormData: formData,
|
|
708
|
+
shouldMergeDefaultsIntoFormData: true,
|
|
616
709
|
});
|
|
617
710
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
711
|
+
// If the formData is an object or an array, add additional properties from formData and override formData with
|
|
712
|
+
// defaults since the defaults are already merged with formData.
|
|
713
|
+
if (isObject(formData) || Array.isArray(formData)) {
|
|
714
|
+
const { mergeDefaultsIntoFormData } = experimental_defaultFormStateBehavior || {};
|
|
715
|
+
const defaultSupercedesUndefined = mergeDefaultsIntoFormData === 'useDefaultIfFormDataUndefined';
|
|
716
|
+
const result = mergeDefaultsWithFormData<T | T[]>(
|
|
717
|
+
defaults,
|
|
718
|
+
formData,
|
|
719
|
+
true, // set to true to add any additional default array entries.
|
|
720
|
+
defaultSupercedesUndefined,
|
|
721
|
+
true // set to true to override formData with defaults if they exist.
|
|
722
|
+
);
|
|
723
|
+
return result;
|
|
630
724
|
}
|
|
631
|
-
|
|
725
|
+
|
|
726
|
+
return defaults;
|
|
632
727
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
import set from 'lodash/set';
|
|
4
3
|
import times from 'lodash/times';
|
|
5
4
|
import transform from 'lodash/transform';
|
|
@@ -15,10 +14,10 @@ import {
|
|
|
15
14
|
ANY_OF_KEY,
|
|
16
15
|
DEPENDENCIES_KEY,
|
|
17
16
|
IF_KEY,
|
|
17
|
+
ITEMS_KEY,
|
|
18
18
|
ONE_OF_KEY,
|
|
19
|
-
REF_KEY,
|
|
20
19
|
PROPERTIES_KEY,
|
|
21
|
-
|
|
20
|
+
REF_KEY,
|
|
22
21
|
} from '../constants';
|
|
23
22
|
import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition';
|
|
24
23
|
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
|
|
@@ -34,6 +33,7 @@ import {
|
|
|
34
33
|
ValidatorType,
|
|
35
34
|
} from '../types';
|
|
36
35
|
import getFirstMatchingOption from './getFirstMatchingOption';
|
|
36
|
+
import deepEquals from '../deepEquals';
|
|
37
37
|
|
|
38
38
|
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
|
|
39
39
|
* resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
|
|
@@ -256,7 +256,10 @@ export function resolveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema,
|
|
|
256
256
|
)
|
|
257
257
|
);
|
|
258
258
|
const allPermutations = getAllPermutationsOfXxxOf<S>(allOfSchemaElements);
|
|
259
|
-
return allPermutations.map((permutation) => ({
|
|
259
|
+
return allPermutations.map((permutation) => ({
|
|
260
|
+
...schema,
|
|
261
|
+
allOf: permutation,
|
|
262
|
+
}));
|
|
260
263
|
}
|
|
261
264
|
// No $ref or dependencies or allOf attribute was found, returning the original schema.
|
|
262
265
|
return [schema];
|
|
@@ -356,7 +359,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
|
|
|
356
359
|
};
|
|
357
360
|
}
|
|
358
361
|
|
|
359
|
-
return
|
|
362
|
+
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema;
|
|
360
363
|
}
|
|
361
364
|
|
|
362
365
|
/** Creates new 'properties' items for each key in the `formData`
|
package/src/schema/toIdSchema.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
|
|
4
3
|
import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants';
|
|
5
4
|
import isObject from '../isObject';
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
} from '../types';
|
|
15
14
|
import retrieveSchema from './retrieveSchema';
|
|
16
15
|
import getSchemaType from '../getSchemaType';
|
|
16
|
+
import deepEquals from '../deepEquals';
|
|
17
17
|
|
|
18
18
|
/** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against
|
|
19
19
|
* infinite recursion
|
|
@@ -42,7 +42,7 @@ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F
|
|
|
42
42
|
): IdSchema<T> {
|
|
43
43
|
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
|
|
44
44
|
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf);
|
|
45
|
-
const sameSchemaIndex = _recurseList.findIndex((item) =>
|
|
45
|
+
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
|
|
46
46
|
if (sameSchemaIndex === -1) {
|
|
47
47
|
return toIdSchemaInternal<T, S, F>(
|
|
48
48
|
validator,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
import set from 'lodash/set';
|
|
4
3
|
|
|
5
4
|
import {
|
|
5
|
+
ADDITIONAL_PROPERTIES_KEY,
|
|
6
6
|
ALL_OF_KEY,
|
|
7
7
|
ANY_OF_KEY,
|
|
8
|
-
ADDITIONAL_PROPERTIES_KEY,
|
|
9
8
|
DEPENDENCIES_KEY,
|
|
10
9
|
ITEMS_KEY,
|
|
11
10
|
NAME_KEY,
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
} from '../types';
|
|
27
26
|
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
28
27
|
import retrieveSchema from './retrieveSchema';
|
|
28
|
+
import deepEquals from '../deepEquals';
|
|
29
29
|
|
|
30
30
|
/** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
|
|
31
31
|
* infinite recursion
|
|
@@ -50,7 +50,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
|
|
|
50
50
|
): PathSchema<T> {
|
|
51
51
|
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
|
|
52
52
|
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf);
|
|
53
|
-
const sameSchemaIndex = _recurseList.findIndex((item) =>
|
|
53
|
+
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
|
|
54
54
|
if (sameSchemaIndex === -1) {
|
|
55
55
|
return toPathSchemaInternal<T, S, F>(
|
|
56
56
|
validator,
|