@rjsf/utils 6.0.0-beta.2 → 6.0.0-beta.21

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 (99) hide show
  1. package/dist/{index.js → index.cjs} +396 -264
  2. package/dist/index.cjs.map +7 -0
  3. package/dist/utils.esm.js +395 -263
  4. package/dist/utils.esm.js.map +4 -4
  5. package/dist/utils.umd.js +368 -249
  6. package/lib/ErrorSchemaBuilder.d.ts +2 -2
  7. package/lib/canExpand.d.ts +1 -1
  8. package/lib/constants.d.ts +3 -0
  9. package/lib/constants.js +3 -0
  10. package/lib/constants.js.map +1 -1
  11. package/lib/createSchemaUtils.js +25 -18
  12. package/lib/createSchemaUtils.js.map +1 -1
  13. package/lib/enums.d.ts +9 -3
  14. package/lib/enums.js +9 -3
  15. package/lib/enums.js.map +1 -1
  16. package/lib/findSchemaDefinition.d.ts +7 -1
  17. package/lib/findSchemaDefinition.js +48 -6
  18. package/lib/findSchemaDefinition.js.map +1 -1
  19. package/lib/getTestIds.js +2 -2
  20. package/lib/getTestIds.js.map +1 -1
  21. package/lib/getUiOptions.js +4 -0
  22. package/lib/getUiOptions.js.map +1 -1
  23. package/lib/getWidget.js +3 -3
  24. package/lib/getWidget.js.map +1 -1
  25. package/lib/guessType.d.ts +1 -1
  26. package/lib/idGenerators.d.ts +22 -15
  27. package/lib/idGenerators.js +17 -8
  28. package/lib/idGenerators.js.map +1 -1
  29. package/lib/index.d.ts +10 -4
  30. package/lib/index.js +9 -4
  31. package/lib/index.js.map +1 -1
  32. package/lib/isFormDataAvailable.d.ts +7 -0
  33. package/lib/isFormDataAvailable.js +13 -0
  34. package/lib/isFormDataAvailable.js.map +1 -0
  35. package/lib/isRootSchema.d.ts +13 -0
  36. package/lib/isRootSchema.js +25 -0
  37. package/lib/isRootSchema.js.map +1 -0
  38. package/lib/mergeDefaultsWithFormData.js +14 -2
  39. package/lib/mergeDefaultsWithFormData.js.map +1 -1
  40. package/lib/schema/findFieldInSchema.d.ts +1 -1
  41. package/lib/schema/findFieldInSchema.js +1 -1
  42. package/lib/schema/getDefaultFormState.d.ts +17 -3
  43. package/lib/schema/getDefaultFormState.js +73 -27
  44. package/lib/schema/getDefaultFormState.js.map +1 -1
  45. package/lib/schema/getDisplayLabel.js +2 -2
  46. package/lib/schema/getDisplayLabel.js.map +1 -1
  47. package/lib/schema/index.d.ts +1 -2
  48. package/lib/schema/index.js +1 -2
  49. package/lib/schema/index.js.map +1 -1
  50. package/lib/schema/retrieveSchema.d.ts +11 -6
  51. package/lib/schema/retrieveSchema.js +42 -19
  52. package/lib/schema/retrieveSchema.js.map +1 -1
  53. package/lib/shallowEquals.d.ts +8 -0
  54. package/lib/shallowEquals.js +36 -0
  55. package/lib/shallowEquals.js.map +1 -0
  56. package/lib/shouldRender.d.ts +8 -2
  57. package/lib/shouldRender.js +17 -2
  58. package/lib/shouldRender.js.map +1 -1
  59. package/lib/shouldRenderOptionalField.d.ts +18 -0
  60. package/lib/shouldRenderOptionalField.js +47 -0
  61. package/lib/shouldRenderOptionalField.js.map +1 -0
  62. package/lib/toFieldPathId.d.ts +12 -0
  63. package/lib/toFieldPathId.js +19 -0
  64. package/lib/toFieldPathId.js.map +1 -0
  65. package/lib/tsconfig.tsbuildinfo +1 -1
  66. package/lib/types.d.ts +136 -81
  67. package/lib/validationDataMerge.d.ts +2 -1
  68. package/lib/validationDataMerge.js +3 -2
  69. package/lib/validationDataMerge.js.map +1 -1
  70. package/package.json +13 -14
  71. package/src/ErrorSchemaBuilder.ts +2 -2
  72. package/src/constants.ts +3 -0
  73. package/src/createSchemaUtils.ts +25 -26
  74. package/src/enums.ts +9 -3
  75. package/src/findSchemaDefinition.ts +55 -6
  76. package/src/getTestIds.ts +2 -2
  77. package/src/getUiOptions.ts +4 -0
  78. package/src/getWidget.tsx +3 -3
  79. package/src/idGenerators.ts +35 -25
  80. package/src/index.ts +16 -2
  81. package/src/isFormDataAvailable.ts +13 -0
  82. package/src/isRootSchema.ts +30 -0
  83. package/src/mergeDefaultsWithFormData.ts +16 -2
  84. package/src/schema/findFieldInSchema.ts +1 -1
  85. package/src/schema/getDefaultFormState.ts +95 -33
  86. package/src/schema/getDisplayLabel.ts +2 -2
  87. package/src/schema/index.ts +0 -2
  88. package/src/schema/retrieveSchema.ts +46 -10
  89. package/src/shallowEquals.ts +41 -0
  90. package/src/shouldRender.ts +27 -2
  91. package/src/shouldRenderOptionalField.ts +56 -0
  92. package/src/toFieldPathId.ts +24 -0
  93. package/src/types.ts +156 -84
  94. package/src/validationDataMerge.ts +7 -1
  95. package/dist/index.js.map +0 -7
  96. package/lib/schema/toIdSchema.d.ts +0 -14
  97. package/lib/schema/toIdSchema.js +0 -62
  98. package/lib/schema/toIdSchema.js.map +0 -1
  99. package/src/schema/toIdSchema.ts +0 -131
@@ -5,7 +5,6 @@ import {
5
5
  FormContextType,
6
6
  FoundFieldType,
7
7
  GlobalUISchemaOptions,
8
- IdSchema,
9
8
  PathSchema,
10
9
  RJSFSchema,
11
10
  SchemaUtilsType,
@@ -26,9 +25,11 @@ import {
26
25
  isSelect,
27
26
  retrieveSchema,
28
27
  sanitizeDataForNewSchema,
29
- toIdSchema,
30
28
  toPathSchema,
31
29
  } from './schema';
30
+ import { makeAllReferencesAbsolute } from './findSchemaDefinition';
31
+ import { ID_KEY, JSON_SCHEMA_DRAFT_2020_12, SCHEMA_KEY } from './constants';
32
+ import get from 'lodash/get';
32
33
 
33
34
  /** The `SchemaUtils` class provides a wrapper around the publicly exported APIs in the `utils/schema` directory such
34
35
  * that one does not have to explicitly pass the `validator`, `rootSchema`, `experimental_defaultFormStateBehavior` or
@@ -57,12 +58,24 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
57
58
  experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior,
58
59
  experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
59
60
  ) {
60
- this.rootSchema = rootSchema;
61
+ if (rootSchema && rootSchema[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2020_12) {
62
+ this.rootSchema = makeAllReferencesAbsolute(rootSchema, get(rootSchema, ID_KEY, '#'));
63
+ } else {
64
+ this.rootSchema = rootSchema;
65
+ }
61
66
  this.validator = validator;
62
67
  this.experimental_defaultFormStateBehavior = experimental_defaultFormStateBehavior;
63
68
  this.experimental_customMergeAllOf = experimental_customMergeAllOf;
64
69
  }
65
70
 
71
+ /** Returns the `rootSchema` in the `SchemaUtilsType`
72
+ *
73
+ * @returns - The `rootSchema`
74
+ */
75
+ getRootSchema() {
76
+ return this.rootSchema;
77
+ }
78
+
66
79
  /** Returns the `ValidatorType` in the `SchemaUtilsType`
67
80
  *
68
81
  * @returns - The `ValidatorType`
@@ -87,9 +100,12 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
87
100
  experimental_defaultFormStateBehavior = {},
88
101
  experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
89
102
  ): boolean {
103
+ // If either validator or rootSchema are falsy, return false to prevent the creation
104
+ // of a new SchemaUtilsType with incomplete properties.
90
105
  if (!validator || !rootSchema) {
91
106
  return false;
92
107
  }
108
+
93
109
  return (
94
110
  this.validator !== validator ||
95
111
  !deepEquals(this.rootSchema, rootSchema) ||
@@ -149,12 +165,14 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
149
165
  * @param [includeUndefinedValues=false] - Optional flag, if true, cause undefined values to be added as defaults.
150
166
  * If "excludeObjectChildren", pass `includeUndefinedValues` as false when computing defaults for any nested
151
167
  * object properties.
168
+ * @param initialDefaultsGenerated - Indicates whether or not initial defaults have been generated
152
169
  * @returns - The resulting `formData` with all the defaults provided
153
170
  */
154
171
  getDefaultFormState(
155
172
  schema: S,
156
173
  formData?: T,
157
174
  includeUndefinedValues: boolean | 'excludeObjectChildren' = false,
175
+ initialDefaultsGenerated?: boolean,
158
176
  ): T | T[] | undefined {
159
177
  return getDefaultFormState<T, S, F>(
160
178
  this.validator,
@@ -164,6 +182,7 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
164
182
  includeUndefinedValues,
165
183
  this.experimental_defaultFormStateBehavior,
166
184
  this.experimental_customMergeAllOf,
185
+ initialDefaultsGenerated,
167
186
  );
168
187
  }
169
188
 
@@ -285,15 +304,17 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
285
304
  *
286
305
  * @param schema - The schema for which retrieving a schema is desired
287
306
  * @param [rawFormData] - The current formData, if any, to assist retrieving a schema
307
+ * @param [resolveAnyOfOrOneOfRefs] - Optional flag indicating whether to resolved refs in anyOf/oneOf lists
288
308
  * @returns - The schema having its conditions, additional properties, references and dependencies resolved
289
309
  */
290
- retrieveSchema(schema: S, rawFormData?: T) {
310
+ retrieveSchema(schema: S, rawFormData?: T, resolveAnyOfOrOneOfRefs?: boolean) {
291
311
  return retrieveSchema<T, S, F>(
292
312
  this.validator,
293
313
  schema,
294
314
  this.rootSchema,
295
315
  rawFormData,
296
316
  this.experimental_customMergeAllOf,
317
+ resolveAnyOfOrOneOfRefs,
297
318
  );
298
319
  }
299
320
 
@@ -319,28 +340,6 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
319
340
  );
320
341
  }
321
342
 
322
- /** Generates an `IdSchema` object for the `schema`, recursively
323
- *
324
- * @param schema - The schema for which the display label flag is desired
325
- * @param [id] - The base id for the schema
326
- * @param [formData] - The current formData, if any, onto which to provide any missing defaults
327
- * @param [idPrefix='root'] - The prefix to use for the id
328
- * @param [idSeparator='_'] - The separator to use for the path segments in the id
329
- * @returns - The `IdSchema` object for the `schema`
330
- */
331
- toIdSchema(schema: S, id?: string | null, formData?: T, idPrefix = 'root', idSeparator = '_'): IdSchema<T> {
332
- return toIdSchema<T, S, F>(
333
- this.validator,
334
- schema,
335
- id,
336
- this.rootSchema,
337
- formData,
338
- idPrefix,
339
- idSeparator,
340
- this.experimental_customMergeAllOf,
341
- );
342
- }
343
-
344
343
  /** Generates an `PathSchema` object for the `schema`, recursively
345
344
  *
346
345
  * @param schema - The schema for which the display label flag is desired
package/src/enums.ts CHANGED
@@ -45,6 +45,12 @@ export enum TranslatableString {
45
45
  DecrementAriaLabel = 'Decrease value by 1',
46
46
  /** Increment button aria label, used by UpDownWidget */
47
47
  IncrementAriaLabel = 'Increase value by 1',
48
+ /** The label for the Add button in for an optional object field */
49
+ OptionalObjectAdd = 'Add data for optional field',
50
+ /** The label for the Remove button in for an optional object field */
51
+ OptionalObjectRemove = 'Remove data for optional field',
52
+ /** The label for when displaying a non-editable form with missing optional field data */
53
+ OptionalObjectEmptyMsg = 'No data for optional field',
48
54
  // Strings with replaceable parameters
49
55
  /** Unknown field type reason, where %1 will be replaced with the type as provided by SchemaField */
50
56
  UnknownFieldType = 'Unknown field type %1',
@@ -63,7 +69,7 @@ export enum TranslatableString {
63
69
  InvalidObjectField = 'Invalid "%1" object field configuration: _%2_.',
64
70
  /** Unsupported field schema, used by UnsupportedField */
65
71
  UnsupportedField = 'Unsupported field schema.',
66
- /** Unsupported field schema, where %1 will be replaced by the idSchema.$id as provided by UnsupportedField.
72
+ /** Unsupported field schema, where %1 will be replaced by the FieldPathId.$id as provided by UnsupportedField.
67
73
  * NOTE: Use markdown notation rather than html tags.
68
74
  */
69
75
  UnsupportedFieldWithId = 'Unsupported field schema for field `%1`.',
@@ -71,8 +77,8 @@ export enum TranslatableString {
71
77
  * NOTE: Use markdown notation rather than html tags.
72
78
  */
73
79
  UnsupportedFieldWithReason = 'Unsupported field schema: _%1_.',
74
- /** Unsupported field schema, where %1 and %2 will be replaced by the idSchema.$id and reason strings, respectively,
75
- * as provided by UnsupportedField.
80
+ /** Unsupported field schema, where %1 and %2 will be replaced by the FieldPathId.$id and reason strings,
81
+ * respectively, as provided by UnsupportedField.
76
82
  * NOTE: Use markdown notation rather than html tags.
77
83
  */
78
84
  UnsupportedFieldWithIdAndReason = 'Unsupported field schema for field `%1`: _%2_.',
@@ -1,11 +1,19 @@
1
1
  import jsonpointer from 'jsonpointer';
2
2
  import omit from 'lodash/omit';
3
3
 
4
- import { ID_KEY, JSON_SCHEMA_DRAFT_2020_12, REF_KEY, SCHEMA_KEY } from './constants';
4
+ import {
5
+ ALL_OF_KEY,
6
+ ID_KEY,
7
+ JSON_SCHEMA_DRAFT_2019_09,
8
+ JSON_SCHEMA_DRAFT_2020_12,
9
+ REF_KEY,
10
+ SCHEMA_KEY,
11
+ } from './constants';
5
12
  import { GenericObjectType, RJSFSchema, StrictRJSFSchema } from './types';
6
13
  import isObject from 'lodash/isObject';
7
14
  import isEmpty from 'lodash/isEmpty';
8
15
  import UriResolver from 'fast-uri';
16
+ import get from 'lodash/get';
9
17
 
10
18
  /** Looks for the `$id` pointed by `ref` in the schema definitions embedded in
11
19
  * a JSON Schema bundle
@@ -19,7 +27,16 @@ function findEmbeddedSchemaRecursive<S extends StrictRJSFSchema = RJSFSchema>(sc
19
27
  return schema;
20
28
  }
21
29
  for (const subSchema of Object.values(schema)) {
22
- if (isObject(subSchema)) {
30
+ if (Array.isArray(subSchema)) {
31
+ for (const item of subSchema) {
32
+ if (isObject(item)) {
33
+ const result = findEmbeddedSchemaRecursive<S>(item as S, ref);
34
+ if (result !== undefined) {
35
+ return result as S;
36
+ }
37
+ }
38
+ }
39
+ } else if (isObject(subSchema)) {
23
40
  const result = findEmbeddedSchemaRecursive<S>(subSchema as S, ref);
24
41
  if (result !== undefined) {
25
42
  return result as S;
@@ -29,6 +46,31 @@ function findEmbeddedSchemaRecursive<S extends StrictRJSFSchema = RJSFSchema>(sc
29
46
  return undefined;
30
47
  }
31
48
 
49
+ /** Parses a JSONSchema and makes all references absolute with respect to
50
+ * the `baseURI` argument
51
+ * @param schema - The schema to be processed
52
+ * @param baseURI - The base URI to be used for resolving relative references
53
+ */
54
+ export function makeAllReferencesAbsolute<S extends StrictRJSFSchema = RJSFSchema>(schema: S, baseURI: string): S {
55
+ const currentURI = get(schema, ID_KEY, baseURI);
56
+ // Make all other references absolute
57
+ if (REF_KEY in schema) {
58
+ schema = { ...schema, [REF_KEY]: UriResolver.resolve(currentURI, schema[REF_KEY]!) };
59
+ }
60
+ // Look for references in nested subschemas
61
+ for (const [key, subSchema] of Object.entries(schema)) {
62
+ if (Array.isArray(subSchema)) {
63
+ schema = {
64
+ ...schema,
65
+ [key]: subSchema.map((item) => (isObject(item) ? makeAllReferencesAbsolute(item as S, currentURI) : item)),
66
+ };
67
+ } else if (isObject(subSchema)) {
68
+ schema = { ...schema, [key]: makeAllReferencesAbsolute(subSchema as S, currentURI) };
69
+ }
70
+ }
71
+ return schema;
72
+ }
73
+
32
74
  /** Splits out the value at the `key` in `object` from the `object`, returning an array that contains in the first
33
75
  * location, the `object` minus the `key: value` and in the second location the `value`.
34
76
  *
@@ -51,7 +93,7 @@ export function splitKeyElementFromObject(key: string, object: GenericObjectType
51
93
  * @param $ref - The ref string for which the schema definition is desired
52
94
  * @param [rootSchema={}] - The root schema in which to search for the definition
53
95
  * @param recurseList - List of $refs already resolved to prevent recursion
54
- * @param baseURI - The base URI to be used for resolving relative references
96
+ * @param [baseURI=rootSchema['$id']] - The base URI to be used for resolving relative references
55
97
  * @returns - The sub-schema within the `rootSchema` which matches the `$ref` if it exists
56
98
  * @throws - Error indicating that no schema for that reference could be resolved
57
99
  */
@@ -59,7 +101,7 @@ export function findSchemaDefinitionRecursive<S extends StrictRJSFSchema = RJSFS
59
101
  $ref?: string,
60
102
  rootSchema: S = {} as S,
61
103
  recurseList: string[] = [],
62
- baseURI: string | undefined = ID_KEY in rootSchema ? rootSchema[ID_KEY] : undefined,
104
+ baseURI: string | undefined = get(rootSchema, [ID_KEY]),
63
105
  ): S {
64
106
  const ref = $ref || '';
65
107
  let current: S | undefined = undefined;
@@ -102,7 +144,14 @@ export function findSchemaDefinitionRecursive<S extends StrictRJSFSchema = RJSFS
102
144
  const [remaining, theRef] = splitKeyElementFromObject(REF_KEY, current);
103
145
  const subSchema = findSchemaDefinitionRecursive<S>(theRef, rootSchema, [...recurseList, ref], baseURI);
104
146
  if (Object.keys(remaining).length > 0) {
105
- return { ...remaining, ...subSchema };
147
+ if (
148
+ rootSchema[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2019_09 ||
149
+ rootSchema[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2020_12
150
+ ) {
151
+ return { [ALL_OF_KEY]: [remaining, subSchema] } as S;
152
+ } else {
153
+ return { ...remaining, ...subSchema };
154
+ }
106
155
  }
107
156
  return subSchema;
108
157
  }
@@ -123,7 +172,7 @@ export function findSchemaDefinitionRecursive<S extends StrictRJSFSchema = RJSFS
123
172
  export default function findSchemaDefinition<S extends StrictRJSFSchema = RJSFSchema>(
124
173
  $ref?: string,
125
174
  rootSchema: S = {} as S,
126
- baseURI: string | undefined = ID_KEY in rootSchema ? rootSchema[ID_KEY] : undefined,
175
+ baseURI: string | undefined = get(rootSchema, [ID_KEY]),
127
176
  ): S {
128
177
  const recurseList: string[] = [];
129
178
  return findSchemaDefinitionRecursive($ref, rootSchema, recurseList, baseURI);
package/src/getTestIds.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { nanoid } from 'nanoid';
2
1
  import get from 'lodash/get';
2
+ import uniqueId from 'lodash/uniqueId';
3
3
 
4
4
  import { TestIdShape } from './types';
5
5
 
@@ -31,7 +31,7 @@ export default function getTestIds(): TestIdShape {
31
31
  {
32
32
  get(_obj, prop) {
33
33
  if (!ids.has(prop)) {
34
- ids.set(prop, nanoid());
34
+ ids.set(prop, uniqueId('test-id-'));
35
35
  }
36
36
  return ids.get(prop);
37
37
  },
@@ -13,6 +13,10 @@ export default function getUiOptions<T = any, S extends StrictRJSFSchema = RJSFS
13
13
  uiSchema: UiSchema<T, S, F> = {},
14
14
  globalOptions: GlobalUISchemaOptions = {},
15
15
  ): UIOptionsType<T, S, F> {
16
+ // Handle null or undefined uiSchema
17
+ if (!uiSchema) {
18
+ return { ...globalOptions };
19
+ }
16
20
  return Object.keys(uiSchema)
17
21
  .filter((key) => key.indexOf('ui:') === 0)
18
22
  .reduce(
package/src/getWidget.tsx CHANGED
@@ -110,7 +110,7 @@ export default function getWidget<T = any, S extends StrictRJSFSchema = RJSFSche
110
110
  }
111
111
 
112
112
  if (typeof widget !== 'string') {
113
- throw new Error(`Unsupported widget definition: ${typeof widget}`);
113
+ throw new Error(`Unsupported widget definition: ${typeof widget} in schema: ${JSON.stringify(schema)}`);
114
114
  }
115
115
 
116
116
  if (widget in registeredWidgets) {
@@ -120,7 +120,7 @@ export default function getWidget<T = any, S extends StrictRJSFSchema = RJSFSche
120
120
 
121
121
  if (typeof type === 'string') {
122
122
  if (!(type in widgetMap)) {
123
- throw new Error(`No widget for type '${type}'`);
123
+ throw new Error(`No widget for type '${type}' in schema: ${JSON.stringify(schema)}`);
124
124
  }
125
125
 
126
126
  if (widget in widgetMap[type]) {
@@ -129,5 +129,5 @@ export default function getWidget<T = any, S extends StrictRJSFSchema = RJSFSche
129
129
  }
130
130
  }
131
131
 
132
- throw new Error(`No widget '${widget}' for type '${type}'`);
132
+ throw new Error(`No widget '${widget}' for type '${type}' in schema: ${JSON.stringify(schema)}`);
133
133
  }
@@ -1,73 +1,73 @@
1
1
  import isString from 'lodash/isString';
2
2
 
3
- import { IdSchema } from './types';
3
+ import { FieldPathId } from './types';
4
4
  import { ID_KEY } from './constants';
5
5
 
6
6
  /** Generates a consistent `id` pattern for a given `id` and a `suffix`
7
7
  *
8
- * @param id - Either simple string id or an IdSchema from which to extract it
8
+ * @param id - Either simple string id or an FieldPathId from which to extract it
9
9
  * @param suffix - The suffix to append to the id
10
10
  */
11
- function idGenerator<T = any>(id: IdSchema<T> | string, suffix: string) {
11
+ function idGenerator(id: FieldPathId | string, suffix: string) {
12
12
  const theId = isString(id) ? id : id[ID_KEY];
13
13
  return `${theId}__${suffix}`;
14
14
  }
15
15
  /** Return a consistent `id` for the field description element
16
16
  *
17
- * @param id - Either simple string id or an IdSchema from which to extract it
17
+ * @param id - Either simple string id or an FieldPathId from which to extract it
18
18
  * @returns - The consistent id for the field description element from the given `id`
19
19
  */
20
- export function descriptionId<T = any>(id: IdSchema<T> | string) {
21
- return idGenerator<T>(id, 'description');
20
+ export function descriptionId(id: FieldPathId | string) {
21
+ return idGenerator(id, 'description');
22
22
  }
23
23
 
24
24
  /** Return a consistent `id` for the field error element
25
25
  *
26
- * @param id - Either simple string id or an IdSchema from which to extract it
26
+ * @param id - Either simple string id or an FieldPathId from which to extract it
27
27
  * @returns - The consistent id for the field error element from the given `id`
28
28
  */
29
- export function errorId<T = any>(id: IdSchema<T> | string) {
30
- return idGenerator<T>(id, 'error');
29
+ export function errorId(id: FieldPathId | string) {
30
+ return idGenerator(id, 'error');
31
31
  }
32
32
 
33
33
  /** Return a consistent `id` for the field examples element
34
34
  *
35
- * @param id - Either simple string id or an IdSchema from which to extract it
35
+ * @param id - Either simple string id or an FieldPathId from which to extract it
36
36
  * @returns - The consistent id for the field examples element from the given `id`
37
37
  */
38
- export function examplesId<T = any>(id: IdSchema<T> | string) {
39
- return idGenerator<T>(id, 'examples');
38
+ export function examplesId(id: FieldPathId | string) {
39
+ return idGenerator(id, 'examples');
40
40
  }
41
41
 
42
42
  /** Return a consistent `id` for the field help element
43
43
  *
44
- * @param id - Either simple string id or an IdSchema from which to extract it
44
+ * @param id - Either simple string id or an FieldPathId from which to extract it
45
45
  * @returns - The consistent id for the field help element from the given `id`
46
46
  */
47
- export function helpId<T = any>(id: IdSchema<T> | string) {
48
- return idGenerator<T>(id, 'help');
47
+ export function helpId(id: FieldPathId | string) {
48
+ return idGenerator(id, 'help');
49
49
  }
50
50
 
51
51
  /** Return a consistent `id` for the field title element
52
52
  *
53
- * @param id - Either simple string id or an IdSchema from which to extract it
53
+ * @param id - Either simple string id or an FieldPathId from which to extract it
54
54
  * @returns - The consistent id for the field title element from the given `id`
55
55
  */
56
- export function titleId<T = any>(id: IdSchema<T> | string) {
57
- return idGenerator<T>(id, 'title');
56
+ export function titleId(id: FieldPathId | string) {
57
+ return idGenerator(id, 'title');
58
58
  }
59
59
 
60
60
  /** Return a list of element ids that contain additional information about the field that can be used to as the aria
61
61
  * description of the field. This is correctly omitting `titleId` which would be "labeling" rather than "describing" the
62
62
  * element.
63
63
  *
64
- * @param id - Either simple string id or an IdSchema from which to extract it
64
+ * @param id - Either simple string id or an FieldPathId from which to extract it
65
65
  * @param [includeExamples=false] - Optional flag, if true, will add the `examplesId` into the list
66
66
  * @returns - The string containing the list of ids for use in an `aria-describedBy` attribute
67
67
  */
68
- export function ariaDescribedByIds<T = any>(id: IdSchema<T> | string, includeExamples = false) {
69
- const examples = includeExamples ? ` ${examplesId<T>(id)}` : '';
70
- return `${errorId<T>(id)} ${descriptionId<T>(id)} ${helpId<T>(id)}${examples}`;
68
+ export function ariaDescribedByIds(id: FieldPathId | string, includeExamples = false) {
69
+ const examples = includeExamples ? ` ${examplesId(id)}` : '';
70
+ return `${errorId(id)} ${descriptionId(id)} ${helpId(id)}${examples}`;
71
71
  }
72
72
 
73
73
  /** Return a consistent `id` for the `optionIndex`s of a `Radio` or `Checkboxes` widget
@@ -82,10 +82,20 @@ export function optionId(id: string, optionIndex: number) {
82
82
 
83
83
  /** Return a consistent `id` for the `btn` button element
84
84
  *
85
- * @param id - Either simple string id or an IdSchema from which to extract it
85
+ * @param id - The id of the parent component for the option
86
86
  * @param btn - The button type for which to generate the id
87
87
  * @returns - The consistent id for the button from the given `id` and `btn` type
88
88
  */
89
- export function buttonId<T = any>(id: IdSchema<T> | string, btn: 'add' | 'copy' | 'moveDown' | 'moveUp' | 'remove') {
90
- return idGenerator<T>(id, btn);
89
+ export function buttonId(id: FieldPathId | string, btn: 'add' | 'copy' | 'moveDown' | 'moveUp' | 'remove') {
90
+ return idGenerator(id, btn);
91
+ }
92
+
93
+ /** Return a consistent `id` for the optional data controls `element`
94
+ *
95
+ * @param id - The id of the parent component for the option
96
+ * @param element - The element type for which to generate the id
97
+ * @returns - The consistent id for the optional data controls element from the given `id` and `element` type
98
+ */
99
+ export function optionalControlsId(id: FieldPathId | string, element: 'Add' | 'Msg' | 'Remove') {
100
+ return idGenerator(id, `optional${element}`);
91
101
  }
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import createSchemaUtils from './createSchemaUtils';
6
6
  import dataURItoBlob from './dataURItoBlob';
7
7
  import dateRangeOptions from './dateRangeOptions';
8
8
  import deepEquals from './deepEquals';
9
+ import shallowEquals from './shallowEquals';
9
10
  import englishStringTranslator from './englishStringTranslator';
10
11
  import enumOptionsDeselectValue from './enumOptionsDeselectValue';
11
12
  import enumOptionsIndexForValue from './enumOptionsIndexForValue';
@@ -14,9 +15,11 @@ import enumOptionsSelectValue from './enumOptionsSelectValue';
14
15
  import enumOptionsValueForIndex from './enumOptionsValueForIndex';
15
16
  import ErrorSchemaBuilder from './ErrorSchemaBuilder';
16
17
  import findSchemaDefinition from './findSchemaDefinition';
18
+ import getChangedFields from './getChangedFields';
17
19
  import getDateElementProps, { type DateElementFormat } from './getDateElementProps';
18
20
  import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema';
19
21
  import getInputProps from './getInputProps';
22
+ import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator';
20
23
  import getSchemaType from './getSchemaType';
21
24
  import getSubmitButtonOptions from './getSubmitButtonOptions';
22
25
  import getTemplate from './getTemplate';
@@ -33,13 +36,16 @@ import {
33
36
  errorId,
34
37
  examplesId,
35
38
  helpId,
39
+ optionalControlsId,
36
40
  optionId,
37
41
  titleId,
38
42
  } from './idGenerators';
39
43
  import isConstant from './isConstant';
40
44
  import isCustomWidget from './isCustomWidget';
41
45
  import isFixedItems from './isFixedItems';
46
+ import isFormDataAvailable from './isFormDataAvailable';
42
47
  import isObject from './isObject';
48
+ import isRootSchema from './isRootSchema';
43
49
  import labelValue from './labelValue';
44
50
  import localToUTC from './localToUTC';
45
51
  import lookupFromFormContext from './lookupFromFormContext';
@@ -54,16 +60,16 @@ import rangeSpec from './rangeSpec';
54
60
  import replaceStringParameters from './replaceStringParameters';
55
61
  import schemaRequiresTrueValue from './schemaRequiresTrueValue';
56
62
  import shouldRender from './shouldRender';
63
+ import shouldRenderOptionalField from './shouldRenderOptionalField';
57
64
  import toConstant from './toConstant';
58
65
  import toDateString from './toDateString';
59
66
  import toErrorList from './toErrorList';
60
67
  import toErrorSchema from './toErrorSchema';
68
+ import toFieldPathId from './toFieldPathId';
61
69
  import unwrapErrorHandler from './unwrapErrorHandler';
62
70
  import utcToLocal from './utcToLocal';
63
71
  import validationDataMerge from './validationDataMerge';
64
72
  import withIdRefPrefix from './withIdRefPrefix';
65
- import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator';
66
- import getChangedFields from './getChangedFields';
67
73
 
68
74
  export * from './types';
69
75
  export * from './enums';
@@ -115,13 +121,16 @@ export {
115
121
  isConstant,
116
122
  isCustomWidget,
117
123
  isFixedItems,
124
+ isFormDataAvailable,
118
125
  isObject,
126
+ isRootSchema,
119
127
  labelValue,
120
128
  localToUTC,
121
129
  lookupFromFormContext,
122
130
  mergeDefaultsWithFormData,
123
131
  mergeObjects,
124
132
  mergeSchemas,
133
+ optionalControlsId,
125
134
  optionId,
126
135
  optionsList,
127
136
  orderProperties,
@@ -130,15 +139,20 @@ export {
130
139
  rangeSpec,
131
140
  replaceStringParameters,
132
141
  schemaRequiresTrueValue,
142
+ shallowEquals,
133
143
  shouldRender,
144
+ shouldRenderOptionalField,
134
145
  sortedJSONStringify,
135
146
  titleId,
136
147
  toConstant,
137
148
  toDateString,
138
149
  toErrorList,
139
150
  toErrorSchema,
151
+ toFieldPathId,
140
152
  unwrapErrorHandler,
141
153
  utcToLocal,
142
154
  validationDataMerge,
143
155
  withIdRefPrefix,
144
156
  };
157
+
158
+ export type { ComponentUpdateStrategy } from './shouldRender';
@@ -0,0 +1,13 @@
1
+ import isNil from 'lodash/isNil';
2
+ import isEmpty from 'lodash/isEmpty';
3
+ import isObject from 'lodash/isObject';
4
+
5
+ /** Determines whether the given `formData` represents valid form data, such as a primitive type, an array, or a
6
+ * non-empty object.
7
+ *
8
+ * @param formData - The data to check
9
+ * @returns - True if `formData` is not undefined, null, a primitive type or an array or an empty object
10
+ */
11
+ export default function isFormDataAvailable<T = any>(formData?: T): boolean {
12
+ return !isNil(formData) && (!isObject(formData) || Array.isArray(formData) || !isEmpty(formData));
13
+ }
@@ -0,0 +1,30 @@
1
+ import isEqual from 'lodash/isEqual';
2
+
3
+ import { FormContextType, Registry, RJSFSchema, StrictRJSFSchema } from './types';
4
+ import { REF_KEY } from './constants';
5
+
6
+ /** Helper to check whether a JSON schema object is the root schema. The schema is a root schema with root `properties`
7
+ * key or a root `$ref` key. If the `schemaToCompare` has a root `oneOf` property, the function will
8
+ * return false. Else if `schemaToCompare` and `rootSchema` are the same object or equal, the function will return
9
+ * `true`. Else if the `rootSchema` has a $ref, it will be resolved using `schemaUtils.resolveSchema` utility. If the
10
+ * resolved schema matches the `schemaToCompare` the function will return `true`. Otherwise, it will return false.
11
+ *
12
+ * @param registry - The `Registry` used to get the `rootSchema` and `schemaUtils`
13
+ * @param schemaToCompare - The JSON schema object to check. If `schemaToCompare` is an root schema, the
14
+ * function will return true.
15
+ * @returns - Flag indicating whether the `schemaToCompare` is the root schema
16
+ */
17
+ export default function isRootSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
18
+ registry: Registry<T, S, F>,
19
+ schemaToCompare: S,
20
+ ): boolean {
21
+ const { rootSchema, schemaUtils } = registry;
22
+ if (isEqual(schemaToCompare, rootSchema)) {
23
+ return true;
24
+ }
25
+ if (REF_KEY in rootSchema) {
26
+ const resolvedSchema = schemaUtils.retrieveSchema(rootSchema);
27
+ return isEqual(schemaToCompare, resolvedSchema);
28
+ }
29
+ return false;
30
+ }
@@ -67,8 +67,22 @@ export default function mergeDefaultsWithFormData<T = any>(
67
67
  const keyValue = get(formData, key);
68
68
  const keyExistsInDefaults = isObject(defaults) && key in (defaults as GenericObjectType);
69
69
  const keyExistsInFormData = key in (formData as GenericObjectType);
70
+ const keyDefault = get(defaults, key) ?? {};
71
+ const defaultValueIsNestedObject = keyExistsInDefaults && Object.entries(keyDefault).some(([, v]) => isObject(v));
72
+
73
+ const keyDefaultIsObject = keyExistsInDefaults && isObject(get(defaults, key));
74
+ const keyHasFormDataObject = keyExistsInFormData && isObject(keyValue);
75
+
76
+ if (keyDefaultIsObject && keyHasFormDataObject && !defaultValueIsNestedObject) {
77
+ acc[key as keyof T] = {
78
+ ...get(defaults, key),
79
+ ...keyValue,
80
+ };
81
+ return acc;
82
+ }
83
+
70
84
  acc[key as keyof T] = mergeDefaultsWithFormData<T>(
71
- defaults ? get(defaults, key) : {},
85
+ get(defaults, key),
72
86
  keyValue,
73
87
  mergeExtraArrayDefaults,
74
88
  defaultSupercedesUndefined,
@@ -88,7 +102,7 @@ export default function mergeDefaultsWithFormData<T = any>(
88
102
  */
89
103
  if (
90
104
  (defaultSupercedesUndefined &&
91
- ((!isNil(defaults) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) ||
105
+ ((!(defaults === undefined) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) ||
92
106
  (overrideFormDataWithDefaults && !isNil(formData))
93
107
  ) {
94
108
  return defaults;
@@ -22,7 +22,7 @@ export const NOT_FOUND_SCHEMA = { title: '!@#$_UNKNOWN_$#@!' };
22
22
  *
23
23
  * @param validator - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs
24
24
  * @param rootSchema - The root schema that will be forwarded to all the APIs
25
- // * @param schema - The node within the JSON schema in which to search
25
+ * @param schema - The node within the JSON schema in which to search
26
26
  * @param path - The keys in the path to the desired field
27
27
  * @param [formData={}] - The form data that is used to determine which anyOf/oneOf option to descend
28
28
  * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas