@rjsf/utils 5.20.1 → 5.21.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.
Files changed (39) hide show
  1. package/dist/index.js +186 -149
  2. package/dist/index.js.map +4 -4
  3. package/dist/utils.esm.js +186 -149
  4. package/dist/utils.esm.js.map +4 -4
  5. package/dist/utils.umd.js +191 -143
  6. package/lib/createSchemaUtils.js +1 -1
  7. package/lib/createSchemaUtils.js.map +1 -1
  8. package/lib/deepEquals.d.ts +1 -1
  9. package/lib/deepEquals.js +34 -10
  10. package/lib/deepEquals.js.map +1 -1
  11. package/lib/enumOptionsDeselectValue.js +3 -3
  12. package/lib/enumOptionsDeselectValue.js.map +1 -1
  13. package/lib/enumOptionsIsSelected.js +3 -3
  14. package/lib/enumOptionsIsSelected.js.map +1 -1
  15. package/lib/parser/ParserValidator.js +3 -3
  16. package/lib/parser/ParserValidator.js.map +1 -1
  17. package/lib/parser/schemaParser.js +4 -4
  18. package/lib/parser/schemaParser.js.map +1 -1
  19. package/lib/schema/getDefaultFormState.d.ts +39 -11
  20. package/lib/schema/getDefaultFormState.js +160 -125
  21. package/lib/schema/getDefaultFormState.js.map +1 -1
  22. package/lib/schema/retrieveSchema.js +7 -4
  23. package/lib/schema/retrieveSchema.js.map +1 -1
  24. package/lib/schema/toIdSchema.js +2 -2
  25. package/lib/schema/toIdSchema.js.map +1 -1
  26. package/lib/schema/toPathSchema.js +3 -3
  27. package/lib/schema/toPathSchema.js.map +1 -1
  28. package/lib/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +3 -2
  30. package/src/createSchemaUtils.ts +1 -1
  31. package/src/deepEquals.ts +37 -10
  32. package/src/enumOptionsDeselectValue.ts +3 -4
  33. package/src/enumOptionsIsSelected.ts +3 -4
  34. package/src/parser/ParserValidator.ts +3 -3
  35. package/src/parser/schemaParser.ts +4 -4
  36. package/src/schema/getDefaultFormState.ts +237 -156
  37. package/src/schema/retrieveSchema.ts +8 -5
  38. package/src/schema/toIdSchema.ts +2 -2
  39. package/src/schema/toPathSchema.ts +3 -3
@@ -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 (!isEqual(existing, identifiedSchema)) {
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 (!isEqual(rootSchema, this.rootSchema)) {
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 { PROPERTIES_KEY, ITEMS_KEY } from '../constants';
4
+ import { ITEMS_KEY, PROPERTIES_KEY } from '../constants';
6
5
  import ParserValidator, { SchemaMap } from './ParserValidator';
7
- import { retrieveSchemaInternal, resolveAnyOrOneOfSchemas } from '../schema/retrieveSchema';
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) => isEqual(item, schema));
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);
@@ -141,12 +141,22 @@ function maybeAddDefaultToObject<T = any>(
141
141
  }
142
142
 
143
143
  interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema> {
144
+ /** Any defaults provided by the parent field in the schema */
144
145
  parentDefaults?: T;
146
+ /** The options root schema, used to primarily to look up `$ref`s */
145
147
  rootSchema?: S;
148
+ /** The current formData, if any, onto which to provide any missing defaults */
146
149
  rawFormData?: T;
150
+ /** Optional flag, if true, cause undefined values to be added as defaults.
151
+ * If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
152
+ * false when computing defaults for any nested object properties.
153
+ */
147
154
  includeUndefinedValues?: boolean | 'excludeObjectChildren';
155
+ /** The list of ref names currently being recursed, used to prevent infinite recursion */
148
156
  _recurseList?: string[];
157
+ /** Optional configuration object, if provided, allows users to override default form state behavior */
149
158
  experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior;
159
+ /** Optional flag, if true, indicates this schema was required in the parent schema. */
150
160
  required?: boolean;
151
161
  }
152
162
 
@@ -155,22 +165,15 @@ interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema>
155
165
  *
156
166
  * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
157
167
  * @param rawSchema - The schema for which the default state is desired
158
- * @param [props] - Optional props for this function
159
- * @param [props.parentDefaults] - Any defaults provided by the parent field in the schema
160
- * @param [props.rootSchema] - The options root schema, used to primarily to look up `$ref`s
161
- * @param [props.rawFormData] - The current formData, if any, onto which to provide any missing defaults
162
- * @param [props.includeUndefinedValues=false] - Optional flag, if true, cause undefined values to be added as defaults.
163
- * If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
164
- * false when computing defaults for any nested object properties.
165
- * @param [props._recurseList=[]] - The list of ref names currently being recursed, used to prevent infinite recursion
166
- * @param [props.experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior
167
- * @param [props.required] - Optional flag, if true, indicates this schema was required in the parent schema.
168
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
168
169
  * @returns - The resulting `formData` with all the defaults provided
169
170
  */
170
171
  export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
171
172
  validator: ValidatorType<T, S, F>,
172
173
  rawSchema: S,
173
- {
174
+ computeDefaultsProps: ComputeDefaultsProps<T, S> = {}
175
+ ): T | T[] | undefined {
176
+ const {
174
177
  parentDefaults,
175
178
  rawFormData,
176
179
  rootSchema = {} as S,
@@ -178,8 +181,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
178
181
  _recurseList = [],
179
182
  experimental_defaultFormStateBehavior = undefined,
180
183
  required,
181
- }: ComputeDefaultsProps<T, S> = {}
182
- ): T | T[] | undefined {
184
+ } = computeDefaultsProps;
183
185
  const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
184
186
  const schema: S = isObject(rawSchema) ? rawSchema : ({} as S);
185
187
  // Compute the defaults recursively: give highest priority to deepest nodes.
@@ -202,7 +204,12 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
202
204
  schemaToCompute = findSchemaDefinition<S>(refName, rootSchema);
203
205
  }
204
206
  } else if (DEPENDENCIES_KEY in schema) {
205
- const resolvedSchema = resolveDependencies<T, S, F>(validator, schema, rootSchema, false, [], formData);
207
+ // Get the default if set from properties to ensure the dependencies conditions are resolved based on it
208
+ const defaultFormData: T = {
209
+ ...formData,
210
+ ...getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults),
211
+ };
212
+ const resolvedSchema = resolveDependencies<T, S, F>(validator, schema, rootSchema, false, [], defaultFormData);
206
213
  schemaToCompute = resolvedSchema[0]; // pick the first element from resolve dependencies
207
214
  } else if (isFixedItems(schema)) {
208
215
  defaults = (schema.items! as S[]).map((itemSchema: S, idx: number) =>
@@ -269,164 +276,238 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
269
276
  defaults = schema.default as unknown as T;
270
277
  }
271
278
 
272
- switch (getSchemaType<S>(schema)) {
273
- // We need to recurse for object schema inner default values.
274
- case 'object': {
275
- // This is a custom addition that fixes this issue:
276
- // https://github.com/rjsf-team/react-jsonschema-form/issues/3832
277
- const retrievedSchema =
278
- experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema
279
- ? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData)
280
- : schema;
281
- const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
282
- (acc: GenericObjectType, key: string) => {
283
- // Compute the defaults for this node, with the parent defaults we might
284
- // have from a previous run: defaults[key].
285
- const computedDefault = computeDefaults<T, S, F>(validator, get(retrievedSchema, [PROPERTIES_KEY, key]), {
286
- rootSchema,
287
- _recurseList,
288
- experimental_defaultFormStateBehavior,
289
- includeUndefinedValues: includeUndefinedValues === true,
290
- parentDefaults: get(defaults, [key]),
291
- rawFormData: get(formData, [key]),
292
- required: retrievedSchema.required?.includes(key),
293
- });
294
- maybeAddDefaultToObject<T>(
295
- acc,
296
- key,
297
- computedDefault,
298
- includeUndefinedValues,
299
- required,
300
- retrievedSchema.required,
301
- experimental_defaultFormStateBehavior
302
- );
303
- return acc;
304
- },
305
- {}
306
- ) as T;
307
- if (retrievedSchema.additionalProperties) {
308
- // as per spec additionalProperties may be either schema or boolean
309
- const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties)
310
- ? retrievedSchema.additionalProperties
311
- : {};
279
+ const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults);
312
280
 
313
- const keys = new Set<string>();
314
- if (isObject(defaults)) {
315
- Object.keys(defaults as GenericObjectType)
316
- .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
317
- .forEach((key) => keys.add(key));
318
- }
319
- const formDataRequired: string[] = [];
320
- Object.keys(formData as GenericObjectType)
321
- .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
322
- .forEach((key) => {
323
- keys.add(key);
324
- formDataRequired.push(key);
325
- });
326
- keys.forEach((key) => {
327
- const computedDefault = computeDefaults(validator, additionalPropertiesSchema as S, {
328
- rootSchema,
329
- _recurseList,
330
- experimental_defaultFormStateBehavior,
331
- includeUndefinedValues: includeUndefinedValues === true,
332
- parentDefaults: get(defaults, [key]),
333
- rawFormData: get(formData, [key]),
334
- required: retrievedSchema.required?.includes(key),
335
- });
336
- // Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop
337
- maybeAddDefaultToObject<T>(
338
- objectDefaults as GenericObjectType,
339
- key,
340
- computedDefault,
341
- includeUndefinedValues,
342
- required,
343
- formDataRequired
344
- );
345
- });
346
- }
347
- return objectDefaults;
348
- }
349
- case 'array': {
350
- const neverPopulate = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'never';
351
- const ignoreMinItemsFlagSet = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'requiredOnly';
352
- const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults';
353
- const computeSkipPopulate =
354
- experimental_defaultFormStateBehavior?.arrayMinItems?.computeSkipPopulate ?? (() => false);
281
+ return defaultBasedOnSchemaType ?? defaults;
282
+ }
355
283
 
356
- const emptyDefault = isSkipEmptyDefaults ? undefined : [];
284
+ /** Computes the default value for objects.
285
+ *
286
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
287
+ * @param rawSchema - The schema for which the default state is desired
288
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
289
+ * @param defaults - Optional props for this function
290
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
291
+ */
292
+ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
293
+ validator: ValidatorType<T, S, F>,
294
+ rawSchema: S,
295
+ {
296
+ rawFormData,
297
+ rootSchema = {} as S,
298
+ includeUndefinedValues = false,
299
+ _recurseList = [],
300
+ experimental_defaultFormStateBehavior = undefined,
301
+ required,
302
+ }: ComputeDefaultsProps<T, S> = {},
303
+ defaults?: T | T[] | undefined
304
+ ): T {
305
+ {
306
+ const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
307
+ const schema: S = rawSchema;
308
+ // This is a custom addition that fixes this issue:
309
+ // https://github.com/rjsf-team/react-jsonschema-form/issues/3832
310
+ const retrievedSchema =
311
+ experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema
312
+ ? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData)
313
+ : schema;
314
+ const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
315
+ (acc: GenericObjectType, key: string) => {
316
+ // Compute the defaults for this node, with the parent defaults we might
317
+ // have from a previous run: defaults[key].
318
+ const computedDefault = computeDefaults<T, S, F>(validator, get(retrievedSchema, [PROPERTIES_KEY, key]), {
319
+ rootSchema,
320
+ _recurseList,
321
+ experimental_defaultFormStateBehavior,
322
+ includeUndefinedValues: includeUndefinedValues === true,
323
+ parentDefaults: get(defaults, [key]),
324
+ rawFormData: get(formData, [key]),
325
+ required: retrievedSchema.required?.includes(key),
326
+ });
327
+ maybeAddDefaultToObject<T>(
328
+ acc,
329
+ key,
330
+ computedDefault,
331
+ includeUndefinedValues,
332
+ required,
333
+ retrievedSchema.required,
334
+ experimental_defaultFormStateBehavior
335
+ );
336
+ return acc;
337
+ },
338
+ {}
339
+ ) as T;
340
+ if (retrievedSchema.additionalProperties) {
341
+ // as per spec additionalProperties may be either schema or boolean
342
+ const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties)
343
+ ? retrievedSchema.additionalProperties
344
+ : {};
357
345
 
358
- // Inject defaults into existing array defaults
359
- if (Array.isArray(defaults)) {
360
- defaults = defaults.map((item, idx) => {
361
- const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx);
362
- return computeDefaults<T, S, F>(validator, schemaItem, {
363
- rootSchema,
364
- _recurseList,
365
- experimental_defaultFormStateBehavior,
366
- parentDefaults: item,
367
- required,
368
- });
369
- }) as T[];
346
+ const keys = new Set<string>();
347
+ if (isObject(defaults)) {
348
+ Object.keys(defaults as GenericObjectType)
349
+ .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
350
+ .forEach((key) => keys.add(key));
370
351
  }
352
+ const formDataRequired: string[] = [];
353
+ Object.keys(formData as GenericObjectType)
354
+ .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
355
+ .forEach((key) => {
356
+ keys.add(key);
357
+ formDataRequired.push(key);
358
+ });
359
+ keys.forEach((key) => {
360
+ const computedDefault = computeDefaults(validator, additionalPropertiesSchema as S, {
361
+ rootSchema,
362
+ _recurseList,
363
+ experimental_defaultFormStateBehavior,
364
+ includeUndefinedValues: includeUndefinedValues === true,
365
+ parentDefaults: get(defaults, [key]),
366
+ rawFormData: get(formData, [key]),
367
+ required: retrievedSchema.required?.includes(key),
368
+ });
369
+ // Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop
370
+ maybeAddDefaultToObject<T>(
371
+ objectDefaults as GenericObjectType,
372
+ key,
373
+ computedDefault,
374
+ includeUndefinedValues,
375
+ required,
376
+ formDataRequired
377
+ );
378
+ });
379
+ }
380
+ return objectDefaults;
381
+ }
382
+ }
371
383
 
372
- // Deeply inject defaults into already existing form data
373
- if (Array.isArray(rawFormData)) {
374
- const schemaItem: S = getInnerSchemaForArrayItem<S>(schema);
375
- if (neverPopulate) {
376
- defaults = rawFormData;
377
- } else {
378
- defaults = rawFormData.map((item: T, idx: number) => {
379
- return computeDefaults<T, S, F>(validator, schemaItem, {
380
- rootSchema,
381
- _recurseList,
382
- experimental_defaultFormStateBehavior,
383
- rawFormData: item,
384
- parentDefaults: get(defaults, [idx]),
385
- required,
386
- });
387
- }) as T[];
388
- }
389
- }
384
+ /** Computes the default value for arrays.
385
+ *
386
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
387
+ * @param rawSchema - The schema for which the default state is desired
388
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
389
+ * @param defaults - Optional props for this function
390
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
391
+ */
392
+ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
393
+ validator: ValidatorType<T, S, F>,
394
+ rawSchema: S,
395
+ {
396
+ rawFormData,
397
+ rootSchema = {} as S,
398
+ _recurseList = [],
399
+ experimental_defaultFormStateBehavior = undefined,
400
+ required,
401
+ }: ComputeDefaultsProps<T, S> = {},
402
+ defaults?: T | T[] | undefined
403
+ ): T | T[] | undefined {
404
+ const schema: S = rawSchema;
390
405
 
391
- if (neverPopulate) {
392
- return defaults ?? emptyDefault;
393
- }
394
- if (ignoreMinItemsFlagSet && !required) {
395
- // If no form data exists or defaults are set leave the field empty/non-existent, otherwise
396
- // return form data/defaults
397
- return defaults ? defaults : undefined;
398
- }
406
+ const neverPopulate = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'never';
407
+ const ignoreMinItemsFlagSet = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'requiredOnly';
408
+ const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults';
409
+ const computeSkipPopulate =
410
+ experimental_defaultFormStateBehavior?.arrayMinItems?.computeSkipPopulate ?? (() => false);
399
411
 
400
- const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
401
- if (
402
- !schema.minItems ||
403
- isMultiSelect<T, S, F>(validator, schema, rootSchema) ||
404
- computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
405
- schema.minItems <= defaultsLength
406
- ) {
407
- return defaults ? defaults : emptyDefault;
408
- }
412
+ const emptyDefault = isSkipEmptyDefaults ? undefined : [];
409
413
 
410
- const defaultEntries: T[] = (defaults || []) as T[];
411
- const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert);
412
- const fillerDefault = fillerSchema.default;
414
+ // Inject defaults into existing array defaults
415
+ if (Array.isArray(defaults)) {
416
+ defaults = defaults.map((item, idx) => {
417
+ const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx);
418
+ return computeDefaults<T, S, F>(validator, schemaItem, {
419
+ rootSchema,
420
+ _recurseList,
421
+ experimental_defaultFormStateBehavior,
422
+ parentDefaults: item,
423
+ required,
424
+ });
425
+ }) as T[];
426
+ }
413
427
 
414
- // Calculate filler entries for remaining items (minItems - existing raw data/defaults)
415
- const fillerEntries: T[] = new Array(schema.minItems - defaultsLength).fill(
416
- computeDefaults<any, S, F>(validator, fillerSchema, {
417
- parentDefaults: fillerDefault,
428
+ // Deeply inject defaults into already existing form data
429
+ if (Array.isArray(rawFormData)) {
430
+ const schemaItem: S = getInnerSchemaForArrayItem<S>(schema);
431
+ if (neverPopulate) {
432
+ defaults = rawFormData;
433
+ } else {
434
+ defaults = rawFormData.map((item: T, idx: number) => {
435
+ return computeDefaults<T, S, F>(validator, schemaItem, {
418
436
  rootSchema,
419
437
  _recurseList,
420
438
  experimental_defaultFormStateBehavior,
439
+ rawFormData: item,
440
+ parentDefaults: get(defaults, [idx]),
421
441
  required,
422
- })
423
- ) as T[];
424
- // then fill up the rest with either the item default or empty, up to minItems
425
- return defaultEntries.concat(fillerEntries);
442
+ });
443
+ }) as T[];
426
444
  }
427
445
  }
428
446
 
429
- return defaults;
447
+ if (neverPopulate) {
448
+ return defaults ?? emptyDefault;
449
+ }
450
+ if (ignoreMinItemsFlagSet && !required) {
451
+ // If no form data exists or defaults are set leave the field empty/non-existent, otherwise
452
+ // return form data/defaults
453
+ return defaults ? defaults : undefined;
454
+ }
455
+
456
+ const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
457
+ if (
458
+ !schema.minItems ||
459
+ isMultiSelect<T, S, F>(validator, schema, rootSchema) ||
460
+ computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
461
+ schema.minItems <= defaultsLength
462
+ ) {
463
+ return defaults ? defaults : emptyDefault;
464
+ }
465
+
466
+ const defaultEntries: T[] = (defaults || []) as T[];
467
+ const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert);
468
+ const fillerDefault = fillerSchema.default;
469
+
470
+ // Calculate filler entries for remaining items (minItems - existing raw data/defaults)
471
+ const fillerEntries: T[] = new Array(schema.minItems - defaultsLength).fill(
472
+ computeDefaults<any, S, F>(validator, fillerSchema, {
473
+ parentDefaults: fillerDefault,
474
+ rootSchema,
475
+ _recurseList,
476
+ experimental_defaultFormStateBehavior,
477
+ required,
478
+ })
479
+ ) as T[];
480
+ // then fill up the rest with either the item default or empty, up to minItems
481
+ return defaultEntries.concat(fillerEntries);
482
+ }
483
+
484
+ /** Computes the default value based on the schema type.
485
+ *
486
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
487
+ * @param rawSchema - The schema for which the default state is desired
488
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
489
+ * @param defaults - Optional props for this function
490
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
491
+ */
492
+ export function getDefaultBasedOnSchemaType<
493
+ T = any,
494
+ S extends StrictRJSFSchema = RJSFSchema,
495
+ F extends FormContextType = any
496
+ >(
497
+ validator: ValidatorType<T, S, F>,
498
+ rawSchema: S,
499
+ computeDefaultsProps: ComputeDefaultsProps<T, S> = {},
500
+ defaults?: T | T[] | undefined
501
+ ): T | T[] | void {
502
+ switch (getSchemaType<S>(rawSchema)) {
503
+ // We need to recurse for object schema inner default values.
504
+ case 'object': {
505
+ return getObjectDefaults(validator, rawSchema, computeDefaultsProps, defaults);
506
+ }
507
+ case 'array': {
508
+ return getArrayDefaults(validator, rawSchema, computeDefaultsProps, defaults);
509
+ }
510
+ }
430
511
  }
431
512
 
432
513
  /** Returns the superset of `formData` that includes the given set updated to include any missing fields that have
@@ -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
- ITEMS_KEY,
20
+ REF_KEY,
22
21
  } from '../constants';
23
22
  import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition';
24
23
  import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
@@ -27,6 +26,7 @@ import isObject from '../isObject';
27
26
  import mergeSchemas from '../mergeSchemas';
28
27
  import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
29
28
  import getFirstMatchingOption from './getFirstMatchingOption';
29
+ import deepEquals from '../deepEquals';
30
30
 
31
31
  /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
32
32
  * resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
@@ -196,7 +196,10 @@ export function resolveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema,
196
196
  )
197
197
  );
198
198
  const allPermutations = getAllPermutationsOfXxxOf<S>(allOfSchemaElements);
199
- return allPermutations.map((permutation) => ({ ...schema, allOf: permutation }));
199
+ return allPermutations.map((permutation) => ({
200
+ ...schema,
201
+ allOf: permutation,
202
+ }));
200
203
  }
201
204
  // No $ref or dependencies or allOf attribute was found, returning the original schema.
202
205
  return [schema];
@@ -293,7 +296,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
293
296
  };
294
297
  }
295
298
 
296
- return isEqual(schema, resolvedSchema) ? schema : resolvedSchema;
299
+ return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema;
297
300
  }
298
301
 
299
302
  /** Creates new 'properties' items for each key in the `formData`
@@ -1,11 +1,11 @@
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';
6
5
  import { FormContextType, GenericObjectType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
7
6
  import retrieveSchema from './retrieveSchema';
8
7
  import getSchemaType from '../getSchemaType';
8
+ import deepEquals from '../deepEquals';
9
9
 
10
10
  /** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against
11
11
  * infinite recursion
@@ -32,7 +32,7 @@ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F
32
32
  ): IdSchema<T> {
33
33
  if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
34
34
  const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
35
- const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
35
+ const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
36
36
  if (sameSchemaIndex === -1) {
37
37
  return toIdSchemaInternal<T, S, F>(
38
38
  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,
@@ -18,6 +17,7 @@ import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'
18
17
  import { FormContextType, GenericObjectType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
19
18
  import getClosestMatchingOption from './getClosestMatchingOption';
20
19
  import retrieveSchema from './retrieveSchema';
20
+ import deepEquals from '../deepEquals';
21
21
 
22
22
  /** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
23
23
  * infinite recursion
@@ -40,7 +40,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
40
40
  ): PathSchema<T> {
41
41
  if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
42
42
  const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
43
- const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
43
+ const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
44
44
  if (sameSchemaIndex === -1) {
45
45
  return toPathSchemaInternal<T, S, F>(
46
46
  validator,