@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,7 +1,6 @@
1
- import isEqual from 'lodash/isEqual';
2
-
3
1
  import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
4
2
  import enumOptionsValueForIndex from './enumOptionsValueForIndex';
3
+ import deepEquals from './deepEquals';
5
4
 
6
5
  /** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is
7
6
  * a list, then that list is updated to remove the enum option value with the `valueIndex` in `allEnumOptions`. If it is
@@ -18,11 +17,11 @@ import enumOptionsValueForIndex from './enumOptionsValueForIndex';
18
17
  export default function enumOptionsDeselectValue<S extends StrictRJSFSchema = RJSFSchema>(
19
18
  valueIndex: string | number,
20
19
  selected?: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][],
21
- allEnumOptions: EnumOptionsType<S>[] = []
20
+ allEnumOptions: EnumOptionsType<S>[] = [],
22
21
  ): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined {
23
22
  const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions);
24
23
  if (Array.isArray(selected)) {
25
- return selected.filter((v) => !isEqual(v, value));
24
+ return selected.filter((v) => !deepEquals(v, value));
26
25
  }
27
- return isEqual(value, selected) ? undefined : selected;
26
+ return deepEquals(value, selected) ? undefined : selected;
28
27
  }
@@ -15,7 +15,7 @@ import enumOptionsIsSelected from './enumOptionsIsSelected';
15
15
  export default function enumOptionsIndexForValue<S extends StrictRJSFSchema = RJSFSchema>(
16
16
  value: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][],
17
17
  allEnumOptions: EnumOptionsType<S>[] = [],
18
- multiple = false
18
+ multiple = false,
19
19
  ): string | string[] | undefined {
20
20
  const selectedIndexes: string[] = allEnumOptions
21
21
  .map((opt, index) => (enumOptionsIsSelected(opt.value, value) ? String(index) : undefined))
@@ -1,6 +1,5 @@
1
- import isEqual from 'lodash/isEqual';
2
-
3
1
  import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
2
+ import deepEquals from './deepEquals';
4
3
 
5
4
  /** Determines whether the given `value` is (one of) the `selected` value(s).
6
5
  *
@@ -10,10 +9,10 @@ import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
10
9
  */
11
10
  export default function enumOptionsIsSelected<S extends StrictRJSFSchema = RJSFSchema>(
12
11
  value: EnumOptionsType<S>['value'],
13
- selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][]
12
+ selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][],
14
13
  ) {
15
14
  if (Array.isArray(selected)) {
16
- return selected.some((sel) => isEqual(sel, value));
15
+ return selected.some((sel) => deepEquals(sel, value));
17
16
  }
18
- return isEqual(selected, value);
17
+ return deepEquals(selected, value);
19
18
  }
@@ -13,7 +13,7 @@ import isNil from 'lodash/isNil';
13
13
  export default function enumOptionsSelectValue<S extends StrictRJSFSchema = RJSFSchema>(
14
14
  valueIndex: string | number,
15
15
  selected: EnumOptionsType<S>['value'][],
16
- allEnumOptions: EnumOptionsType<S>[] = []
16
+ allEnumOptions: EnumOptionsType<S>[] = [],
17
17
  ) {
18
18
  const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions);
19
19
  if (!isNil(value)) {
@@ -14,7 +14,7 @@ import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
14
14
  export default function enumOptionsValueForIndex<S extends StrictRJSFSchema = RJSFSchema>(
15
15
  valueIndex: string | number | Array<string | number>,
16
16
  allEnumOptions: EnumOptionsType<S>[] = [],
17
- emptyValue?: EnumOptionsType<S>['value']
17
+ emptyValue?: EnumOptionsType<S>['value'],
18
18
  ): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined {
19
19
  if (Array.isArray(valueIndex)) {
20
20
  return (
package/src/enums.ts CHANGED
@@ -9,6 +9,8 @@ export enum TranslatableString {
9
9
  ArrayItemTitle = 'Item',
10
10
  /** Missing items reason, used by ArrayField */
11
11
  MissingItems = 'Missing items definition',
12
+ /** Empty array message, used by ArrayField */
13
+ EmptyArray = 'No items yet. Use the button below to add some.',
12
14
  /** Yes label, used by BooleanField */
13
15
  YesLabel = 'Yes',
14
16
  /** No label, used by BooleanField */
@@ -32,7 +32,7 @@ export function splitKeyElementFromObject(key: string, object: GenericObjectType
32
32
  export function findSchemaDefinitionRecursive<S extends StrictRJSFSchema = RJSFSchema>(
33
33
  $ref?: string,
34
34
  rootSchema: S = {} as S,
35
- recurseList: string[] = []
35
+ recurseList: string[] = [],
36
36
  ): S {
37
37
  const ref = $ref || '';
38
38
  let decodedRef;
@@ -79,7 +79,7 @@ export function findSchemaDefinitionRecursive<S extends StrictRJSFSchema = RJSFS
79
79
  */
80
80
  export default function findSchemaDefinition<S extends StrictRJSFSchema = RJSFSchema>(
81
81
  $ref?: string,
82
- rootSchema: S = {} as S
82
+ rootSchema: S = {} as S,
83
83
  ): S {
84
84
  const recurseList: string[] = [];
85
85
  return findSchemaDefinitionRecursive($ref, rootSchema, recurseList);
@@ -0,0 +1,40 @@
1
+ import keys from 'lodash/keys';
2
+ import pickBy from 'lodash/pickBy';
3
+ import isPlainObject from 'lodash/isPlainObject';
4
+ import get from 'lodash/get';
5
+ import difference from 'lodash/difference';
6
+ import deepEquals from './deepEquals';
7
+
8
+ /**
9
+ * Compares two objects and returns the names of the fields that have changed.
10
+ * This function iterates over each field of object `a`, using `_.isEqual` to compare the field value
11
+ * with the corresponding field value in object `b`. If the values are different, the field name will
12
+ * be included in the returned array.
13
+ *
14
+ * @param a - The first object, representing the original data to compare.
15
+ * @param b - The second object, representing the updated data to compare.
16
+ * @returns - An array of field names that have changed.
17
+ *
18
+ * @example
19
+ * const a = { name: 'John', age: 30 };
20
+ * const b = { name: 'John', age: 31 };
21
+ * const changedFields = getChangedFields(a, b);
22
+ * console.log(changedFields); // Output: ['age']
23
+ */
24
+ export default function getChangedFields(a: unknown, b: unknown): string[] {
25
+ const aIsPlainObject = isPlainObject(a);
26
+ const bIsPlainObject = isPlainObject(b);
27
+ // If strictly equal or neither of them is a plainObject returns an empty array
28
+ if (a === b || (!aIsPlainObject && !bIsPlainObject)) {
29
+ return [];
30
+ }
31
+ if (aIsPlainObject && !bIsPlainObject) {
32
+ return keys(a);
33
+ } else if (!aIsPlainObject && bIsPlainObject) {
34
+ return keys(b);
35
+ } else {
36
+ const unequalFields = keys(pickBy(a as object, (value, key) => !deepEquals(value, get(b, key))));
37
+ const diffFields = difference(keys(b), keys(a));
38
+ return [...unequalFields, ...diffFields];
39
+ }
40
+ }
@@ -23,7 +23,7 @@ export default function getDateElementProps(
23
23
  date: DateObject,
24
24
  time: boolean,
25
25
  yearRange: [number, number] = [1900, new Date().getFullYear() + 2],
26
- format: DateElementFormat = 'YMD'
26
+ format: DateElementFormat = 'YMD',
27
27
  ) {
28
28
  const { day, month, year, hour, minute, second } = date;
29
29
 
@@ -48,7 +48,7 @@ export default function getDateElementProps(
48
48
  dateElementProp.push(
49
49
  { type: 'hour', range: [0, 23], value: hour },
50
50
  { type: 'minute', range: [0, 59], value: minute },
51
- { type: 'second', range: [0, 59], value: second }
51
+ { type: 'second', range: [0, 59], value: second },
52
52
  );
53
53
  }
54
54
 
@@ -2,6 +2,7 @@ import get from 'lodash/get';
2
2
  import isString from 'lodash/isString';
3
3
 
4
4
  import { RJSFSchema, StrictRJSFSchema } from './types';
5
+ import { DISCRIMINATOR_PATH } from './constants';
5
6
 
6
7
  /** Returns the `discriminator.propertyName` when defined in the `schema` if it is a string. A warning is generated when
7
8
  * it is not a string. Returns `undefined` when a valid discriminator is not present.
@@ -11,7 +12,7 @@ import { RJSFSchema, StrictRJSFSchema } from './types';
11
12
  */
12
13
  export default function getDiscriminatorFieldFromSchema<S extends StrictRJSFSchema = RJSFSchema>(schema: S) {
13
14
  let discriminator: string | undefined;
14
- const maybeString = get(schema, 'discriminator.propertyName', undefined);
15
+ const maybeString = get(schema, DISCRIMINATOR_PATH);
15
16
  if (isString(maybeString)) {
16
17
  discriminator = maybeString;
17
18
  } else if (maybeString !== undefined) {
@@ -12,12 +12,12 @@ import { FormContextType, InputPropsType, RJSFSchema, StrictRJSFSchema, UIOption
12
12
  export default function getInputProps<
13
13
  T = any,
14
14
  S extends StrictRJSFSchema = RJSFSchema,
15
- F extends FormContextType = any
15
+ F extends FormContextType = any,
16
16
  >(
17
17
  schema: RJSFSchema,
18
18
  defaultType?: string,
19
19
  options: UIOptionsType<T, S, F> = {},
20
- autoDefaultStepAny = true
20
+ autoDefaultStepAny = true,
21
21
  ): InputPropsType {
22
22
  const inputProps: InputPropsType = {
23
23
  type: defaultType || 'text',
@@ -51,5 +51,9 @@ export default function getInputProps<
51
51
  inputProps.autoComplete = options.autocomplete;
52
52
  }
53
53
 
54
+ if (options.accept) {
55
+ inputProps.accept = options.accept as string;
56
+ }
57
+
54
58
  return inputProps;
55
59
  }
@@ -15,7 +15,7 @@ import { RJSFSchema, StrictRJSFSchema } from './types';
15
15
  export default function getOptionMatchingSimpleDiscriminator<T = any, S extends StrictRJSFSchema = RJSFSchema>(
16
16
  formData: T | undefined,
17
17
  options: S[],
18
- discriminatorField?: string
18
+ discriminatorField?: string,
19
19
  ): number | undefined {
20
20
  if (formData && discriminatorField) {
21
21
  const value = get(formData, discriminatorField);
@@ -26,7 +26,7 @@ export default function getOptionMatchingSimpleDiscriminator<T = any, S extends
26
26
 
27
27
  for (let i = 0; i < options.length; i++) {
28
28
  const option = options[i];
29
- const discriminator = get(option, [PROPERTIES_KEY, discriminatorField], {});
29
+ const discriminator: S = get(option, [PROPERTIES_KEY, discriminatorField], {}) as S;
30
30
 
31
31
  if (discriminator.type === 'object' || discriminator.type === 'array') {
32
32
  continue;
@@ -7,13 +7,14 @@ import { RJSFSchema, StrictRJSFSchema } from './types';
7
7
  * - schema.enum: Returns `string`
8
8
  * - schema.properties: Returns `object`
9
9
  * - schema.additionalProperties: Returns `object`
10
+ * - schema.patternProperties: Returns `object`
10
11
  * - type is an array with a length of 2 and one type is 'null': Returns the other type
11
12
  *
12
13
  * @param schema - The schema for which to get the type
13
14
  * @returns - The type of the schema
14
15
  */
15
16
  export default function getSchemaType<S extends StrictRJSFSchema = RJSFSchema>(
16
- schema: S
17
+ schema: S,
17
18
  ): string | string[] | undefined {
18
19
  let { type } = schema;
19
20
 
@@ -25,7 +26,7 @@ export default function getSchemaType<S extends StrictRJSFSchema = RJSFSchema>(
25
26
  return 'string';
26
27
  }
27
28
 
28
- if (!type && (schema.properties || schema.additionalProperties)) {
29
+ if (!type && (schema.properties || schema.additionalProperties || schema.patternProperties)) {
29
30
  return 'object';
30
31
  }
31
32
 
@@ -20,7 +20,7 @@ export const DEFAULT_OPTIONS: UISchemaSubmitButtonOptions = {
20
20
  export default function getSubmitButtonOptions<
21
21
  T = any,
22
22
  S extends StrictRJSFSchema = RJSFSchema,
23
- F extends FormContextType = any
23
+ F extends FormContextType = any,
24
24
  >(uiSchema: UiSchema<T, S, F> = {}): UISchemaSubmitButtonOptions {
25
25
  const uiOptions = getUiOptions<T, S, F>(uiSchema);
26
26
  if (uiOptions && uiOptions[SUBMIT_BTN_OPTIONS_KEY]) {
@@ -12,12 +12,23 @@ export default function getTemplate<
12
12
  Name extends keyof TemplatesType<T, S, F>,
13
13
  T = any,
14
14
  S extends StrictRJSFSchema = RJSFSchema,
15
- F extends FormContextType = any
15
+ F extends FormContextType = any,
16
16
  >(name: Name, registry: Registry<T, S, F>, uiOptions: UIOptionsType<T, S, F> = {}): TemplatesType<T, S, F>[Name] {
17
17
  const { templates } = registry;
18
18
  if (name === 'ButtonTemplates') {
19
19
  return templates[name];
20
20
  }
21
+ // Allow templates to be customized per-field by using string keys from the registry
22
+ if (
23
+ Object.hasOwn(uiOptions, name) &&
24
+ typeof uiOptions[name] === 'string' &&
25
+ Object.hasOwn(templates, uiOptions[name] as string)
26
+ ) {
27
+ const key = uiOptions[name];
28
+ // Evaluating templates[key] results in TS2590: Expression produces a union type that is too complex to represent
29
+ // To avoid that, we cast templates to `any` before accessing the key field
30
+ return (templates as any)[key];
31
+ }
21
32
  return (
22
33
  // Evaluating uiOptions[name] results in TS2590: Expression produces a union type that is too complex to represent
23
34
  // To avoid that, we cast uiOptions to `any` before accessing the name field
@@ -0,0 +1,40 @@
1
+ import { nanoid } from 'nanoid';
2
+ import get from 'lodash/get';
3
+
4
+ import { TestIdShape } from './types';
5
+
6
+ /** Returns an object of test IDs that can only be used in test mode. If the function is called in a test environment
7
+ * (`NODE_ENV === 'test'`, this is set by jest) then a Proxy object will be returned. If a key within the returned
8
+ * object is accessed, if the value already exists the object will return that value, otherwise it will create that key
9
+ * with a generated `uuid` value and return the generated ID. If it is called outside of a test environment, the
10
+ * function will return an empty object, therefore returning `undefined` for any property within the object and
11
+ * excluding the prop from the rendered output of the component in which it is used.
12
+ * This implementation was adapted from the following blog post: https://www.matthewsessions.com/blog/react-test-id/
13
+ * To use this helper, you will want to generate a separate object for each component to avoid potential overlapping of
14
+ * ID names. You will also want to export the object for use in tests, because the keys will be generated in the
15
+ * component file, and used in the test file. Within the component file, add:
16
+ * `export const TEST_IDS = getTestIds();`
17
+ * Then pass `TEST_IDS.examplePropertyName` as the value of the test ID attribute of the intended component. This will
18
+ * allow you to use `TEST_IDS.examplePropertyName` within your tests, while keeping the test IDs out of your rendered
19
+ * output.
20
+ */
21
+ export default function getTestIds(): TestIdShape {
22
+ // For some reason, even though process.env contains the value of `test` for NODE_ENV, accessing it directly returns
23
+ // 'development'. Using `get()` does, in fact, return test so sticking with it
24
+ if (typeof process === 'undefined' || get(process, 'env.NODE_ENV') !== 'test') {
25
+ return {};
26
+ }
27
+
28
+ const ids = new Map();
29
+ return new Proxy(
30
+ {},
31
+ {
32
+ get(_obj, prop) {
33
+ if (!ids.has(prop)) {
34
+ ids.set(prop, nanoid());
35
+ }
36
+ return ids.get(prop);
37
+ },
38
+ },
39
+ );
40
+ }
@@ -11,7 +11,7 @@ import { FormContextType, GlobalUISchemaOptions, RJSFSchema, StrictRJSFSchema, U
11
11
  */
12
12
  export default function getUiOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
13
13
  uiSchema: UiSchema<T, S, F> = {},
14
- globalOptions: GlobalUISchemaOptions = {}
14
+ globalOptions: GlobalUISchemaOptions = {},
15
15
  ): UIOptionsType<T, S, F> {
16
16
  return Object.keys(uiSchema)
17
17
  .filter((key) => key.indexOf('ui:') === 0)
@@ -27,6 +27,6 @@ export default function getUiOptions<T = any, S extends StrictRJSFSchema = RJSFS
27
27
  }
28
28
  return { ...options, [key.substring(3)]: value };
29
29
  },
30
- { ...globalOptions }
30
+ { ...globalOptions },
31
31
  );
32
32
  }
package/src/getWidget.tsx CHANGED
@@ -69,7 +69,7 @@ const widgetMap: { [k: string]: { [j: string]: string } } = {
69
69
  * @returns - The wrapper widget
70
70
  */
71
71
  function mergeWidgetOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
72
- AWidget: Widget<T, S, F>
72
+ AWidget: Widget<T, S, F>,
73
73
  ) {
74
74
  let MergedWidget: Widget<T, S, F> | undefined = get(AWidget, 'MergedWidget');
75
75
  // cache return value as property of widget for proper react reconciliation
@@ -97,7 +97,7 @@ function mergeWidgetOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F
97
97
  export default function getWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
98
98
  schema: RJSFSchema,
99
99
  widget?: Widget<T, S, F> | string,
100
- registeredWidgets: RegistryWidgetsType<T, S, F> = {}
100
+ registeredWidgets: RegistryWidgetsType<T, S, F> = {},
101
101
  ): Widget<T, S, F> {
102
102
  const type = getSchemaType(schema);
103
103
 
package/src/hasWidget.ts CHANGED
@@ -12,7 +12,7 @@ import { FormContextType, RegistryWidgetsType, RJSFSchema, StrictRJSFSchema, Wid
12
12
  export default function hasWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
13
13
  schema: RJSFSchema,
14
14
  widget: Widget<T, S, F> | string,
15
- registeredWidgets: RegistryWidgetsType<T, S, F> = {}
15
+ registeredWidgets: RegistryWidgetsType<T, S, F> = {},
16
16
  ) {
17
17
  try {
18
18
  getWidget(schema, widget, registeredWidgets);
@@ -1,13 +1,14 @@
1
1
  import { RJSFSchema, StrictRJSFSchema } from './types';
2
2
 
3
- /** JS has no built-in hashing function, so rolling our own
3
+ /** Hashes a string using the algorithm based on Java's hashing function.
4
+ * JS has no built-in hashing function, so rolling our own
4
5
  * based on Java's hashing fn:
5
6
  * http://www.java2s.com/example/nodejs-utility-method/string-hash/hashcode-4dc2b.html
6
7
  *
7
8
  * @param string - The string for which to get the hash
8
9
  * @returns - The resulting hash of the string in hex format
9
10
  */
10
- function hashString(string: string): string {
11
+ export function hashString(string: string): string {
11
12
  let hash = 0;
12
13
  for (let i = 0; i < string.length; i += 1) {
13
14
  const chr = string.charCodeAt(i);
@@ -17,6 +18,28 @@ function hashString(string: string): string {
17
18
  return hash.toString(16);
18
19
  }
19
20
 
21
+ /** Stringifies an `object`, sorts object fields in consistent order before stringifying it.
22
+ *
23
+ * @param object - The object for which the sorted stringify is desired
24
+ * @returns - The stringified object with keys sorted in a consistent order
25
+ */
26
+ export function sortedJSONStringify(object: unknown): string {
27
+ const allKeys = new Set<string>();
28
+ // solution source: https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify/53593328#53593328
29
+ JSON.stringify(object, (key, value) => (allKeys.add(key), value));
30
+ return JSON.stringify(object, Array.from(allKeys).sort());
31
+ }
32
+
33
+ /** Stringifies an `object` and returns the hash of the resulting string. Sorts object fields
34
+ * in consistent order before stringify to prevent different hash ids for the same object.
35
+ *
36
+ * @param object - The object for which the hash is desired
37
+ * @returns - The string obtained from the hash of the stringified object
38
+ */
39
+ export function hashObject(object: unknown): string {
40
+ return hashString(sortedJSONStringify(object));
41
+ }
42
+
20
43
  /** Stringifies the schema and returns the hash of the resulting string. Sorts schema fields
21
44
  * in consistent order before stringify to prevent different hash ids for the same schema.
22
45
  *
@@ -24,8 +47,5 @@ function hashString(string: string): string {
24
47
  * @returns - The string obtained from the hash of the stringified schema
25
48
  */
26
49
  export default function hashForSchema<S extends StrictRJSFSchema = RJSFSchema>(schema: S) {
27
- const allKeys = new Set<string>();
28
- // solution source: https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify/53593328#53593328
29
- JSON.stringify(schema, (key, value) => (allKeys.add(key), value));
30
- return hashString(JSON.stringify(schema, Array.from(allKeys).sort()));
50
+ return hashObject(schema);
31
51
  }
@@ -79,3 +79,13 @@ export function ariaDescribedByIds<T = any>(id: IdSchema<T> | string, includeExa
79
79
  export function optionId(id: string, optionIndex: number) {
80
80
  return `${id}-${optionIndex}`;
81
81
  }
82
+
83
+ /** Return a consistent `id` for the `btn` button element
84
+ *
85
+ * @param id - Either simple string id or an IdSchema from which to extract it
86
+ * @param btn - The button type for which to generate the id
87
+ * @returns - The consistent id for the button from the given `id` and `btn` type
88
+ */
89
+ export function buttonId<T = any>(id: IdSchema<T> | string, btn: 'add' | 'copy' | 'moveDown' | 'moveUp' | 'remove') {
90
+ return idGenerator<T>(id, btn);
91
+ }
package/src/index.ts CHANGED
@@ -20,18 +20,29 @@ import getInputProps from './getInputProps';
20
20
  import getSchemaType from './getSchemaType';
21
21
  import getSubmitButtonOptions from './getSubmitButtonOptions';
22
22
  import getTemplate from './getTemplate';
23
+ import getTestIds from './getTestIds';
23
24
  import getUiOptions from './getUiOptions';
24
25
  import getWidget from './getWidget';
25
26
  import guessType from './guessType';
26
- import hashForSchema from './hashForSchema';
27
+ import hashForSchema, { hashObject, hashString, sortedJSONStringify } from './hashForSchema';
27
28
  import hasWidget from './hasWidget';
28
- import { ariaDescribedByIds, descriptionId, errorId, examplesId, helpId, optionId, titleId } from './idGenerators';
29
+ import {
30
+ ariaDescribedByIds,
31
+ buttonId,
32
+ descriptionId,
33
+ errorId,
34
+ examplesId,
35
+ helpId,
36
+ optionId,
37
+ titleId,
38
+ } from './idGenerators';
29
39
  import isConstant from './isConstant';
30
40
  import isCustomWidget from './isCustomWidget';
31
41
  import isFixedItems from './isFixedItems';
32
42
  import isObject from './isObject';
33
43
  import labelValue from './labelValue';
34
44
  import localToUTC from './localToUTC';
45
+ import lookupFromFormContext from './lookupFromFormContext';
35
46
  import mergeDefaultsWithFormData from './mergeDefaultsWithFormData';
36
47
  import mergeObjects from './mergeObjects';
37
48
  import mergeSchemas from './mergeSchemas';
@@ -52,6 +63,7 @@ import utcToLocal from './utcToLocal';
52
63
  import validationDataMerge from './validationDataMerge';
53
64
  import withIdRefPrefix from './withIdRefPrefix';
54
65
  import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator';
66
+ import getChangedFields from './getChangedFields';
55
67
 
56
68
  export * from './types';
57
69
  export * from './enums';
@@ -64,6 +76,7 @@ export {
64
76
  allowAdditionalItems,
65
77
  ariaDescribedByIds,
66
78
  asNumber,
79
+ buttonId,
67
80
  canExpand,
68
81
  createErrorHandler,
69
82
  createSchemaUtils,
@@ -82,6 +95,7 @@ export {
82
95
  examplesId,
83
96
  ErrorSchemaBuilder,
84
97
  findSchemaDefinition,
98
+ getChangedFields,
85
99
  getDateElementProps,
86
100
  getDiscriminatorFieldFromSchema,
87
101
  getInputProps,
@@ -89,11 +103,14 @@ export {
89
103
  getSchemaType,
90
104
  getSubmitButtonOptions,
91
105
  getTemplate,
106
+ getTestIds,
92
107
  getUiOptions,
93
108
  getWidget,
94
109
  guessType,
95
110
  hasWidget,
96
111
  hashForSchema,
112
+ hashObject,
113
+ hashString,
97
114
  helpId,
98
115
  isConstant,
99
116
  isCustomWidget,
@@ -101,6 +118,7 @@ export {
101
118
  isObject,
102
119
  labelValue,
103
120
  localToUTC,
121
+ lookupFromFormContext,
104
122
  mergeDefaultsWithFormData,
105
123
  mergeObjects,
106
124
  mergeSchemas,
@@ -113,6 +131,7 @@ export {
113
131
  replaceStringParameters,
114
132
  schemaRequiresTrueValue,
115
133
  shouldRender,
134
+ sortedJSONStringify,
116
135
  titleId,
117
136
  toConstant,
118
137
  toDateString,
@@ -9,7 +9,7 @@ import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema } from './types
9
9
  export default function isCustomWidget<
10
10
  T = any,
11
11
  S extends StrictRJSFSchema = RJSFSchema,
12
- F extends FormContextType = any
12
+ F extends FormContextType = any,
13
13
  >(uiSchema: UiSchema<T, S, F> = {}) {
14
14
  return (
15
15
  // TODO: Remove the `&& uiSchema['ui:widget'] !== 'hidden'` once we support hidden widgets for arrays.
package/src/isObject.ts CHANGED
@@ -1,15 +1,22 @@
1
- /** Determines whether a `thing` is an object for the purposes of RSJF. In this case, `thing` is an object if it has
1
+ /** Determines whether a `thing` is an object for the purposes of RJSF. In this case, `thing` is an object if it has
2
2
  * the type `object` but is NOT null, an array or a File.
3
3
  *
4
4
  * @param thing - The thing to check to see whether it is an object
5
5
  * @returns - True if it is a non-null, non-array, non-File object
6
6
  */
7
- export default function isObject(thing: any) {
8
- if (typeof File !== 'undefined' && thing instanceof File) {
7
+ export default function isObject(thing: any): thing is object {
8
+ if (typeof thing !== 'object' || thing === null) {
9
9
  return false;
10
10
  }
11
- if (typeof Date !== 'undefined' && thing instanceof Date) {
11
+ // lastModified is guaranteed to be a number on a File instance
12
+ // as per https://w3c.github.io/FileAPI/#dfn-lastModified
13
+ if (typeof thing.lastModified === 'number' && typeof File !== 'undefined' && thing instanceof File) {
12
14
  return false;
13
15
  }
14
- return typeof thing === 'object' && thing !== null && !Array.isArray(thing);
16
+ // getMonth is guaranteed to be a method on a Date instance
17
+ // as per https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date.prototype.getmonth
18
+ if (typeof thing.getMonth === 'function' && typeof Date !== 'undefined' && thing instanceof Date) {
19
+ return false;
20
+ }
21
+ return !Array.isArray(thing);
15
22
  }
package/src/labelValue.ts CHANGED
@@ -17,12 +17,12 @@ export default function labelValue(label?: ReactElement, hideLabel?: boolean, fa
17
17
  export default function labelValue(
18
18
  label?: ReactElement,
19
19
  hideLabel?: boolean,
20
- fallback?: false
20
+ fallback?: false,
21
21
  ): undefined | false | ReactElement;
22
22
  export default function labelValue(
23
23
  label?: string | ReactElement,
24
24
  hideLabel?: boolean,
25
- fallback?: false | ''
25
+ fallback?: false | '',
26
26
  ): undefined | false | string | ReactElement {
27
27
  return hideLabel ? fallback : label;
28
28
  }
@@ -0,0 +1,26 @@
1
+ import get from 'lodash/get';
2
+ import has from 'lodash/has';
3
+
4
+ import { FORM_CONTEXT_NAME, LOOKUP_MAP_NAME } from './constants';
5
+ import { FormContextType, RJSFSchema, Registry, StrictRJSFSchema } from './types';
6
+
7
+ /** Given a React JSON Schema Form registry or formContext object, return the value associated with `toLookup`. This
8
+ * might be contained within the lookup map in the formContext. If no such value exists, return the `fallback`
9
+ * value.
10
+ *
11
+ * @param regOrFc - The @rjsf registry or form context in which the lookup will occur
12
+ * @param toLookup - The name of the field in the lookup map in the form context to get the value for
13
+ * @param [fallback] - The fallback value to use if the form context does not contain a value for `toLookup`
14
+ * @returns - The value associated with `toLookup` in the form context or `fallback`
15
+ */
16
+ export default function lookupFromFormContext<
17
+ T = any,
18
+ S extends StrictRJSFSchema = RJSFSchema,
19
+ F extends FormContextType = any,
20
+ >(regOrFc: Registry<T, S, F> | Registry<T, S, F>['formContext'], toLookup: string, fallback?: unknown) {
21
+ const lookupPath = [LOOKUP_MAP_NAME];
22
+ if (has(regOrFc, FORM_CONTEXT_NAME)) {
23
+ lookupPath.unshift(FORM_CONTEXT_NAME);
24
+ }
25
+ return get(regOrFc, [...lookupPath, toLookup], fallback);
26
+ }