@rjsf/utils 6.0.0-alpha.0 → 6.0.0-beta.10

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 +1347 -642
  2. package/dist/index.js.map +4 -4
  3. package/dist/utils.esm.js +1324 -619
  4. package/dist/utils.esm.js.map +4 -4
  5. package/dist/utils.umd.js +1266 -590
  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 +2 -2
  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 +15 -3
  20. package/lib/constants.js +15 -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 +5 -3
  54. package/lib/findSchemaDefinition.js +54 -11
  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 +316 -167
  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 +30 -12
  176. package/lib/schema/retrieveSchema.js +153 -70
  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 +172 -142
  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 +37 -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 +17 -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 +55 -10
  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 +461 -193
  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 +268 -78
  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 +278 -184
  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,46 @@ 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 {
183
- const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
205
+ shouldMergeDefaultsIntoFormData = false,
206
+ } = computeDefaultsProps;
207
+ let 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
-
191
- if (isObject(defaults) && isObject(schema.default)) {
215
+ if (
216
+ schema[CONST_KEY] &&
217
+ experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' &&
218
+ !constIsAjvDataReference(schema)
219
+ ) {
220
+ defaults = schema[CONST_KEY] as unknown as T;
221
+ } else if (isObject(defaults) && isObject(schema.default)) {
192
222
  // For object defaults, only override parent defaults that are defined in
193
223
  // schema.default.
194
224
  defaults = mergeObjects(defaults!, schema.default as GenericObjectType) as T;
195
- } else if (DEFAULT_KEY in schema) {
225
+ } else if (DEFAULT_KEY in schema && !schema[ANY_OF_KEY] && !schema[ONE_OF_KEY] && !schema[REF_KEY]) {
226
+ // If the schema has a default value, then we should use it as the default.
227
+ // And if the schema does not have anyOf or oneOf, this is done because we need to merge the defaults with the formData.
196
228
  defaults = schema.default as unknown as T;
197
229
  } else if (REF_KEY in schema) {
198
230
  const refName = schema[REF_KEY];
@@ -201,8 +233,34 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
201
233
  updatedRecurseList = _recurseList.concat(refName!);
202
234
  schemaToCompute = findSchemaDefinition<S>(refName, rootSchema);
203
235
  }
236
+
237
+ // If the referenced schema exists and parentDefaults is not set
238
+ // Then set the defaults from the current schema for the referenced schema
239
+ if (schemaToCompute && !defaults) {
240
+ defaults = schema.default as T | undefined;
241
+ }
242
+
243
+ // If shouldMergeDefaultsIntoFormData is true
244
+ // And the schemaToCompute is set and the rawFormData is not an object
245
+ // Then set the formData to the rawFormData
246
+ if (shouldMergeDefaultsIntoFormData && schemaToCompute && !isObject(rawFormData)) {
247
+ formData = rawFormData as T;
248
+ }
204
249
  } else if (DEPENDENCIES_KEY in schema) {
205
- const resolvedSchema = resolveDependencies<T, S, F>(validator, schema, rootSchema, false, [], formData);
250
+ // Get the default if set from properties to ensure the dependencies conditions are resolved based on it
251
+ const defaultFormData: T = {
252
+ ...getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults),
253
+ ...formData,
254
+ };
255
+ const resolvedSchema = resolveDependencies<T, S, F>(
256
+ validator,
257
+ schema,
258
+ rootSchema,
259
+ false,
260
+ [],
261
+ defaultFormData,
262
+ experimental_customMergeAllOf,
263
+ );
206
264
  schemaToCompute = resolvedSchema[0]; // pick the first element from resolve dependencies
207
265
  } else if (isFixedItems(schema)) {
208
266
  defaults = (schema.items! as S[]).map((itemSchema: S, idx: number) =>
@@ -211,10 +269,12 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
211
269
  includeUndefinedValues,
212
270
  _recurseList,
213
271
  experimental_defaultFormStateBehavior,
272
+ experimental_customMergeAllOf,
214
273
  parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined,
215
274
  rawFormData: formData as T,
216
275
  required,
217
- })
276
+ shouldMergeDefaultsIntoFormData,
277
+ }),
218
278
  ) as T[];
219
279
  } else if (ONE_OF_KEY in schema) {
220
280
  const { oneOf, ...remaining } = schema;
@@ -222,14 +282,27 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
222
282
  return undefined;
223
283
  }
224
284
  const discriminator = getDiscriminatorFieldFromSchema<S>(schema);
285
+ const { type = 'null' } = remaining;
286
+ if (
287
+ !Array.isArray(type) &&
288
+ PRIMITIVE_TYPES.includes(type) &&
289
+ experimental_dfsb_to_compute?.constAsDefaults === 'skipOneOf'
290
+ ) {
291
+ // If we are in a oneOf of a primitive type, then we want to pass constAsDefaults as 'never' for the recursion
292
+ experimental_dfsb_to_compute = {
293
+ ...experimental_dfsb_to_compute,
294
+ constAsDefaults: 'never',
295
+ };
296
+ }
225
297
  schemaToCompute = oneOf![
226
298
  getClosestMatchingOption<T, S, F>(
227
299
  validator,
228
300
  rootSchema,
229
- isEmpty(formData) ? undefined : formData,
301
+ rawFormData ?? (schema.default as T),
230
302
  oneOf as S[],
231
303
  0,
232
- discriminator
304
+ discriminator,
305
+ experimental_customMergeAllOf,
233
306
  )
234
307
  ] as S;
235
308
  schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
@@ -243,10 +316,11 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
243
316
  getClosestMatchingOption<T, S, F>(
244
317
  validator,
245
318
  rootSchema,
246
- isEmpty(formData) ? undefined : formData,
319
+ rawFormData ?? (schema.default as T),
247
320
  anyOf as S[],
248
321
  0,
249
- discriminator
322
+ discriminator,
323
+ experimental_customMergeAllOf,
250
324
  )
251
325
  ] as S;
252
326
  schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
@@ -257,10 +331,12 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
257
331
  rootSchema,
258
332
  includeUndefinedValues,
259
333
  _recurseList: updatedRecurseList,
260
- experimental_defaultFormStateBehavior,
334
+ experimental_defaultFormStateBehavior: experimental_dfsb_to_compute,
335
+ experimental_customMergeAllOf,
261
336
  parentDefaults: defaults as T | undefined,
262
337
  rawFormData: formData as T,
263
338
  required,
339
+ shouldMergeDefaultsIntoFormData,
264
340
  });
265
341
  }
266
342
 
@@ -269,164 +345,343 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
269
345
  defaults = schema.default as unknown as T;
270
346
  }
271
347
 
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
- {}
348
+ const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults);
349
+
350
+ let defaultsWithFormData = defaultBasedOnSchemaType ?? defaults;
351
+ // if shouldMergeDefaultsIntoFormData is true, then merge the defaults into the formData.
352
+ if (shouldMergeDefaultsIntoFormData) {
353
+ const { arrayMinItems = {} } = experimental_defaultFormStateBehavior || {};
354
+ const { mergeExtraDefaults } = arrayMinItems;
355
+
356
+ const matchingFormData = ensureFormDataMatchingSchema(
357
+ validator,
358
+ schema,
359
+ rootSchema,
360
+ rawFormData,
361
+ experimental_defaultFormStateBehavior,
362
+ experimental_customMergeAllOf,
363
+ );
364
+ if (!isObject(rawFormData) || ALL_OF_KEY in schema) {
365
+ // If the formData is not an object which means it's a primitive field, then we need to merge the defaults into the formData.
366
+ // Or if the schema has allOf, we need to merge the defaults into the formData because we don't compute the defaults for allOf.
367
+ defaultsWithFormData = mergeDefaultsWithFormData<T>(
368
+ defaultsWithFormData as T,
369
+ matchingFormData as T,
370
+ mergeExtraDefaults,
371
+ true,
306
372
  ) 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
- );
373
+ }
374
+ }
375
+
376
+ return defaultsWithFormData;
377
+ }
378
+
379
+ /**
380
+ * 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.
381
+ *
382
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
383
+ * @param schema - The schema for which the formData state is desired
384
+ * @param rootSchema - The root schema, used to primarily to look up `$ref`s
385
+ * @param formData - The current formData
386
+ * @param [experimental_defaultFormStateBehavior] - Optional configuration object, if provided, allows users to override default form state behavior
387
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
388
+ * @returns - valid formData that matches schema
389
+ */
390
+ export function ensureFormDataMatchingSchema<
391
+ T = any,
392
+ S extends StrictRJSFSchema = RJSFSchema,
393
+ F extends FormContextType = any,
394
+ >(
395
+ validator: ValidatorType<T, S, F>,
396
+ schema: S,
397
+ rootSchema: S,
398
+ formData: T | undefined,
399
+ experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior,
400
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
401
+ ): T | T[] | undefined {
402
+ const isSelectField =
403
+ !isConstant<S>(schema) && isSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf);
404
+ let validFormData: T | T[] | undefined = formData;
405
+ if (isSelectField) {
406
+ const getOptionsList = optionsList<T, S, F>(schema);
407
+ const isValid = getOptionsList?.some((option) => deepEquals(option.value, formData));
408
+ validFormData = isValid ? formData : undefined;
409
+ }
410
+
411
+ // Override the formData with the const if the constAsDefaults is set to always
412
+ const constTakesPrecedence = schema[CONST_KEY] && experimental_defaultFormStateBehavior?.constAsDefaults === 'always';
413
+ if (constTakesPrecedence) {
414
+ validFormData = schema.const as T;
415
+ }
416
+
417
+ return validFormData;
418
+ }
419
+
420
+ /** Computes the default value for objects.
421
+ *
422
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
423
+ * @param rawSchema - The schema for which the default state is desired
424
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
425
+ * @param defaults - Optional props for this function
426
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
427
+ */
428
+ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
429
+ validator: ValidatorType<T, S, F>,
430
+ rawSchema: S,
431
+ {
432
+ rawFormData,
433
+ rootSchema = {} as S,
434
+ includeUndefinedValues = false,
435
+ _recurseList = [],
436
+ experimental_defaultFormStateBehavior = undefined,
437
+ experimental_customMergeAllOf = undefined,
438
+ required,
439
+ shouldMergeDefaultsIntoFormData,
440
+ }: ComputeDefaultsProps<T, S> = {},
441
+ defaults?: T | T[] | undefined,
442
+ ): T {
443
+ {
444
+ const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
445
+ const schema: S = rawSchema;
446
+ // This is a custom addition that fixes this issue:
447
+ // https://github.com/rjsf-team/react-jsonschema-form/issues/3832
448
+ const retrievedSchema =
449
+ experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema
450
+ ? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf)
451
+ : schema;
452
+ const parentConst = retrievedSchema[CONST_KEY];
453
+ const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
454
+ (acc: GenericObjectType, key: string) => {
455
+ const propertySchema: S = get(retrievedSchema, [PROPERTIES_KEY, key], {}) as S;
456
+ // Check if the parent schema has a const property defined AND we are supporting const as defaults, then we
457
+ // should always return the computedDefault since it's coming from the const.
458
+ const hasParentConst = isObject(parentConst) && (parentConst as JSONSchema7Object)[key] !== undefined;
459
+ const hasConst =
460
+ ((isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst) &&
461
+ experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' &&
462
+ !constIsAjvDataReference(propertySchema);
463
+ // Compute the defaults for this node, with the parent defaults we might
464
+ // have from a previous run: defaults[key].
465
+ const computedDefault = computeDefaults<T, S, F>(validator, propertySchema, {
466
+ rootSchema,
467
+ _recurseList,
468
+ experimental_defaultFormStateBehavior,
469
+ experimental_customMergeAllOf,
470
+ includeUndefinedValues: includeUndefinedValues === true,
471
+ parentDefaults: get(defaults, [key]),
472
+ rawFormData: get(formData, [key]),
473
+ required: retrievedSchema.required?.includes(key),
474
+ shouldMergeDefaultsIntoFormData,
345
475
  });
476
+ maybeAddDefaultToObject<T>(
477
+ acc,
478
+ key,
479
+ computedDefault,
480
+ includeUndefinedValues,
481
+ required,
482
+ retrievedSchema.required,
483
+ experimental_defaultFormStateBehavior,
484
+ hasConst,
485
+ );
486
+ return acc;
487
+ },
488
+ {},
489
+ ) as T;
490
+ if (retrievedSchema.additionalProperties) {
491
+ // as per spec additionalProperties may be either schema or boolean
492
+ const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties)
493
+ ? retrievedSchema.additionalProperties
494
+ : {};
495
+
496
+ const keys = new Set<string>();
497
+ if (isObject(defaults)) {
498
+ Object.keys(defaults as GenericObjectType)
499
+ .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
500
+ .forEach((key) => keys.add(key));
346
501
  }
347
- return objectDefaults;
502
+ const formDataRequired: string[] = [];
503
+ Object.keys(formData as GenericObjectType)
504
+ .filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
505
+ .forEach((key) => {
506
+ keys.add(key);
507
+ formDataRequired.push(key);
508
+ });
509
+ keys.forEach((key) => {
510
+ const computedDefault = computeDefaults(validator, additionalPropertiesSchema as S, {
511
+ rootSchema,
512
+ _recurseList,
513
+ experimental_defaultFormStateBehavior,
514
+ experimental_customMergeAllOf,
515
+ includeUndefinedValues: includeUndefinedValues === true,
516
+ parentDefaults: get(defaults, [key]),
517
+ rawFormData: get(formData, [key]),
518
+ required: retrievedSchema.required?.includes(key),
519
+ shouldMergeDefaultsIntoFormData,
520
+ });
521
+ // Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop
522
+ maybeAddDefaultToObject<T>(
523
+ objectDefaults as GenericObjectType,
524
+ key,
525
+ computedDefault,
526
+ includeUndefinedValues,
527
+ required,
528
+ formDataRequired,
529
+ );
530
+ });
348
531
  }
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
- }
532
+ return objectDefaults;
533
+ }
534
+ }
371
535
 
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
- }
536
+ /** Computes the default value for arrays.
537
+ *
538
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
539
+ * @param rawSchema - The schema for which the default state is desired
540
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
541
+ * @param defaults - Optional props for this function
542
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
543
+ */
544
+ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
545
+ validator: ValidatorType<T, S, F>,
546
+ rawSchema: S,
547
+ {
548
+ rawFormData,
549
+ rootSchema = {} as S,
550
+ _recurseList = [],
551
+ experimental_defaultFormStateBehavior = undefined,
552
+ experimental_customMergeAllOf = undefined,
553
+ required,
554
+ shouldMergeDefaultsIntoFormData,
555
+ }: ComputeDefaultsProps<T, S> = {},
556
+ defaults?: T | T[] | undefined,
557
+ ): T | T[] | undefined {
558
+ const schema: S = rawSchema;
390
559
 
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
- }
560
+ const arrayMinItemsStateBehavior = experimental_defaultFormStateBehavior?.arrayMinItems ?? {};
561
+ const { populate: arrayMinItemsPopulate, mergeExtraDefaults: arrayMergeExtraDefaults } = arrayMinItemsStateBehavior;
399
562
 
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
- }
563
+ const neverPopulate = arrayMinItemsPopulate === 'never';
564
+ const ignoreMinItemsFlagSet = arrayMinItemsPopulate === 'requiredOnly';
565
+ const isPopulateAll = arrayMinItemsPopulate === 'all' || (!neverPopulate && !ignoreMinItemsFlagSet);
566
+ const computeSkipPopulate = arrayMinItemsStateBehavior?.computeSkipPopulate ?? (() => false);
567
+ const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults';
409
568
 
410
- const defaultEntries: T[] = (defaults || []) as T[];
411
- const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert);
412
- const fillerDefault = fillerSchema.default;
569
+ const emptyDefault = isSkipEmptyDefaults ? undefined : [];
413
570
 
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,
571
+ // Inject defaults into existing array defaults
572
+ if (Array.isArray(defaults)) {
573
+ defaults = defaults.map((item, idx) => {
574
+ const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx);
575
+ return computeDefaults<T, S, F>(validator, schemaItem, {
576
+ rootSchema,
577
+ _recurseList,
578
+ experimental_defaultFormStateBehavior,
579
+ experimental_customMergeAllOf,
580
+ parentDefaults: item,
581
+ required,
582
+ shouldMergeDefaultsIntoFormData,
583
+ });
584
+ }) as T[];
585
+ }
586
+
587
+ // Deeply inject defaults into already existing form data
588
+ if (Array.isArray(rawFormData)) {
589
+ const schemaItem: S = getInnerSchemaForArrayItem<S>(schema);
590
+ if (neverPopulate) {
591
+ defaults = rawFormData;
592
+ } else {
593
+ const itemDefaults = rawFormData.map((item: T, idx: number) => {
594
+ return computeDefaults<T, S, F>(validator, schemaItem, {
418
595
  rootSchema,
419
596
  _recurseList,
420
597
  experimental_defaultFormStateBehavior,
598
+ experimental_customMergeAllOf,
599
+ rawFormData: item,
600
+ parentDefaults: get(defaults, [idx]),
421
601
  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);
602
+ shouldMergeDefaultsIntoFormData,
603
+ });
604
+ }) as T[];
605
+
606
+ // If the populate 'requiredOnly' flag is set then we only merge and include extra defaults if they are required.
607
+ // Or if populate 'all' is set we merge and include extra defaults.
608
+ const mergeExtraDefaults = ((ignoreMinItemsFlagSet && required) || isPopulateAll) && arrayMergeExtraDefaults;
609
+ defaults = mergeDefaultsWithFormData(defaults, itemDefaults, mergeExtraDefaults);
426
610
  }
427
611
  }
428
612
 
429
- return defaults;
613
+ // Check if the schema has a const property defined AND we are supporting const as defaults, then we should always
614
+ // return the computedDefault since it's coming from the const.
615
+ const hasConst =
616
+ isObject(schema) && CONST_KEY in schema && experimental_defaultFormStateBehavior?.constAsDefaults !== 'never';
617
+ if (hasConst === false) {
618
+ if (neverPopulate) {
619
+ return defaults ?? emptyDefault;
620
+ }
621
+ if (ignoreMinItemsFlagSet && !required) {
622
+ // If no form data exists or defaults are set leave the field empty/non-existent, otherwise
623
+ // return form data/defaults
624
+ return defaults ? defaults : undefined;
625
+ }
626
+ }
627
+
628
+ const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
629
+ if (
630
+ !schema.minItems ||
631
+ isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
632
+ computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
633
+ schema.minItems <= defaultsLength
634
+ ) {
635
+ return defaults ? defaults : emptyDefault;
636
+ }
637
+
638
+ const defaultEntries: T[] = (defaults || []) as T[];
639
+ const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert);
640
+ const fillerDefault = fillerSchema.default;
641
+
642
+ // Calculate filler entries for remaining items (minItems - existing raw data/defaults)
643
+ const fillerEntries: T[] = new Array(schema.minItems - defaultsLength).fill(
644
+ computeDefaults<any, S, F>(validator, fillerSchema, {
645
+ parentDefaults: fillerDefault,
646
+ rootSchema,
647
+ _recurseList,
648
+ experimental_defaultFormStateBehavior,
649
+ experimental_customMergeAllOf,
650
+ required,
651
+ shouldMergeDefaultsIntoFormData,
652
+ }),
653
+ ) as T[];
654
+ // then fill up the rest with either the item default or empty, up to minItems
655
+ return defaultEntries.concat(fillerEntries);
656
+ }
657
+
658
+ /** Computes the default value based on the schema type.
659
+ *
660
+ * @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
661
+ * @param rawSchema - The schema for which the default state is desired
662
+ * @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
663
+ * @param defaults - Optional props for this function
664
+ * @returns - The default value based on the schema type if they are defined for object or array schemas.
665
+ */
666
+ export function getDefaultBasedOnSchemaType<
667
+ T = any,
668
+ S extends StrictRJSFSchema = RJSFSchema,
669
+ F extends FormContextType = any,
670
+ >(
671
+ validator: ValidatorType<T, S, F>,
672
+ rawSchema: S,
673
+ computeDefaultsProps: ComputeDefaultsProps<T, S> = {},
674
+ defaults?: T | T[] | undefined,
675
+ ): T | T[] | void {
676
+ switch (getSchemaType<S>(rawSchema)) {
677
+ // We need to recurse for object schema inner default values.
678
+ case 'object': {
679
+ return getObjectDefaults(validator, rawSchema, computeDefaultsProps, defaults);
680
+ }
681
+ case 'array': {
682
+ return getArrayDefaults(validator, rawSchema, computeDefaultsProps, defaults);
683
+ }
684
+ }
430
685
  }
431
686
 
432
687
  /** Returns the superset of `formData` that includes the given set updated to include any missing fields that have
@@ -440,40 +695,53 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
440
695
  * If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
441
696
  * false when computing defaults for any nested object properties.
442
697
  * @param [experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior
698
+ * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
443
699
  * @returns - The resulting `formData` with all the defaults provided
444
700
  */
445
701
  export default function getDefaultFormState<
446
702
  T = any,
447
703
  S extends StrictRJSFSchema = RJSFSchema,
448
- F extends FormContextType = any
704
+ F extends FormContextType = any,
449
705
  >(
450
706
  validator: ValidatorType<T, S, F>,
451
707
  theSchema: S,
452
708
  formData?: T,
453
709
  rootSchema?: S,
454
710
  includeUndefinedValues: boolean | 'excludeObjectChildren' = false,
455
- experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior
711
+ experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior,
712
+ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
456
713
  ) {
457
714
  if (!isObject(theSchema)) {
458
715
  throw new Error('Invalid schema: ' + theSchema);
459
716
  }
460
- const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, formData);
717
+ const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, formData, experimental_customMergeAllOf);
718
+
719
+ // Get the computed defaults with 'shouldMergeDefaultsIntoFormData' set to true to merge defaults into formData.
720
+ // This is done when for example the value from formData does not exist in the schema 'enum' property, in such
721
+ // cases we take the value from the defaults because the value from the formData is not valid.
461
722
  const defaults = computeDefaults<T, S, F>(validator, schema, {
462
723
  rootSchema,
463
724
  includeUndefinedValues,
464
725
  experimental_defaultFormStateBehavior,
726
+ experimental_customMergeAllOf,
465
727
  rawFormData: formData,
728
+ shouldMergeDefaultsIntoFormData: true,
466
729
  });
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);
730
+
731
+ // If the formData is an object or an array, add additional properties from formData and override formData with
732
+ // defaults since the defaults are already merged with formData.
733
+ if (isObject(formData) || Array.isArray(formData)) {
734
+ const { mergeDefaultsIntoFormData } = experimental_defaultFormStateBehavior || {};
735
+ const defaultSupercedesUndefined = mergeDefaultsIntoFormData === 'useDefaultIfFormDataUndefined';
736
+ const result = mergeDefaultsWithFormData<T | T[]>(
737
+ defaults,
738
+ formData,
739
+ true, // set to true to add any additional default array entries.
740
+ defaultSupercedesUndefined,
741
+ true, // set to true to override formData with defaults if they exist.
742
+ );
743
+ return result;
477
744
  }
478
- return formData;
745
+
746
+ return defaults;
479
747
  }