@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.
Files changed (279) hide show
  1. package/dist/index.js +1281 -625
  2. package/dist/index.js.map +4 -4
  3. package/dist/utils.esm.js +1254 -598
  4. package/dist/utils.esm.js.map +4 -4
  5. package/dist/utils.umd.js +1201 -570
  6. package/lib/ErrorSchemaBuilder.d.ts +8 -4
  7. package/lib/ErrorSchemaBuilder.js +10 -8
  8. package/lib/ErrorSchemaBuilder.js.map +1 -1
  9. package/lib/allowAdditionalItems.d.ts +1 -1
  10. package/lib/allowAdditionalItems.js +1 -1
  11. package/lib/allowAdditionalItems.js.map +1 -1
  12. package/lib/asNumber.js.map +1 -1
  13. package/lib/canExpand.d.ts +1 -1
  14. package/lib/canExpand.js +2 -2
  15. package/lib/canExpand.js.map +1 -1
  16. package/lib/constIsAjvDataReference.d.ts +9 -0
  17. package/lib/constIsAjvDataReference.js +15 -0
  18. package/lib/constIsAjvDataReference.js.map +1 -0
  19. package/lib/constants.d.ts +11 -3
  20. package/lib/constants.js +11 -3
  21. package/lib/constants.js.map +1 -1
  22. package/lib/createErrorHandler.d.ts +1 -1
  23. package/lib/createErrorHandler.js +2 -2
  24. package/lib/createErrorHandler.js.map +1 -1
  25. package/lib/createSchemaUtils.d.ts +3 -2
  26. package/lib/createSchemaUtils.js +56 -46
  27. package/lib/createSchemaUtils.js.map +1 -1
  28. package/lib/dataURItoBlob.js.map +1 -1
  29. package/lib/dateRangeOptions.d.ts +1 -1
  30. package/lib/dateRangeOptions.js +1 -1
  31. package/lib/dateRangeOptions.js.map +1 -1
  32. package/lib/deepEquals.js +1 -1
  33. package/lib/deepEquals.js.map +1 -1
  34. package/lib/englishStringTranslator.d.ts +1 -1
  35. package/lib/englishStringTranslator.js +1 -1
  36. package/lib/enumOptionsDeselectValue.d.ts +1 -1
  37. package/lib/enumOptionsDeselectValue.js +4 -4
  38. package/lib/enumOptionsDeselectValue.js.map +1 -1
  39. package/lib/enumOptionsIndexForValue.d.ts +1 -1
  40. package/lib/enumOptionsIndexForValue.js +1 -1
  41. package/lib/enumOptionsIndexForValue.js.map +1 -1
  42. package/lib/enumOptionsIsSelected.d.ts +1 -1
  43. package/lib/enumOptionsIsSelected.js +3 -3
  44. package/lib/enumOptionsIsSelected.js.map +1 -1
  45. package/lib/enumOptionsSelectValue.d.ts +1 -1
  46. package/lib/enumOptionsSelectValue.js +2 -2
  47. package/lib/enumOptionsSelectValue.js.map +1 -1
  48. package/lib/enumOptionsValueForIndex.d.ts +1 -1
  49. package/lib/enumOptionsValueForIndex.js.map +1 -1
  50. package/lib/enums.d.ts +2 -0
  51. package/lib/enums.js +2 -0
  52. package/lib/enums.js.map +1 -1
  53. package/lib/findSchemaDefinition.d.ts +1 -1
  54. package/lib/findSchemaDefinition.js +2 -2
  55. package/lib/findSchemaDefinition.js.map +1 -1
  56. package/lib/getChangedFields.d.ts +17 -0
  57. package/lib/getChangedFields.js +42 -0
  58. package/lib/getChangedFields.js.map +1 -0
  59. package/lib/getDateElementProps.d.ts +1 -1
  60. package/lib/getDateElementProps.js.map +1 -1
  61. package/lib/getDiscriminatorFieldFromSchema.d.ts +1 -1
  62. package/lib/getDiscriminatorFieldFromSchema.js +4 -3
  63. package/lib/getDiscriminatorFieldFromSchema.js.map +1 -1
  64. package/lib/getInputProps.d.ts +1 -1
  65. package/lib/getInputProps.js +4 -1
  66. package/lib/getInputProps.js.map +1 -1
  67. package/lib/getOptionMatchingSimpleDiscriminator.d.ts +1 -1
  68. package/lib/getOptionMatchingSimpleDiscriminator.js +2 -2
  69. package/lib/getOptionMatchingSimpleDiscriminator.js.map +1 -1
  70. package/lib/getSchemaType.d.ts +2 -1
  71. package/lib/getSchemaType.js +3 -2
  72. package/lib/getSchemaType.js.map +1 -1
  73. package/lib/getSubmitButtonOptions.d.ts +1 -1
  74. package/lib/getSubmitButtonOptions.js +2 -2
  75. package/lib/getSubmitButtonOptions.js.map +1 -1
  76. package/lib/getTemplate.d.ts +1 -1
  77. package/lib/getTemplate.js +9 -0
  78. package/lib/getTemplate.js.map +1 -1
  79. package/lib/getTestIds.d.ts +17 -0
  80. package/lib/getTestIds.js +34 -0
  81. package/lib/getTestIds.js.map +1 -0
  82. package/lib/getUiOptions.d.ts +1 -1
  83. package/lib/getUiOptions.js +2 -2
  84. package/lib/getUiOptions.js.map +1 -1
  85. package/lib/getWidget.d.ts +1 -1
  86. package/lib/getWidget.js +3 -3
  87. package/lib/getWidget.js.map +1 -1
  88. package/lib/guessType.d.ts +1 -1
  89. package/lib/guessType.js.map +1 -1
  90. package/lib/hasWidget.d.ts +1 -1
  91. package/lib/hasWidget.js +1 -1
  92. package/lib/hasWidget.js.map +1 -1
  93. package/lib/hashForSchema.d.ts +23 -1
  94. package/lib/hashForSchema.js +24 -6
  95. package/lib/hashForSchema.js.map +1 -1
  96. package/lib/idGenerators.d.ts +8 -1
  97. package/lib/idGenerators.js +11 -2
  98. package/lib/idGenerators.js.map +1 -1
  99. package/lib/index.d.ts +63 -60
  100. package/lib/index.js +63 -60
  101. package/lib/index.js.map +1 -1
  102. package/lib/isConstant.d.ts +1 -1
  103. package/lib/isConstant.js +1 -1
  104. package/lib/isCustomWidget.d.ts +1 -1
  105. package/lib/isCustomWidget.js +1 -1
  106. package/lib/isFixedItems.d.ts +1 -1
  107. package/lib/isFixedItems.js +1 -1
  108. package/lib/isObject.d.ts +2 -2
  109. package/lib/isObject.js +11 -4
  110. package/lib/isObject.js.map +1 -1
  111. package/lib/lookupFromFormContext.d.ts +11 -0
  112. package/lib/lookupFromFormContext.js +20 -0
  113. package/lib/lookupFromFormContext.js.map +1 -0
  114. package/lib/mergeDefaultsWithFormData.d.ts +8 -2
  115. package/lib/mergeDefaultsWithFormData.js +39 -10
  116. package/lib/mergeDefaultsWithFormData.js.map +1 -1
  117. package/lib/mergeObjects.d.ts +1 -1
  118. package/lib/mergeObjects.js +1 -1
  119. package/lib/mergeObjects.js.map +1 -1
  120. package/lib/mergeSchemas.d.ts +1 -1
  121. package/lib/mergeSchemas.js +4 -4
  122. package/lib/mergeSchemas.js.map +1 -1
  123. package/lib/optionsList.d.ts +9 -7
  124. package/lib/optionsList.js +30 -19
  125. package/lib/optionsList.js.map +1 -1
  126. package/lib/orderProperties.js.map +1 -1
  127. package/lib/pad.js.map +1 -1
  128. package/lib/parseDateString.d.ts +1 -1
  129. package/lib/parseDateString.js +1 -1
  130. package/lib/parseDateString.js.map +1 -1
  131. package/lib/parser/ParserValidator.d.ts +1 -1
  132. package/lib/parser/ParserValidator.js +6 -6
  133. package/lib/parser/ParserValidator.js.map +1 -1
  134. package/lib/parser/index.d.ts +2 -2
  135. package/lib/parser/index.js +1 -1
  136. package/lib/parser/schemaParser.d.ts +2 -2
  137. package/lib/parser/schemaParser.js +6 -6
  138. package/lib/parser/schemaParser.js.map +1 -1
  139. package/lib/rangeSpec.d.ts +2 -2
  140. package/lib/rangeSpec.js.map +1 -1
  141. package/lib/replaceStringParameters.js.map +1 -1
  142. package/lib/schema/findFieldInSchema.d.ts +19 -0
  143. package/lib/schema/findFieldInSchema.js +61 -0
  144. package/lib/schema/findFieldInSchema.js.map +1 -0
  145. package/lib/schema/findSelectedOptionInXxxOf.d.ts +16 -0
  146. package/lib/schema/findSelectedOptionInXxxOf.js +34 -0
  147. package/lib/schema/findSelectedOptionInXxxOf.js.map +1 -0
  148. package/lib/schema/getClosestMatchingOption.d.ts +5 -3
  149. package/lib/schema/getClosestMatchingOption.js +28 -20
  150. package/lib/schema/getClosestMatchingOption.js.map +1 -1
  151. package/lib/schema/getDefaultFormState.d.ts +60 -13
  152. package/lib/schema/getDefaultFormState.js +304 -166
  153. package/lib/schema/getDefaultFormState.js.map +1 -1
  154. package/lib/schema/getDisplayLabel.d.ts +3 -2
  155. package/lib/schema/getDisplayLabel.js +10 -9
  156. package/lib/schema/getDisplayLabel.js.map +1 -1
  157. package/lib/schema/getFirstMatchingOption.d.ts +1 -1
  158. package/lib/schema/getFirstMatchingOption.js +70 -2
  159. package/lib/schema/getFirstMatchingOption.js.map +1 -1
  160. package/lib/schema/getFromSchema.d.ts +14 -0
  161. package/lib/schema/getFromSchema.js +39 -0
  162. package/lib/schema/getFromSchema.js.map +1 -0
  163. package/lib/schema/index.d.ts +15 -14
  164. package/lib/schema/index.js +15 -14
  165. package/lib/schema/index.js.map +1 -1
  166. package/lib/schema/isFilesArray.d.ts +3 -2
  167. package/lib/schema/isFilesArray.js +5 -4
  168. package/lib/schema/isFilesArray.js.map +1 -1
  169. package/lib/schema/isMultiSelect.d.ts +3 -2
  170. package/lib/schema/isMultiSelect.js +4 -3
  171. package/lib/schema/isMultiSelect.js.map +1 -1
  172. package/lib/schema/isSelect.d.ts +3 -2
  173. package/lib/schema/isSelect.js +5 -4
  174. package/lib/schema/isSelect.js.map +1 -1
  175. package/lib/schema/retrieveSchema.d.ts +28 -11
  176. package/lib/schema/retrieveSchema.js +142 -66
  177. package/lib/schema/retrieveSchema.js.map +1 -1
  178. package/lib/schema/sanitizeDataForNewSchema.d.ts +3 -2
  179. package/lib/schema/sanitizeDataForNewSchema.js +12 -11
  180. package/lib/schema/sanitizeDataForNewSchema.js.map +1 -1
  181. package/lib/schema/toIdSchema.d.ts +3 -2
  182. package/lib/schema/toIdSchema.js +30 -27
  183. package/lib/schema/toIdSchema.js.map +1 -1
  184. package/lib/schema/toPathSchema.d.ts +3 -2
  185. package/lib/schema/toPathSchema.js +22 -20
  186. package/lib/schema/toPathSchema.js.map +1 -1
  187. package/lib/schemaRequiresTrueValue.d.ts +1 -1
  188. package/lib/schemaRequiresTrueValue.js.map +1 -1
  189. package/lib/shouldRender.js +1 -1
  190. package/lib/toConstant.d.ts +1 -1
  191. package/lib/toConstant.js +1 -1
  192. package/lib/toConstant.js.map +1 -1
  193. package/lib/toDateString.d.ts +1 -1
  194. package/lib/toErrorList.d.ts +1 -1
  195. package/lib/toErrorList.js +2 -2
  196. package/lib/toErrorList.js.map +1 -1
  197. package/lib/toErrorSchema.d.ts +1 -1
  198. package/lib/toErrorSchema.js +2 -2
  199. package/lib/toErrorSchema.js.map +1 -1
  200. package/lib/tsconfig.tsbuildinfo +1 -1
  201. package/lib/types.d.ts +160 -131
  202. package/lib/unwrapErrorHandler.d.ts +1 -1
  203. package/lib/unwrapErrorHandler.js +1 -1
  204. package/lib/unwrapErrorHandler.js.map +1 -1
  205. package/lib/utcToLocal.js +1 -1
  206. package/lib/utcToLocal.js.map +1 -1
  207. package/lib/validationDataMerge.d.ts +1 -1
  208. package/lib/validationDataMerge.js +3 -3
  209. package/lib/validationDataMerge.js.map +1 -1
  210. package/lib/withIdRefPrefix.d.ts +1 -1
  211. package/lib/withIdRefPrefix.js +2 -2
  212. package/lib/withIdRefPrefix.js.map +1 -1
  213. package/package.json +36 -26
  214. package/src/ErrorSchemaBuilder.ts +15 -8
  215. package/src/canExpand.ts +2 -2
  216. package/src/constIsAjvDataReference.ts +17 -0
  217. package/src/constants.ts +12 -3
  218. package/src/createSchemaUtils.ts +140 -50
  219. package/src/dataURItoBlob.ts +1 -1
  220. package/src/dateRangeOptions.ts +1 -1
  221. package/src/enumOptionsDeselectValue.ts +4 -5
  222. package/src/enumOptionsIndexForValue.ts +1 -1
  223. package/src/enumOptionsIsSelected.ts +4 -5
  224. package/src/enumOptionsSelectValue.ts +1 -1
  225. package/src/enumOptionsValueForIndex.ts +1 -1
  226. package/src/enums.ts +2 -0
  227. package/src/findSchemaDefinition.ts +2 -2
  228. package/src/getChangedFields.ts +40 -0
  229. package/src/getDateElementProps.ts +2 -2
  230. package/src/getDiscriminatorFieldFromSchema.ts +2 -1
  231. package/src/getInputProps.ts +6 -2
  232. package/src/getOptionMatchingSimpleDiscriminator.ts +2 -2
  233. package/src/getSchemaType.ts +3 -2
  234. package/src/getSubmitButtonOptions.ts +1 -1
  235. package/src/getTemplate.ts +12 -1
  236. package/src/getTestIds.ts +40 -0
  237. package/src/getUiOptions.ts +2 -2
  238. package/src/getWidget.tsx +2 -2
  239. package/src/hasWidget.ts +1 -1
  240. package/src/hashForSchema.ts +26 -6
  241. package/src/idGenerators.ts +10 -0
  242. package/src/index.ts +21 -2
  243. package/src/isCustomWidget.ts +1 -1
  244. package/src/isObject.ts +12 -5
  245. package/src/labelValue.ts +2 -2
  246. package/src/lookupFromFormContext.ts +26 -0
  247. package/src/mergeDefaultsWithFormData.ts +54 -9
  248. package/src/mergeObjects.ts +24 -21
  249. package/src/optionsList.ts +31 -22
  250. package/src/parser/ParserValidator.ts +5 -5
  251. package/src/parser/schemaParser.ts +6 -6
  252. package/src/schema/findFieldInSchema.ts +138 -0
  253. package/src/schema/findSelectedOptionInXxxOf.ts +53 -0
  254. package/src/schema/getClosestMatchingOption.ts +38 -11
  255. package/src/schema/getDefaultFormState.ts +447 -191
  256. package/src/schema/getDisplayLabel.ts +7 -4
  257. package/src/schema/getFirstMatchingOption.ts +79 -4
  258. package/src/schema/getFromSchema.ts +100 -0
  259. package/src/schema/index.ts +6 -4
  260. package/src/schema/isFilesArray.ts +18 -3
  261. package/src/schema/isMultiSelect.ts +10 -4
  262. package/src/schema/isSelect.ts +5 -3
  263. package/src/schema/retrieveSchema.ts +256 -75
  264. package/src/schema/sanitizeDataForNewSchema.ts +52 -11
  265. package/src/schema/toIdSchema.ts +69 -43
  266. package/src/schema/toPathSchema.ts +49 -16
  267. package/src/toErrorList.ts +2 -2
  268. package/src/types.ts +266 -174
  269. package/src/validationDataMerge.ts +1 -1
  270. package/src/withIdRefPrefix.ts +1 -1
  271. package/LICENSE.md +0 -201
  272. package/lib/schema/getMatchingOption.d.ts +0 -14
  273. package/lib/schema/getMatchingOption.js +0 -85
  274. package/lib/schema/getMatchingOption.js.map +0 -1
  275. package/lib/schema/mergeValidationData.d.ts +0 -14
  276. package/lib/schema/mergeValidationData.js +0 -28
  277. package/lib/schema/mergeValidationData.js.map +0 -1
  278. package/src/schema/getMatchingOption.ts +0 -103
  279. package/src/schema/mergeValidationData.ts +0 -38
@@ -1,14 +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,
8
+ CONST_KEY,
6
9
  DEFAULT_KEY,
7
10
  DEPENDENCIES_KEY,
8
- PROPERTIES_KEY,
9
11
  ONE_OF_KEY,
12
+ PROPERTIES_KEY,
10
13
  REF_KEY,
11
- ALL_OF_KEY,
12
14
  } from '../constants';
13
15
  import findSchemaDefinition from '../findSchemaDefinition';
14
16
  import getClosestMatchingOption from './getClosestMatchingOption';
@@ -20,6 +22,7 @@ import mergeDefaultsWithFormData from '../mergeDefaultsWithFormData';
20
22
  import mergeObjects from '../mergeObjects';
21
23
  import mergeSchemas from '../mergeSchemas';
22
24
  import {
25
+ Experimental_CustomMergeAllOf,
23
26
  Experimental_DefaultFormStateBehavior,
24
27
  FormContextType,
25
28
  GenericObjectType,
@@ -28,7 +31,14 @@ import {
28
31
  ValidatorType,
29
32
  } from '../types';
30
33
  import isMultiSelect from './isMultiSelect';
34
+ import isSelect from './isSelect';
31
35
  import retrieveSchema, { resolveDependencies } from './retrieveSchema';
36
+ import isConstant from '../isConstant';
37
+ import constIsAjvDataReference from '../constIsAjvDataReference';
38
+ import optionsList from '../optionsList';
39
+ import deepEquals from '../deepEquals';
40
+
41
+ const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null'];
32
42
 
33
43
  /** Enum that indicates how `schema.additionalItems` should be handled by the `getInnerSchemaForArrayItem()` function.
34
44
  */
@@ -56,7 +66,7 @@ export enum AdditionalItemsHandling {
56
66
  export function getInnerSchemaForArrayItem<S extends StrictRJSFSchema = RJSFSchema>(
57
67
  schema: S,
58
68
  additionalItems: AdditionalItemsHandling = AdditionalItemsHandling.Ignore,
59
- idx = -1
69
+ idx = -1,
60
70
  ): S {
61
71
  if (idx >= 0) {
62
72
  if (Array.isArray(schema.items) && idx < schema.items.length) {
@@ -92,6 +102,7 @@ export function getInnerSchemaForArrayItem<S extends StrictRJSFSchema = RJSFSche
92
102
  * @param requiredFields - The list of fields that are required
93
103
  * @param experimental_defaultFormStateBehavior - Optional configuration object, if provided, allows users to override
94
104
  * default form state behavior
105
+ * @param isConst - Optional flag, if true, indicates that the schema has a const property defined, thus we should always return the computedDefault since it's coming from the const.
95
106
  */
96
107
  function maybeAddDefaultToObject<T = any>(
97
108
  obj: GenericObjectType,
@@ -100,24 +111,26 @@ function maybeAddDefaultToObject<T = any>(
100
111
  includeUndefinedValues: boolean | 'excludeObjectChildren',
101
112
  isParentRequired?: boolean,
102
113
  requiredFields: string[] = [],
103
- experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = {}
114
+ experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = {},
115
+ isConst = false,
104
116
  ) {
105
117
  const { emptyObjectFields = 'populateAllDefaults' } = experimental_defaultFormStateBehavior;
106
- if (includeUndefinedValues) {
118
+ if (includeUndefinedValues || isConst) {
119
+ // If includeUndefinedValues
120
+ // Or if the schema has a const property defined, then we should always return the computedDefault since it's coming from the const.
107
121
  obj[key] = computedDefault;
108
122
  } else if (emptyObjectFields !== 'skipDefaults') {
109
- if (isObject(computedDefault)) {
110
- // If isParentRequired is undefined, then we are at the root level of the schema so defer to the requiredness of
111
- // the field key itself in the `requiredField` list
112
- 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;
113
126
 
127
+ if (isObject(computedDefault)) {
114
128
  // If emptyObjectFields 'skipEmptyDefaults' store computedDefault if it's a non-empty object(e.g. not {})
115
129
  if (emptyObjectFields === 'skipEmptyDefaults') {
116
130
  if (!isEmpty(computedDefault)) {
117
131
  obj[key] = computedDefault;
118
132
  }
119
- }
120
- // 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
121
134
  // Condition 1: If computedDefault is not empty or if the key is a required field
122
135
  // Condition 2: If the parent object is required or emptyObjectFields is not 'populateRequiredDefaults'
123
136
  else if (
@@ -129,11 +142,12 @@ function maybeAddDefaultToObject<T = any>(
129
142
  } else if (
130
143
  // Store computedDefault if it's a defined primitive (e.g., true) and satisfies certain conditions
131
144
  // Condition 1: computedDefault is not undefined
132
- // Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults) or if the key is a required field
145
+ // Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults)
146
+ // Or if isSelfOrParentRequired is 'true' and the key is a required field
133
147
  computedDefault !== undefined &&
134
148
  (emptyObjectFields === 'populateAllDefaults' ||
135
149
  emptyObjectFields === 'skipEmptyDefaults' ||
136
- requiredFields.includes(key))
150
+ (isSelfOrParentRequired && requiredFields.includes(key)))
137
151
  ) {
138
152
  obj[key] = computedDefault;
139
153
  }
@@ -141,13 +155,29 @@ function maybeAddDefaultToObject<T = any>(
141
155
  }
142
156
 
143
157
  interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema> {
158
+ /** Any defaults provided by the parent field in the schema */
144
159
  parentDefaults?: T;
160
+ /** The options root schema, used to primarily to look up `$ref`s */
145
161
  rootSchema?: S;
162
+ /** The current formData, if any, onto which to provide any missing defaults */
146
163
  rawFormData?: T;
164
+ /** Optional flag, if true, cause undefined values to be added as defaults.
165
+ * If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
166
+ * false when computing defaults for any nested object properties.
167
+ */
147
168
  includeUndefinedValues?: boolean | 'excludeObjectChildren';
169
+ /** The list of ref names currently being recursed, used to prevent infinite recursion */
148
170
  _recurseList?: string[];
171
+ /** Optional configuration object, if provided, allows users to override default form state behavior */
149
172
  experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior;
173
+ /** Optional function that allows for custom merging of `allOf` schemas */
174
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>;
175
+ /** Optional flag, if true, indicates this schema was required in the parent schema. */
150
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;
151
181
  }
152
182
 
153
183
  /** Computes the defaults for the current `schema` given the `rawFormData` and `parentDefaults` if any. This drills into
@@ -155,44 +185,47 @@ interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema>
155
185
  *
156
186
  * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
157
187
  * @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.
188
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
168
189
  * @returns - The resulting `formData` with all the defaults provided
169
190
  */
170
191
  export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
171
192
  validator: ValidatorType<T, S, F>,
172
193
  rawSchema: S,
173
- {
194
+ computeDefaultsProps: ComputeDefaultsProps<T, S> = {},
195
+ ): T | T[] | undefined {
196
+ const {
174
197
  parentDefaults,
175
198
  rawFormData,
176
199
  rootSchema = {} as S,
177
200
  includeUndefinedValues = false,
178
201
  _recurseList = [],
179
202
  experimental_defaultFormStateBehavior = undefined,
203
+ experimental_customMergeAllOf = undefined,
180
204
  required,
181
- }: ComputeDefaultsProps<T, S> = {}
182
- ): T | T[] | undefined {
205
+ shouldMergeDefaultsIntoFormData = false,
206
+ } = computeDefaultsProps;
183
207
  const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
184
208
  const schema: S = isObject(rawSchema) ? rawSchema : ({} as S);
185
209
  // Compute the defaults recursively: give highest priority to deepest nodes.
186
210
  let defaults: T | T[] | undefined = parentDefaults;
187
211
  // If we get a new schema, then we need to recompute defaults again for the new schema found.
188
212
  let schemaToCompute: S | null = null;
213
+ let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior;
189
214
  let updatedRecurseList = _recurseList;
190
215
 
191
- if (isObject(defaults) && isObject(schema.default)) {
216
+ if (
217
+ schema[CONST_KEY] &&
218
+ experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' &&
219
+ !constIsAjvDataReference(schema)
220
+ ) {
221
+ defaults = schema[CONST_KEY] as unknown as T;
222
+ } else if (isObject(defaults) && isObject(schema.default)) {
192
223
  // For object defaults, only override parent defaults that are defined in
193
224
  // schema.default.
194
225
  defaults = mergeObjects(defaults!, schema.default as GenericObjectType) as T;
195
- } else if (DEFAULT_KEY in schema) {
226
+ } else if (DEFAULT_KEY in schema && !schema[ANY_OF_KEY] && !schema[ONE_OF_KEY]) {
227
+ // If the schema has a default value, then we should use it as the default.
228
+ // And if the schema does not have anyOf or oneOf, this is done because we need to merge the defaults with the formData.
196
229
  defaults = schema.default as unknown as T;
197
230
  } else if (REF_KEY in schema) {
198
231
  const refName = schema[REF_KEY];
@@ -202,7 +235,20 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
202
235
  schemaToCompute = findSchemaDefinition<S>(refName, rootSchema);
203
236
  }
204
237
  } else if (DEPENDENCIES_KEY in schema) {
205
- const resolvedSchema = resolveDependencies<T, S, F>(validator, schema, rootSchema, false, [], formData);
238
+ // Get the default if set from properties to ensure the dependencies conditions are resolved based on it
239
+ const defaultFormData: T = {
240
+ ...getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults),
241
+ ...formData,
242
+ };
243
+ const resolvedSchema = resolveDependencies<T, S, F>(
244
+ validator,
245
+ schema,
246
+ rootSchema,
247
+ false,
248
+ [],
249
+ defaultFormData,
250
+ experimental_customMergeAllOf,
251
+ );
206
252
  schemaToCompute = resolvedSchema[0]; // pick the first element from resolve dependencies
207
253
  } else if (isFixedItems(schema)) {
208
254
  defaults = (schema.items! as S[]).map((itemSchema: S, idx: number) =>
@@ -211,10 +257,12 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
211
257
  includeUndefinedValues,
212
258
  _recurseList,
213
259
  experimental_defaultFormStateBehavior,
260
+ experimental_customMergeAllOf,
214
261
  parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined,
215
262
  rawFormData: formData as T,
216
263
  required,
217
- })
264
+ shouldMergeDefaultsIntoFormData,
265
+ }),
218
266
  ) as T[];
219
267
  } else if (ONE_OF_KEY in schema) {
220
268
  const { oneOf, ...remaining } = schema;
@@ -222,14 +270,27 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
222
270
  return undefined;
223
271
  }
224
272
  const discriminator = getDiscriminatorFieldFromSchema<S>(schema);
273
+ const { type = 'null' } = remaining;
274
+ if (
275
+ !Array.isArray(type) &&
276
+ PRIMITIVE_TYPES.includes(type) &&
277
+ experimental_dfsb_to_compute?.constAsDefaults === 'skipOneOf'
278
+ ) {
279
+ // If we are in a oneOf of a primitive type, then we want to pass constAsDefaults as 'never' for the recursion
280
+ experimental_dfsb_to_compute = {
281
+ ...experimental_dfsb_to_compute,
282
+ constAsDefaults: 'never',
283
+ };
284
+ }
225
285
  schemaToCompute = oneOf![
226
286
  getClosestMatchingOption<T, S, F>(
227
287
  validator,
228
288
  rootSchema,
229
- isEmpty(formData) ? undefined : formData,
289
+ rawFormData ?? (schema.default as T),
230
290
  oneOf as S[],
231
291
  0,
232
- discriminator
292
+ discriminator,
293
+ experimental_customMergeAllOf,
233
294
  )
234
295
  ] as S;
235
296
  schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
@@ -243,10 +304,11 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
243
304
  getClosestMatchingOption<T, S, F>(
244
305
  validator,
245
306
  rootSchema,
246
- isEmpty(formData) ? undefined : formData,
307
+ rawFormData ?? (schema.default as T),
247
308
  anyOf as S[],
248
309
  0,
249
- discriminator
310
+ discriminator,
311
+ experimental_customMergeAllOf,
250
312
  )
251
313
  ] as S;
252
314
  schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
@@ -257,10 +319,12 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
257
319
  rootSchema,
258
320
  includeUndefinedValues,
259
321
  _recurseList: updatedRecurseList,
260
- experimental_defaultFormStateBehavior,
322
+ experimental_defaultFormStateBehavior: experimental_dfsb_to_compute,
323
+ experimental_customMergeAllOf,
261
324
  parentDefaults: defaults as T | undefined,
262
325
  rawFormData: formData as T,
263
326
  required,
327
+ shouldMergeDefaultsIntoFormData,
264
328
  });
265
329
  }
266
330
 
@@ -269,164 +333,343 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
269
333
  defaults = schema.default as unknown as T;
270
334
  }
271
335
 
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
- {}
336
+ const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults);
337
+
338
+ let defaultsWithFormData = defaultBasedOnSchemaType ?? defaults;
339
+ // if shouldMergeDefaultsIntoFormData is true, then merge the defaults into the formData.
340
+ if (shouldMergeDefaultsIntoFormData) {
341
+ const { arrayMinItems = {} } = experimental_defaultFormStateBehavior || {};
342
+ const { mergeExtraDefaults } = arrayMinItems;
343
+
344
+ const matchingFormData = ensureFormDataMatchingSchema(
345
+ validator,
346
+ schema,
347
+ rootSchema,
348
+ rawFormData,
349
+ experimental_defaultFormStateBehavior,
350
+ experimental_customMergeAllOf,
351
+ );
352
+ if (!isObject(rawFormData) || ALL_OF_KEY in schema) {
353
+ // If the formData is not an object which means it's a primitive field, then we need to merge the defaults into the formData.
354
+ // Or if the schema has allOf, we need to merge the defaults into the formData because we don't compute the defaults for allOf.
355
+ defaultsWithFormData = mergeDefaultsWithFormData<T>(
356
+ defaultsWithFormData as T,
357
+ matchingFormData as T,
358
+ mergeExtraDefaults,
359
+ true,
306
360
  ) 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
- : {};
312
-
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
- );
361
+ }
362
+ }
363
+
364
+ return defaultsWithFormData;
365
+ }
366
+
367
+ /**
368
+ * 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.
369
+ *
370
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
371
+ * @param schema - The schema for which the formData state is desired
372
+ * @param rootSchema - The root schema, used to primarily to look up `$ref`s
373
+ * @param formData - The current formData
374
+ * @param [experimental_defaultFormStateBehavior] - Optional configuration object, if provided, allows users to override default form state behavior
375
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
376
+ * @returns - valid formData that matches schema
377
+ */
378
+ export function ensureFormDataMatchingSchema<
379
+ T = any,
380
+ S extends StrictRJSFSchema = RJSFSchema,
381
+ F extends FormContextType = any,
382
+ >(
383
+ validator: ValidatorType<T, S, F>,
384
+ schema: S,
385
+ rootSchema: S,
386
+ formData: T | undefined,
387
+ experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior,
388
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
389
+ ): T | T[] | undefined {
390
+ const isSelectField =
391
+ !isConstant<S>(schema) && isSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf);
392
+ let validFormData: T | T[] | undefined = formData;
393
+ if (isSelectField) {
394
+ const getOptionsList = optionsList<T, S, F>(schema);
395
+ const isValid = getOptionsList?.some((option) => deepEquals(option.value, formData));
396
+ validFormData = isValid ? formData : undefined;
397
+ }
398
+
399
+ // Override the formData with the const if the constAsDefaults is set to always
400
+ const constTakesPrecedence = schema[CONST_KEY] && experimental_defaultFormStateBehavior?.constAsDefaults === 'always';
401
+ if (constTakesPrecedence) {
402
+ validFormData = schema.const as T;
403
+ }
404
+
405
+ return validFormData;
406
+ }
407
+
408
+ /** Computes the default value for objects.
409
+ *
410
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
411
+ * @param rawSchema - The schema for which the default state is desired
412
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
413
+ * @param defaults - Optional props for this function
414
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
415
+ */
416
+ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
417
+ validator: ValidatorType<T, S, F>,
418
+ rawSchema: S,
419
+ {
420
+ rawFormData,
421
+ rootSchema = {} as S,
422
+ includeUndefinedValues = false,
423
+ _recurseList = [],
424
+ experimental_defaultFormStateBehavior = undefined,
425
+ experimental_customMergeAllOf = undefined,
426
+ required,
427
+ shouldMergeDefaultsIntoFormData,
428
+ }: ComputeDefaultsProps<T, S> = {},
429
+ defaults?: T | T[] | undefined,
430
+ ): T {
431
+ {
432
+ const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
433
+ const schema: S = rawSchema;
434
+ // This is a custom addition that fixes this issue:
435
+ // https://github.com/rjsf-team/react-jsonschema-form/issues/3832
436
+ const retrievedSchema =
437
+ experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema
438
+ ? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf)
439
+ : schema;
440
+ const parentConst = retrievedSchema[CONST_KEY];
441
+ const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
442
+ (acc: GenericObjectType, key: string) => {
443
+ const propertySchema: S = get(retrievedSchema, [PROPERTIES_KEY, key], {}) as S;
444
+ // Check if the parent schema has a const property defined AND we are supporting const as defaults, then we
445
+ // should always return the computedDefault since it's coming from the const.
446
+ const hasParentConst = isObject(parentConst) && (parentConst as JSONSchema7Object)[key] !== undefined;
447
+ const hasConst =
448
+ ((isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst) &&
449
+ experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' &&
450
+ !constIsAjvDataReference(propertySchema);
451
+ // Compute the defaults for this node, with the parent defaults we might
452
+ // have from a previous run: defaults[key].
453
+ const computedDefault = computeDefaults<T, S, F>(validator, propertySchema, {
454
+ rootSchema,
455
+ _recurseList,
456
+ experimental_defaultFormStateBehavior,
457
+ experimental_customMergeAllOf,
458
+ includeUndefinedValues: includeUndefinedValues === true,
459
+ parentDefaults: get(defaults, [key]),
460
+ rawFormData: get(formData, [key]),
461
+ required: retrievedSchema.required?.includes(key),
462
+ shouldMergeDefaultsIntoFormData,
345
463
  });
464
+ maybeAddDefaultToObject<T>(
465
+ acc,
466
+ key,
467
+ computedDefault,
468
+ includeUndefinedValues,
469
+ required,
470
+ retrievedSchema.required,
471
+ experimental_defaultFormStateBehavior,
472
+ hasConst,
473
+ );
474
+ return acc;
475
+ },
476
+ {},
477
+ ) as T;
478
+ if (retrievedSchema.additionalProperties) {
479
+ // as per spec additionalProperties may be either schema or boolean
480
+ const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties)
481
+ ? retrievedSchema.additionalProperties
482
+ : {};
483
+
484
+ const keys = new Set<string>();
485
+ if (isObject(defaults)) {
486
+ Object.keys(defaults as GenericObjectType)
487
+ .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
488
+ .forEach((key) => keys.add(key));
346
489
  }
347
- return objectDefaults;
490
+ const formDataRequired: string[] = [];
491
+ Object.keys(formData as GenericObjectType)
492
+ .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
493
+ .forEach((key) => {
494
+ keys.add(key);
495
+ formDataRequired.push(key);
496
+ });
497
+ keys.forEach((key) => {
498
+ const computedDefault = computeDefaults(validator, additionalPropertiesSchema as S, {
499
+ rootSchema,
500
+ _recurseList,
501
+ experimental_defaultFormStateBehavior,
502
+ experimental_customMergeAllOf,
503
+ includeUndefinedValues: includeUndefinedValues === true,
504
+ parentDefaults: get(defaults, [key]),
505
+ rawFormData: get(formData, [key]),
506
+ required: retrievedSchema.required?.includes(key),
507
+ shouldMergeDefaultsIntoFormData,
508
+ });
509
+ // Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop
510
+ maybeAddDefaultToObject<T>(
511
+ objectDefaults as GenericObjectType,
512
+ key,
513
+ computedDefault,
514
+ includeUndefinedValues,
515
+ required,
516
+ formDataRequired,
517
+ );
518
+ });
348
519
  }
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);
355
-
356
- const emptyDefault = isSkipEmptyDefaults ? undefined : [];
357
-
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[];
370
- }
520
+ return objectDefaults;
521
+ }
522
+ }
371
523
 
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
- }
524
+ /** Computes the default value for arrays.
525
+ *
526
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
527
+ * @param rawSchema - The schema for which the default state is desired
528
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
529
+ * @param defaults - Optional props for this function
530
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
531
+ */
532
+ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
533
+ validator: ValidatorType<T, S, F>,
534
+ rawSchema: S,
535
+ {
536
+ rawFormData,
537
+ rootSchema = {} as S,
538
+ _recurseList = [],
539
+ experimental_defaultFormStateBehavior = undefined,
540
+ experimental_customMergeAllOf = undefined,
541
+ required,
542
+ shouldMergeDefaultsIntoFormData,
543
+ }: ComputeDefaultsProps<T, S> = {},
544
+ defaults?: T | T[] | undefined,
545
+ ): T | T[] | undefined {
546
+ const schema: S = rawSchema;
390
547
 
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
- }
548
+ const arrayMinItemsStateBehavior = experimental_defaultFormStateBehavior?.arrayMinItems ?? {};
549
+ const { populate: arrayMinItemsPopulate, mergeExtraDefaults: arrayMergeExtraDefaults } = arrayMinItemsStateBehavior;
399
550
 
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
- }
551
+ const neverPopulate = arrayMinItemsPopulate === 'never';
552
+ const ignoreMinItemsFlagSet = arrayMinItemsPopulate === 'requiredOnly';
553
+ const isPopulateAll = arrayMinItemsPopulate === 'all' || (!neverPopulate && !ignoreMinItemsFlagSet);
554
+ const computeSkipPopulate = arrayMinItemsStateBehavior?.computeSkipPopulate ?? (() => false);
555
+ const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults';
409
556
 
410
- const defaultEntries: T[] = (defaults || []) as T[];
411
- const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert);
412
- const fillerDefault = fillerSchema.default;
557
+ const emptyDefault = isSkipEmptyDefaults ? undefined : [];
413
558
 
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,
559
+ // Inject defaults into existing array defaults
560
+ if (Array.isArray(defaults)) {
561
+ defaults = defaults.map((item, idx) => {
562
+ const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx);
563
+ return computeDefaults<T, S, F>(validator, schemaItem, {
564
+ rootSchema,
565
+ _recurseList,
566
+ experimental_defaultFormStateBehavior,
567
+ experimental_customMergeAllOf,
568
+ parentDefaults: item,
569
+ required,
570
+ shouldMergeDefaultsIntoFormData,
571
+ });
572
+ }) as T[];
573
+ }
574
+
575
+ // Deeply inject defaults into already existing form data
576
+ if (Array.isArray(rawFormData)) {
577
+ const schemaItem: S = getInnerSchemaForArrayItem<S>(schema);
578
+ if (neverPopulate) {
579
+ defaults = rawFormData;
580
+ } else {
581
+ const itemDefaults = rawFormData.map((item: T, idx: number) => {
582
+ return computeDefaults<T, S, F>(validator, schemaItem, {
418
583
  rootSchema,
419
584
  _recurseList,
420
585
  experimental_defaultFormStateBehavior,
586
+ experimental_customMergeAllOf,
587
+ rawFormData: item,
588
+ parentDefaults: get(defaults, [idx]),
421
589
  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);
590
+ shouldMergeDefaultsIntoFormData,
591
+ });
592
+ }) as T[];
593
+
594
+ // If the populate 'requiredOnly' flag is set then we only merge and include extra defaults if they are required.
595
+ // Or if populate 'all' is set we merge and include extra defaults.
596
+ const mergeExtraDefaults = ((ignoreMinItemsFlagSet && required) || isPopulateAll) && arrayMergeExtraDefaults;
597
+ defaults = mergeDefaultsWithFormData(defaults, itemDefaults, mergeExtraDefaults);
426
598
  }
427
599
  }
428
600
 
429
- return defaults;
601
+ // Check if the schema has a const property defined AND we are supporting const as defaults, then we should always
602
+ // return the computedDefault since it's coming from the const.
603
+ const hasConst =
604
+ isObject(schema) && CONST_KEY in schema && experimental_defaultFormStateBehavior?.constAsDefaults !== 'never';
605
+ if (hasConst === false) {
606
+ if (neverPopulate) {
607
+ return defaults ?? emptyDefault;
608
+ }
609
+ if (ignoreMinItemsFlagSet && !required) {
610
+ // If no form data exists or defaults are set leave the field empty/non-existent, otherwise
611
+ // return form data/defaults
612
+ return defaults ? defaults : undefined;
613
+ }
614
+ }
615
+
616
+ const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
617
+ if (
618
+ !schema.minItems ||
619
+ isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
620
+ computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
621
+ schema.minItems <= defaultsLength
622
+ ) {
623
+ return defaults ? defaults : emptyDefault;
624
+ }
625
+
626
+ const defaultEntries: T[] = (defaults || []) as T[];
627
+ const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert);
628
+ const fillerDefault = fillerSchema.default;
629
+
630
+ // Calculate filler entries for remaining items (minItems - existing raw data/defaults)
631
+ const fillerEntries: T[] = new Array(schema.minItems - defaultsLength).fill(
632
+ computeDefaults<any, S, F>(validator, fillerSchema, {
633
+ parentDefaults: fillerDefault,
634
+ rootSchema,
635
+ _recurseList,
636
+ experimental_defaultFormStateBehavior,
637
+ experimental_customMergeAllOf,
638
+ required,
639
+ shouldMergeDefaultsIntoFormData,
640
+ }),
641
+ ) as T[];
642
+ // then fill up the rest with either the item default or empty, up to minItems
643
+ return defaultEntries.concat(fillerEntries);
644
+ }
645
+
646
+ /** Computes the default value based on the schema type.
647
+ *
648
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
649
+ * @param rawSchema - The schema for which the default state is desired
650
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
651
+ * @param defaults - Optional props for this function
652
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
653
+ */
654
+ export function getDefaultBasedOnSchemaType<
655
+ T = any,
656
+ S extends StrictRJSFSchema = RJSFSchema,
657
+ F extends FormContextType = any,
658
+ >(
659
+ validator: ValidatorType<T, S, F>,
660
+ rawSchema: S,
661
+ computeDefaultsProps: ComputeDefaultsProps<T, S> = {},
662
+ defaults?: T | T[] | undefined,
663
+ ): T | T[] | void {
664
+ switch (getSchemaType<S>(rawSchema)) {
665
+ // We need to recurse for object schema inner default values.
666
+ case 'object': {
667
+ return getObjectDefaults(validator, rawSchema, computeDefaultsProps, defaults);
668
+ }
669
+ case 'array': {
670
+ return getArrayDefaults(validator, rawSchema, computeDefaultsProps, defaults);
671
+ }
672
+ }
430
673
  }
431
674
 
432
675
  /** Returns the superset of `formData` that includes the given set updated to include any missing fields that have
@@ -440,40 +683,53 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
440
683
  * If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
441
684
  * false when computing defaults for any nested object properties.
442
685
  * @param [experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior
686
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
443
687
  * @returns - The resulting `formData` with all the defaults provided
444
688
  */
445
689
  export default function getDefaultFormState<
446
690
  T = any,
447
691
  S extends StrictRJSFSchema = RJSFSchema,
448
- F extends FormContextType = any
692
+ F extends FormContextType = any,
449
693
  >(
450
694
  validator: ValidatorType<T, S, F>,
451
695
  theSchema: S,
452
696
  formData?: T,
453
697
  rootSchema?: S,
454
698
  includeUndefinedValues: boolean | 'excludeObjectChildren' = false,
455
- experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior
699
+ experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior,
700
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
456
701
  ) {
457
702
  if (!isObject(theSchema)) {
458
703
  throw new Error('Invalid schema: ' + theSchema);
459
704
  }
460
- const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, formData);
705
+ const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, formData, experimental_customMergeAllOf);
706
+
707
+ // Get the computed defaults with 'shouldMergeDefaultsIntoFormData' set to true to merge defaults into formData.
708
+ // This is done when for example the value from formData does not exist in the schema 'enum' property, in such
709
+ // cases we take the value from the defaults because the value from the formData is not valid.
461
710
  const defaults = computeDefaults<T, S, F>(validator, schema, {
462
711
  rootSchema,
463
712
  includeUndefinedValues,
464
713
  experimental_defaultFormStateBehavior,
714
+ experimental_customMergeAllOf,
465
715
  rawFormData: formData,
716
+ shouldMergeDefaultsIntoFormData: true,
466
717
  });
467
- if (formData === undefined || formData === null || (typeof formData === 'number' && isNaN(formData))) {
468
- // No form data? Use schema defaults.
469
- return defaults;
470
- }
471
- const { mergeExtraDefaults } = experimental_defaultFormStateBehavior?.arrayMinItems || {};
472
- if (isObject(formData)) {
473
- return mergeDefaultsWithFormData<T>(defaults as T, formData, mergeExtraDefaults);
474
- }
475
- if (Array.isArray(formData)) {
476
- return mergeDefaultsWithFormData<T[]>(defaults as T[], formData, mergeExtraDefaults);
718
+
719
+ // If the formData is an object or an array, add additional properties from formData and override formData with
720
+ // defaults since the defaults are already merged with formData.
721
+ if (isObject(formData) || Array.isArray(formData)) {
722
+ const { mergeDefaultsIntoFormData } = experimental_defaultFormStateBehavior || {};
723
+ const defaultSupercedesUndefined = mergeDefaultsIntoFormData === 'useDefaultIfFormDataUndefined';
724
+ const result = mergeDefaultsWithFormData<T | T[]>(
725
+ defaults,
726
+ formData,
727
+ true, // set to true to add any additional default array entries.
728
+ defaultSupercedesUndefined,
729
+ true, // set to true to override formData with defaults if they exist.
730
+ );
731
+ return result;
477
732
  }
478
- return formData;
733
+
734
+ return defaults;
479
735
  }