@rjsf/core 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 (114) hide show
  1. package/dist/core.umd.js +705 -471
  2. package/dist/{index.js → index.cjs} +1094 -844
  3. package/dist/index.cjs.map +7 -0
  4. package/dist/index.esm.js +1053 -774
  5. package/dist/index.esm.js.map +4 -4
  6. package/lib/components/Form.d.ts +88 -23
  7. package/lib/components/Form.d.ts.map +1 -1
  8. package/lib/components/Form.js +213 -151
  9. package/lib/components/fields/ArrayField.d.ts +17 -7
  10. package/lib/components/fields/ArrayField.d.ts.map +1 -1
  11. package/lib/components/fields/ArrayField.js +116 -70
  12. package/lib/components/fields/BooleanField.d.ts.map +1 -1
  13. package/lib/components/fields/BooleanField.js +7 -2
  14. package/lib/components/fields/LayoutGridField.d.ts +27 -25
  15. package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
  16. package/lib/components/fields/LayoutGridField.js +83 -59
  17. package/lib/components/fields/LayoutHeaderField.d.ts +1 -1
  18. package/lib/components/fields/LayoutHeaderField.js +3 -3
  19. package/lib/components/fields/LayoutMultiSchemaField.js +6 -5
  20. package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
  21. package/lib/components/fields/MultiSchemaField.js +13 -9
  22. package/lib/components/fields/NullField.js +3 -3
  23. package/lib/components/fields/NumberField.d.ts.map +1 -1
  24. package/lib/components/fields/NumberField.js +3 -3
  25. package/lib/components/fields/ObjectField.d.ts +3 -3
  26. package/lib/components/fields/ObjectField.d.ts.map +1 -1
  27. package/lib/components/fields/ObjectField.js +34 -34
  28. package/lib/components/fields/OptionalDataControlsField.d.ts +8 -0
  29. package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -0
  30. package/lib/components/fields/OptionalDataControlsField.js +43 -0
  31. package/lib/components/fields/SchemaField.d.ts.map +1 -1
  32. package/lib/components/fields/SchemaField.js +17 -17
  33. package/lib/components/fields/StringField.d.ts.map +1 -1
  34. package/lib/components/fields/StringField.js +7 -2
  35. package/lib/components/fields/index.d.ts.map +1 -1
  36. package/lib/components/fields/index.js +2 -0
  37. package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -1
  38. package/lib/components/templates/ArrayFieldDescriptionTemplate.js +3 -3
  39. package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +2 -2
  40. package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
  41. package/lib/components/templates/ArrayFieldTemplate.js +4 -3
  42. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
  43. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
  44. package/lib/components/templates/ArrayFieldTitleTemplate.js +3 -3
  45. package/lib/components/templates/ButtonTemplates/AddButton.d.ts +1 -1
  46. package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -1
  47. package/lib/components/templates/ButtonTemplates/AddButton.js +2 -2
  48. package/lib/components/templates/FieldErrorTemplate.js +2 -2
  49. package/lib/components/templates/FieldHelpTemplate.js +2 -2
  50. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +8 -0
  51. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -0
  52. package/lib/components/templates/MultiSchemaFieldTemplate.js +10 -0
  53. package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
  54. package/lib/components/templates/ObjectFieldTemplate.js +3 -2
  55. package/lib/components/templates/OptionalDataControlsTemplate.d.ts +11 -0
  56. package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -0
  57. package/lib/components/templates/OptionalDataControlsTemplate.js +20 -0
  58. package/lib/components/templates/TitleField.d.ts.map +1 -1
  59. package/lib/components/templates/TitleField.js +2 -2
  60. package/lib/components/templates/UnsupportedField.js +3 -3
  61. package/lib/components/templates/index.d.ts.map +1 -1
  62. package/lib/components/templates/index.js +4 -0
  63. package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
  64. package/lib/components/widgets/AltDateWidget.js +15 -18
  65. package/lib/components/widgets/CheckboxesWidget.js +2 -2
  66. package/lib/getDefaultRegistry.d.ts.map +1 -1
  67. package/lib/getDefaultRegistry.js +2 -1
  68. package/lib/getTestRegistry.d.ts +5 -0
  69. package/lib/getTestRegistry.d.ts.map +1 -0
  70. package/lib/getTestRegistry.js +19 -0
  71. package/lib/index.d.ts +2 -1
  72. package/lib/index.d.ts.map +1 -1
  73. package/lib/index.js +2 -1
  74. package/lib/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +18 -19
  76. package/src/components/Form.tsx +306 -177
  77. package/src/components/fields/ArrayField.tsx +127 -80
  78. package/src/components/fields/BooleanField.tsx +12 -3
  79. package/src/components/fields/LayoutGridField.tsx +95 -88
  80. package/src/components/fields/LayoutHeaderField.tsx +3 -3
  81. package/src/components/fields/LayoutMultiSchemaField.tsx +5 -5
  82. package/src/components/fields/MultiSchemaField.tsx +51 -35
  83. package/src/components/fields/NullField.tsx +3 -3
  84. package/src/components/fields/NumberField.tsx +11 -3
  85. package/src/components/fields/ObjectField.tsx +47 -53
  86. package/src/components/fields/OptionalDataControlsField.tsx +84 -0
  87. package/src/components/fields/SchemaField.tsx +24 -30
  88. package/src/components/fields/StringField.tsx +12 -3
  89. package/src/components/fields/index.ts +2 -0
  90. package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +3 -3
  91. package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +5 -5
  92. package/src/components/templates/ArrayFieldTemplate.tsx +9 -5
  93. package/src/components/templates/ArrayFieldTitleTemplate.tsx +4 -3
  94. package/src/components/templates/BaseInputTemplate.tsx +3 -3
  95. package/src/components/templates/ButtonTemplates/AddButton.tsx +2 -0
  96. package/src/components/templates/FieldErrorTemplate.tsx +2 -2
  97. package/src/components/templates/FieldHelpTemplate.tsx +2 -2
  98. package/src/components/templates/MultiSchemaFieldTemplate.tsx +20 -0
  99. package/src/components/templates/ObjectFieldTemplate.tsx +10 -5
  100. package/src/components/templates/OptionalDataControlsTemplate.tsx +43 -0
  101. package/src/components/templates/TitleField.tsx +6 -1
  102. package/src/components/templates/UnsupportedField.tsx +3 -3
  103. package/src/components/templates/WrapIfAdditionalTemplate.tsx +1 -1
  104. package/src/components/templates/index.ts +4 -0
  105. package/src/components/widgets/AltDateWidget.tsx +21 -23
  106. package/src/components/widgets/CheckboxWidget.tsx +2 -2
  107. package/src/components/widgets/CheckboxesWidget.tsx +3 -3
  108. package/src/components/widgets/RadioWidget.tsx +1 -1
  109. package/src/components/widgets/SelectWidget.tsx +1 -1
  110. package/src/components/widgets/TextareaWidget.tsx +1 -1
  111. package/src/getDefaultRegistry.ts +10 -1
  112. package/src/getTestRegistry.tsx +34 -0
  113. package/src/index.ts +2 -1
  114. package/dist/index.js.map +0 -7
@@ -3,6 +3,8 @@ import {
3
3
  ANY_OF_KEY,
4
4
  ErrorSchema,
5
5
  FieldProps,
6
+ FieldPathId,
7
+ FieldPathList,
6
8
  FormContextType,
7
9
  GenericObjectType,
8
10
  getDiscriminatorFieldFromSchema,
@@ -11,20 +13,19 @@ import {
11
13
  getUiOptions,
12
14
  hashObject,
13
15
  ID_KEY,
14
- IdSchema,
15
16
  lookupFromFormContext,
16
- mergeObjects,
17
17
  ONE_OF_KEY,
18
18
  PROPERTIES_KEY,
19
19
  READONLY_KEY,
20
20
  RJSFSchema,
21
21
  Registry,
22
- SchemaUtilsType,
23
22
  StrictRJSFSchema,
23
+ toFieldPathId,
24
24
  UI_OPTIONS_KEY,
25
+ UI_GLOBAL_OPTIONS_KEY,
25
26
  UiSchema,
27
+ ITEMS_KEY,
26
28
  } from '@rjsf/utils';
27
- import cloneDeep from 'lodash/cloneDeep';
28
29
  import each from 'lodash/each';
29
30
  import flatten from 'lodash/flatten';
30
31
  import get from 'lodash/get';
@@ -38,6 +39,7 @@ import isObject from 'lodash/isObject';
38
39
  import isPlainObject from 'lodash/isPlainObject';
39
40
  import isString from 'lodash/isString';
40
41
  import isUndefined from 'lodash/isUndefined';
42
+ import last from 'lodash/last';
41
43
  import set from 'lodash/set';
42
44
 
43
45
  /** The enumeration of the three different Layout GridTemplate type values
@@ -101,10 +103,6 @@ export const LAYOUT_GRID_UI_OPTION = 'layoutGrid';
101
103
  */
102
104
  export const LAYOUT_GRID_OPTION = `ui:${LAYOUT_GRID_UI_OPTION}`;
103
105
 
104
- /** The constant representing the global UI Options object potentially contained within the `uiSchema`
105
- */
106
- export const UI_GLOBAL_OPTIONS = 'ui:global_options';
107
-
108
106
  /** Type used to return options list and whether it has a discriminator */
109
107
  type OneOfOptionsInfoType<S extends StrictRJSFSchema = RJSFSchema> = { options: S[]; hasDiscriminator: boolean };
110
108
 
@@ -133,6 +131,15 @@ function getNonNullishValue<T = unknown>(value?: T, fallback?: T): T | undefined
133
131
  return value ?? fallback;
134
132
  }
135
133
 
134
+ /** Detects if a `str` is made up entirely of numeric characters
135
+ *
136
+ * @param str - The string to check to see if it is a numeric index
137
+ * @return - True if the string consists entirely of numeric characters
138
+ */
139
+ function isNumericIndex(str: string) {
140
+ return /^\d+?$/.test(str); // Matches positive integers
141
+ }
142
+
136
143
  /** The `LayoutGridField` will render a schema, uiSchema and formData combination out into a GridTemplate in the shape
137
144
  * described in the uiSchema. To define the grid to use to render the elements within a field in the schema, provide in
138
145
  * the uiSchema for that field the object contained under a `ui:layoutGrid` element. E.g. (as a JSON object):
@@ -386,7 +393,7 @@ export default class LayoutGridField<
386
393
  schemaReadonly?: boolean,
387
394
  forceReadonly?: boolean,
388
395
  ) {
389
- const globalUiOptions = get(uiSchema, [UI_GLOBAL_OPTIONS], {});
396
+ const globalUiOptions = get(uiSchema, [UI_GLOBAL_OPTIONS_KEY], {});
390
397
  const localUiSchema = get(uiSchema, field);
391
398
  const localUiOptions = { ...get(localUiSchema, [UI_OPTIONS_KEY], {}), ...uiProps, ...globalUiOptions };
392
399
  const fieldUiSchema = { ...localUiSchema };
@@ -395,7 +402,7 @@ export default class LayoutGridField<
395
402
  }
396
403
  if (!isEmpty(globalUiOptions)) {
397
404
  // pass the global uiOptions down to the field uiSchema so that they can be applied to all nested fields
398
- set(fieldUiSchema, [UI_GLOBAL_OPTIONS], globalUiOptions);
405
+ set(fieldUiSchema, [UI_GLOBAL_OPTIONS_KEY], globalUiOptions);
399
406
  }
400
407
  let { readonly: uiReadonly } = getUiOptions<T, S, F>(fieldUiSchema);
401
408
  if (forceReadonly === true || (isUndefined(uiReadonly) && schemaReadonly === true)) {
@@ -478,25 +485,46 @@ export default class LayoutGridField<
478
485
  return { children: children as LayoutGridSchemaType[], gridProps };
479
486
  }
480
487
 
481
- /** Generates an idSchema for the `schema` using `@rjsf`'s `toIdSchema` util, passing the `baseIdSchema`'s `$id` value
482
- * as the id prefix.
488
+ /** Computes the `rawSchema` and `fieldPathId` for a `schema` and a `potentialIndex`. If the `schema` is of type array,
489
+ * has an `ITEMS_KEY` element and `potentialIndex` represents a numeric value, the element at `ITEMS_KEY` is checked
490
+ * to see if it is an array. If it is AND the `potentialIndex`th element is available, it is used as the `rawSchema`,
491
+ * otherwise the last value of the element is used. If it is not, then the element is used as the `rawSchema`. In
492
+ * either case, an `fieldPathId` is computed for the array index. If the `schema` does not represent an array or the
493
+ * `potentialIndex` is not a numeric value, then `rawSchema` is returned as undefined and given `fieldPathId` is returned
494
+ * as is.
483
495
  *
484
- * @param schemaUtils - The `SchemaUtilsType` used to call `toIdSchema`
485
- * @param schema - The schema to generate the idSchema for
486
- * @param baseIdSchema - The IdSchema for the base
487
- * @param formData - The formData to pass the `toIdSchema`
488
- * @param [idSeparator] - The param to pass into the `toIdSchema` util which will use it to join the `idSchema` paths
489
- * @returns - The generated `idSchema` for the `schema`
496
+ * @param schema - The schema to generate the fieldPathId for
497
+ * @param fieldPathId - The FieldPathId for the schema
498
+ * @param potentialIndex - A string containing a potential index
499
+ * @returns - An object containing the `rawSchema` and `fieldPathId` of an array item, otherwise an undefined `rawSchema`
490
500
  */
491
- static getIdSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
492
- schemaUtils: SchemaUtilsType<T, S, F>,
493
- baseIdSchema: IdSchema<T>,
494
- formData: FieldProps['formData'],
495
- schema: S = {} as S,
496
- idSeparator?: string,
497
- ): FieldProps<T, S, F>['idSchema'] {
498
- const baseId = get(baseIdSchema, ID_KEY);
499
- return schemaUtils.toIdSchema(schema, baseId, formData, baseId, idSeparator);
501
+ static computeArraySchemasIfPresent<S extends StrictRJSFSchema = RJSFSchema>(
502
+ schema: S | undefined,
503
+ fieldPathId: FieldPathId,
504
+ potentialIndex: string,
505
+ ): {
506
+ rawSchema?: S;
507
+ fieldPathId: FieldPathId;
508
+ } {
509
+ let rawSchema: S | undefined;
510
+ if (isNumericIndex(potentialIndex) && schema && schema?.type === 'array' && has(schema, ITEMS_KEY)) {
511
+ const index = Number(potentialIndex);
512
+ const items = schema[ITEMS_KEY];
513
+ if (Array.isArray(items)) {
514
+ if (index > items.length) {
515
+ rawSchema = last(items) as S;
516
+ } else {
517
+ rawSchema = items[index] as S;
518
+ }
519
+ } else {
520
+ rawSchema = items as S;
521
+ }
522
+ fieldPathId = {
523
+ [ID_KEY]: fieldPathId[ID_KEY],
524
+ path: [...fieldPathId.path.slice(0, fieldPathId.path.length - 1), index],
525
+ };
526
+ }
527
+ return { rawSchema, fieldPathId };
500
528
  }
501
529
 
502
530
  /** Given a `dottedPath` to a field in the `initialSchema`, iterate through each individual path in the schema until
@@ -504,31 +532,30 @@ export default class LayoutGridField<
504
532
  * element in the path. If the leaf schema element happens to be a oneOf/anyOf then also return the oneOf/anyOf as
505
533
  * `options`.
506
534
  *
507
- * @param schemaUtils - The `SchemaUtilsType` used to call `retrieveSchema`
535
+ * @param registry - The registry
508
536
  * @param dottedPath - The dotted-path to the field for which to get the schema
509
537
  * @param initialSchema - The initial schema to start the search from
510
538
  * @param formData - The formData, useful for resolving a oneOf/anyOf selection in the path hierarchy
511
- * @param initialIdSchema - The initial idSchema to start the search from
512
- * @param [idSeparator] - The param to pass into the `toIdSchema` util which will use it to join the `idSchema` paths
539
+ * @param initialFieldIdPath - The initial fieldPathId to start the search from
513
540
  * @returns - An object containing the destination schema, isRequired and isReadonly flags for the field and options
514
541
  * info if a oneOf/anyOf
515
542
  */
516
543
  static getSchemaDetailsForField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
517
- schemaUtils: SchemaUtilsType<T, S, F>,
544
+ registry: Registry<T, S, F>,
518
545
  dottedPath: string,
519
546
  initialSchema: S,
520
547
  formData: FieldProps<T, S, F>['formData'],
521
- initialIdSchema: IdSchema<T>,
522
- idSeparator?: string,
548
+ initialFieldIdPath: FieldPathId,
523
549
  ): {
524
550
  schema?: S;
525
551
  isRequired: boolean;
526
552
  isReadonly?: boolean;
527
553
  optionsInfo?: OneOfOptionsInfoType<S>;
528
- idSchema: IdSchema<T>;
554
+ fieldPathId: FieldPathId;
529
555
  } {
556
+ const { schemaUtils, globalFormOptions } = registry;
530
557
  let rawSchema: S = initialSchema;
531
- let idSchema = initialIdSchema;
558
+ let fieldPathId = initialFieldIdPath;
532
559
  const parts: string[] = dottedPath.split('.');
533
560
  const leafPath: string | undefined = parts.pop(); // pop off the last element in the list as the leaf
534
561
  let schema: S | undefined = schemaUtils.retrieveSchema(rawSchema, formData); // always returns an object
@@ -538,24 +565,18 @@ export default class LayoutGridField<
538
565
  // For all the remaining path parts
539
566
  parts.forEach((part) => {
540
567
  // dive into the properties of the current schema (when it exists) and get the schema for the next part
568
+ fieldPathId = toFieldPathId(part, globalFormOptions, fieldPathId);
541
569
  if (has(schema, PROPERTIES_KEY)) {
542
570
  rawSchema = get(schema, [PROPERTIES_KEY, part], {}) as S;
543
- idSchema = get(idSchema, part, {}) as IdSchema<T>;
544
571
  } else if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
545
572
  const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
546
573
  // When the schema represents a oneOf/anyOf, find the selected schema for it and grab the inner part
547
574
  const selectedSchema = schemaUtils.findSelectedOptionInXxxOf(schema, part, xxx, innerData);
548
- const selectedIdSchema = LayoutGridField.getIdSchema<T, S, F>(
549
- schemaUtils,
550
- idSchema,
551
- formData,
552
- selectedSchema,
553
- idSeparator,
554
- );
555
575
  rawSchema = get(selectedSchema, [PROPERTIES_KEY, part], {}) as S;
556
- idSchema = get(selectedIdSchema, part, {}) as IdSchema<T>;
557
576
  } else {
558
- rawSchema = {} as S;
577
+ const result = LayoutGridField.computeArraySchemasIfPresent<S>(schema, fieldPathId, part);
578
+ rawSchema = result.rawSchema ?? ({} as S);
579
+ fieldPathId = result.fieldPathId;
559
580
  }
560
581
  // Now drill into the innerData for the part, returning an empty object by default if it doesn't exist
561
582
  innerData = get(innerData, part, {}) as T;
@@ -576,16 +597,19 @@ export default class LayoutGridField<
576
597
  const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
577
598
  // Grab the selected schema for the oneOf/anyOf value for the leafPath using the innerData
578
599
  schema = schemaUtils.findSelectedOptionInXxxOf(schema, leafPath, xxx, innerData);
579
- // Generate the idSchema for the oneOf/anyOf value then merge with the existing `idSchema`
580
- const rawIdSchema = LayoutGridField.getIdSchema<T, S, F>(schemaUtils, idSchema, formData, schema, idSeparator);
581
- idSchema = mergeObjects(rawIdSchema, idSchema) as IdSchema<T>;
582
600
  }
601
+ fieldPathId = toFieldPathId(leafPath, globalFormOptions, fieldPathId);
583
602
  isRequired = schema !== undefined && Array.isArray(schema.required) && includes(schema.required, leafPath);
584
- // Now grab the schema from the leafPath of the current schema properties
585
- schema = get(schema, [PROPERTIES_KEY, leafPath]) as S | undefined;
586
- // Resolve any `$ref`s for the current schema
587
- schema = schema ? schemaUtils.retrieveSchema(schema) : schema;
588
- idSchema = get(idSchema, leafPath, {}) as IdSchema<T>;
603
+ const result = LayoutGridField.computeArraySchemasIfPresent<S>(schema, fieldPathId, leafPath);
604
+ if (result.rawSchema) {
605
+ schema = result.rawSchema;
606
+ fieldPathId = result.fieldPathId;
607
+ } else {
608
+ // Now grab the schema from the leafPath of the current schema properties
609
+ schema = get(schema, [PROPERTIES_KEY, leafPath]) as S | undefined;
610
+ // Resolve any `$ref`s for the current schema
611
+ schema = schema ? schemaUtils.retrieveSchema(schema) : schema;
612
+ }
589
613
  isReadonly = getNonNullishValue(schema?.readOnly, isReadonly);
590
614
  if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
591
615
  const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
@@ -595,7 +619,7 @@ export default class LayoutGridField<
595
619
  }
596
620
  }
597
621
 
598
- return { schema, isRequired, isReadonly, optionsInfo, idSchema };
622
+ return { schema, isRequired, isReadonly, optionsInfo, fieldPathId };
599
623
  }
600
624
 
601
625
  /** Gets the custom render component from the `render`, by either determining that it is either already a function or
@@ -640,7 +664,7 @@ export default class LayoutGridField<
640
664
  if (isString(gridSchema) || isUndefined(gridSchema)) {
641
665
  name = gridSchema ?? '';
642
666
  } else {
643
- const { name: innerName, render, ...innerProps } = gridSchema;
667
+ const { name: innerName = '', render, ...innerProps } = gridSchema;
644
668
  name = innerName;
645
669
  uiProps = innerProps;
646
670
  if (!isEmpty(uiProps)) {
@@ -676,19 +700,12 @@ export default class LayoutGridField<
676
700
  * elements, they will then be passed on to the `onChange` handler of the `LayoutFieldGrid`.
677
701
  *
678
702
  * @param dottedPath - The dotted-path to the field for which to generate the onChange handler
679
- * @returns - The `onChange` handling function for the `dottedPath` field
703
+ * @returns - The `onChange` handling function for the `dottedPath` field of the `schemaType` type
680
704
  */
681
705
  onFieldChange = (dottedPath: string) => {
682
- return (value: unknown, errSchema?: ErrorSchema<T>, id?: string) => {
683
- const { onChange, errorSchema, formData } = this.props;
684
- const newFormData = cloneDeep(formData || ({} as T));
685
- let newErrorSchema = errorSchema;
686
- if (errSchema && errorSchema) {
687
- newErrorSchema = cloneDeep(errorSchema);
688
- set(newErrorSchema, dottedPath, errSchema);
689
- }
690
- set(newFormData as object, dottedPath, value);
691
- onChange(newFormData, newErrorSchema, id);
706
+ return (value: T | undefined, path: FieldPathList, errSchema?: ErrorSchema<T>, id?: string) => {
707
+ const { onChange } = this.props;
708
+ onChange(value, path, errSchema, id);
692
709
  };
693
710
  };
694
711
 
@@ -792,21 +809,21 @@ export default class LayoutGridField<
792
809
  );
793
810
  }
794
811
 
795
- /** Iterates through all the `childrenLayoutGridSchema`, rendering a nested `LayoutGridField` for each item in the
812
+ /** Iterates through all the `childrenLayoutGrfieldPathId`, rendering a nested `LayoutGridField` for each item in the
796
813
  * list, passing all the props for the current `LayoutGridField` along, updating the `schema` by calling
797
814
  * `retrieveSchema()` on it to resolve any `$ref`s. In addition to the updated `schema`, each item in
798
- * `childrenLayoutGridSchema` is passed as `layoutGridSchema`.
815
+ * `childrenLayoutGrfieldPathId` is passed as `layoutGridSchema`.
799
816
  *
800
- * @param childrenLayoutGridSchema - The list of strings or objects that represents the configurations for the
817
+ * @param childrenLayoutGrfieldPathId - The list of strings or objects that represents the configurations for the
801
818
  * children fields
802
819
  * @returns - The nested `LayoutGridField`s
803
820
  */
804
- renderChildren(childrenLayoutGridSchema: LayoutGridSchemaType[]) {
821
+ renderChildren(childrenLayoutGrfieldPathId: LayoutGridSchemaType[]) {
805
822
  const { registry, schema: rawSchema, formData } = this.props;
806
823
  const { schemaUtils } = registry;
807
824
  const schema = schemaUtils.retrieveSchema(rawSchema, formData);
808
825
 
809
- return childrenLayoutGridSchema.map((layoutGridSchema) => (
826
+ return childrenLayoutGrfieldPathId.map((layoutGridSchema) => (
810
827
  <LayoutGridField<T, S, F>
811
828
  {...this.props}
812
829
  key={`layoutGrid-${hashObject(layoutGridSchema)}`}
@@ -822,7 +839,7 @@ export default class LayoutGridField<
822
839
  * specified props for that component. If `name` exists, we take the name, the initial & root schemas and the formData
823
840
  * and get the destination schema, is required state and optional oneOf/anyOf options for it. If the destination
824
841
  * schema was located along with oneOf/anyOf options then a `LayoutMultiSchemaField` will be rendered with the
825
- * `uiSchema`, `errorSchema`, `idSchema` and `formData` drilled down to the dotted-path field, spreading any other
842
+ * `uiSchema`, `errorSchema`, `fieldPathId` and `formData` drilled down to the dotted-path field, spreading any other
826
843
  * props from `gridSchema` into the `ui:options`. If the destination schema located without any oneOf/anyOf options,
827
844
  * then a `SchemaField` will be rendered with the same props as mentioned in the previous sentence. If no destination
828
845
  * schema was located, but a custom render component was found, then it will be rendered with many of the non-event
@@ -836,17 +853,16 @@ export default class LayoutGridField<
836
853
  schema: initialSchema,
837
854
  uiSchema,
838
855
  errorSchema,
839
- idSchema,
856
+ fieldPathId,
840
857
  onBlur,
841
858
  onFocus,
842
859
  formData,
843
860
  readonly,
844
861
  registry,
845
- idSeparator,
846
862
  layoutGridSchema, // Used to pull this out of otherProps since we don't want to pass it through
847
863
  ...otherProps
848
864
  } = this.props;
849
- const { fields, schemaUtils } = registry;
865
+ const { fields } = registry;
850
866
  const { SchemaField, LayoutMultiSchemaField } = fields;
851
867
  const uiComponentProps = LayoutGridField.computeUIComponentPropsFromGridSchema(registry, gridSchema);
852
868
  if (uiComponentProps.rendered) {
@@ -858,15 +874,8 @@ export default class LayoutGridField<
858
874
  isRequired,
859
875
  isReadonly,
860
876
  optionsInfo,
861
- idSchema: fieldIdSchema,
862
- } = LayoutGridField.getSchemaDetailsForField<T, S, F>(
863
- schemaUtils,
864
- name,
865
- initialSchema,
866
- formData,
867
- idSchema,
868
- idSeparator,
869
- );
877
+ fieldPathId: fieldIdSchema,
878
+ } = LayoutGridField.getSchemaDetailsForField<T, S, F>(registry, name, initialSchema, formData, fieldPathId);
870
879
 
871
880
  if (schema) {
872
881
  const Field = optionsInfo?.hasDiscriminator ? LayoutMultiSchemaField : SchemaField;
@@ -897,8 +906,7 @@ export default class LayoutGridField<
897
906
  schema={schema}
898
907
  uiSchema={fieldUiSchema}
899
908
  errorSchema={get(errorSchema, name)}
900
- idSchema={fieldIdSchema}
901
- idSeparator={idSeparator}
909
+ fieldPathId={fieldIdSchema}
902
910
  formData={get(formData, name)}
903
911
  onChange={this.onFieldChange(name)}
904
912
  onBlur={onBlur}
@@ -921,8 +929,7 @@ export default class LayoutGridField<
921
929
  errorSchema={errorSchema}
922
930
  uiSchema={uiSchema}
923
931
  schema={initialSchema}
924
- idSchema={idSchema}
925
- idSeparator={idSeparator}
932
+ fieldPathId={fieldPathId}
926
933
  onBlur={onBlur}
927
934
  onFocus={onFocus}
928
935
  registry={registry}
@@ -9,7 +9,7 @@ import {
9
9
  TemplatesType,
10
10
  } from '@rjsf/utils';
11
11
 
12
- /** The `LayoutHeaderField` component renders a `TitleFieldTemplate` with an `id` derived from the `idSchema`
12
+ /** The `LayoutHeaderField` component renders a `TitleFieldTemplate` with an `id` derived from the `fieldPathId`
13
13
  * and whether it is `required` from the props. The `title` is derived from the props as follows:
14
14
  * - If there is a title in the `uiSchema`, it is displayed
15
15
  * - Else, if there is an explicit `title` passed in the props, it is displayed
@@ -23,7 +23,7 @@ export default function LayoutHeaderField<
23
23
  S extends StrictRJSFSchema = RJSFSchema,
24
24
  F extends FormContextType = any,
25
25
  >(props: FieldProps<T, S, F>) {
26
- const { idSchema, title, schema, uiSchema, required, registry, name } = props;
26
+ const { fieldPathId, title, schema, uiSchema, required, registry, name } = props;
27
27
  const options = getUiOptions<T, S, F>(uiSchema, registry.globalUiOptions);
28
28
  const { title: uiTitle } = options;
29
29
  const { title: schemaTitle } = schema;
@@ -38,7 +38,7 @@ export default function LayoutHeaderField<
38
38
  );
39
39
  return (
40
40
  <TitleFieldTemplate
41
- id={titleId<T>(idSchema)}
41
+ id={titleId(fieldPathId)}
42
42
  title={fieldTitle}
43
43
  required={required}
44
44
  schema={schema}
@@ -96,7 +96,7 @@ export default function LayoutMultiSchemaField<
96
96
  baseType,
97
97
  disabled = false,
98
98
  formData,
99
- idSchema,
99
+ fieldPathId,
100
100
  onBlur,
101
101
  onChange,
102
102
  options,
@@ -113,7 +113,7 @@ export default function LayoutMultiSchemaField<
113
113
  } = props;
114
114
  const { widgets, schemaUtils, globalUiOptions } = registry;
115
115
  const [enumOptions, setEnumOptions] = useState(computeEnumOptions(schema, options, schemaUtils, uiSchema, formData)!);
116
- const id = get(idSchema, ID_KEY);
116
+ const id = get(fieldPathId, ID_KEY);
117
117
  const discriminator = getDiscriminatorFieldFromSchema(schema);
118
118
  const FieldErrorTemplate = getTemplate<'FieldErrorTemplate', T, S, F>('FieldErrorTemplate', registry, options);
119
119
  const FieldTemplate = getTemplate<'FieldTemplate', T, S, F>('FieldTemplate', registry, options);
@@ -171,14 +171,15 @@ export default function LayoutMultiSchemaField<
171
171
  if (newFormData) {
172
172
  set(newFormData, selectorField, opt);
173
173
  }
174
- onChange(newFormData, undefined, id);
174
+ // Pass the component name in the path
175
+ onChange(newFormData, fieldPathId.path, undefined, id);
175
176
  };
176
177
 
177
178
  // filtering the options based on the type of widget because `selectField` does not recognize the `convertOther` prop
178
179
  const widgetOptions = { enumOptions, ...uiOptions };
179
180
  const errors =
180
181
  !hideFieldError && rawErrors.length > 0 ? (
181
- <FieldErrorTemplate idSchema={idSchema} schema={schema} errors={rawErrors} registry={registry} />
182
+ <FieldErrorTemplate fieldPathId={fieldPathId} schema={schema} errors={rawErrors} registry={registry} />
182
183
  ) : undefined;
183
184
  const ignored = (value: string) => noop;
184
185
 
@@ -189,7 +190,6 @@ export default function LayoutMultiSchemaField<
189
190
  label={(title || schema.title) ?? ''}
190
191
  disabled={disabled || (Array.isArray(enumOptions) && isEmpty(enumOptions))}
191
192
  uiSchema={uiSchema}
192
- formContext={formContext}
193
193
  required={required}
194
194
  readonly={!!readonly}
195
195
  registry={registry}
@@ -9,6 +9,7 @@ import {
9
9
  FieldProps,
10
10
  FormContextType,
11
11
  getDiscriminatorFieldFromSchema,
12
+ getTemplate,
12
13
  getUiOptions,
13
14
  getWidget,
14
15
  mergeSchemas,
@@ -64,7 +65,7 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
64
65
  * @param prevState - The previous `AnyOfFieldState` for this template
65
66
  */
66
67
  componentDidUpdate(prevProps: Readonly<FieldProps<T, S, F>>, prevState: Readonly<AnyOfFieldState>) {
67
- const { formData, options, idSchema } = this.props;
68
+ const { formData, options, fieldPathId } = this.props;
68
69
  const { selectedOption } = this.state;
69
70
  let newState = this.state;
70
71
  if (!deepEquals(prevProps.options, options)) {
@@ -75,7 +76,7 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
75
76
  const retrievedOptions = options.map((opt: S) => schemaUtils.retrieveSchema(opt, formData));
76
77
  newState = { selectedOption, retrievedOptions };
77
78
  }
78
- if (!deepEquals(formData, prevProps.formData) && idSchema.$id === prevProps.idSchema.$id) {
79
+ if (!deepEquals(formData, prevProps.formData) && fieldPathId.$id === prevProps.fieldPathId.$id) {
79
80
  const { retrievedOptions } = newState;
80
81
  const matchingOption = this.getMatchingOption(selectedOption, formData, retrievedOptions);
81
82
 
@@ -113,7 +114,7 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
113
114
  */
114
115
  onOptionChange = (option?: string) => {
115
116
  const { selectedOption, retrievedOptions } = this.state;
116
- const { formData, onChange, registry } = this.props;
117
+ const { formData, onChange, registry, fieldPathId } = this.props;
117
118
  const { schemaUtils } = registry;
118
119
  const intOption = option !== undefined ? parseInt(option, 10) : -1;
119
120
  if (intOption === selectedOption) {
@@ -130,13 +131,13 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
130
131
  }
131
132
 
132
133
  this.setState({ selectedOption: intOption }, () => {
133
- onChange(newFormData, undefined, this.getFieldId());
134
+ onChange(newFormData, fieldPathId.path, undefined, this.getFieldId());
134
135
  });
135
136
  };
136
137
 
137
138
  getFieldId() {
138
- const { idSchema, schema } = this.props;
139
- return `${idSchema.$id}${schema.oneOf ? '__oneof_select' : '__anyof_select'}`;
139
+ const { fieldPathId, schema } = this.props;
140
+ return `${fieldPathId.$id}${schema.oneOf ? '__oneof_select' : '__anyof_select'}`;
140
141
  }
141
142
 
142
143
  /** Renders the `AnyOfField` selector along with a `SchemaField` for the value of the `formData`
@@ -157,6 +158,12 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
157
158
 
158
159
  const { widgets, fields, translateString, globalUiOptions, schemaUtils } = registry;
159
160
  const { SchemaField: _SchemaField } = fields;
161
+ const MultiSchemaFieldTemplate = getTemplate<'MultiSchemaFieldTemplate', T, S, F>(
162
+ 'MultiSchemaFieldTemplate',
163
+ registry,
164
+ globalUiOptions,
165
+ );
166
+
160
167
  const { selectedOption, retrievedOptions } = this.state;
161
168
  const {
162
169
  widget = 'select',
@@ -215,36 +222,45 @@ class AnyOfField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends For
215
222
  };
216
223
  });
217
224
 
225
+ const selector = (
226
+ <Widget
227
+ id={this.getFieldId()}
228
+ name={`${name}${schema.oneOf ? '__oneof_select' : '__anyof_select'}`}
229
+ schema={{ type: 'number', default: 0 } as S}
230
+ onChange={this.onOptionChange}
231
+ onBlur={onBlur}
232
+ onFocus={onFocus}
233
+ disabled={disabled || isEmpty(enumOptions)}
234
+ multiple={false}
235
+ rawErrors={rawErrors}
236
+ errorSchema={fieldErrorSchema}
237
+ value={selectedOption >= 0 ? selectedOption : undefined}
238
+ options={{ enumOptions, ...uiOptions }}
239
+ registry={registry}
240
+ formContext={formContext}
241
+ placeholder={placeholder}
242
+ autocomplete={autocomplete}
243
+ autofocus={autofocus}
244
+ label={title ?? name}
245
+ hideLabel={!displayLabel}
246
+ readonly={readonly}
247
+ />
248
+ );
249
+
250
+ const optionsSchemaField =
251
+ (optionSchema && optionSchema.type !== 'null' && (
252
+ <_SchemaField {...this.props} schema={optionSchema} uiSchema={optionUiSchema} />
253
+ )) ||
254
+ null;
255
+
218
256
  return (
219
- <div className='panel panel-default panel-body'>
220
- <div className='form-group'>
221
- <Widget
222
- id={this.getFieldId()}
223
- name={`${name}${schema.oneOf ? '__oneof_select' : '__anyof_select'}`}
224
- schema={{ type: 'number', default: 0 } as S}
225
- onChange={this.onOptionChange}
226
- onBlur={onBlur}
227
- onFocus={onFocus}
228
- disabled={disabled || isEmpty(enumOptions)}
229
- multiple={false}
230
- rawErrors={rawErrors}
231
- errorSchema={fieldErrorSchema}
232
- value={selectedOption >= 0 ? selectedOption : undefined}
233
- options={{ enumOptions, ...uiOptions }}
234
- registry={registry}
235
- formContext={formContext}
236
- placeholder={placeholder}
237
- autocomplete={autocomplete}
238
- autofocus={autofocus}
239
- label={title ?? name}
240
- hideLabel={!displayLabel}
241
- readonly={readonly}
242
- />
243
- </div>
244
- {optionSchema && optionSchema.type !== 'null' && (
245
- <_SchemaField {...this.props} schema={optionSchema} uiSchema={optionUiSchema} />
246
- )}
247
- </div>
257
+ <MultiSchemaFieldTemplate
258
+ schema={schema}
259
+ registry={registry}
260
+ uiSchema={uiSchema}
261
+ selector={selector}
262
+ optionSchemaField={optionsSchemaField}
263
+ />
248
264
  );
249
265
  }
250
266
  }
@@ -9,12 +9,12 @@ import { FieldProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf
9
9
  function NullField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
10
10
  props: FieldProps<T, S, F>,
11
11
  ) {
12
- const { formData, onChange } = props;
12
+ const { formData, onChange, fieldPathId } = props;
13
13
  useEffect(() => {
14
14
  if (formData === undefined) {
15
- onChange(null as unknown as T);
15
+ onChange(null as unknown as T, fieldPathId.path);
16
16
  }
17
- }, [formData, onChange]);
17
+ }, [fieldPathId, formData, onChange]);
18
18
 
19
19
  return null;
20
20
  }
@@ -1,5 +1,13 @@
1
1
  import { useState, useCallback } from 'react';
2
- import { asNumber, ErrorSchema, FieldProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
2
+ import {
3
+ asNumber,
4
+ ErrorSchema,
5
+ FieldPathList,
6
+ FieldProps,
7
+ FormContextType,
8
+ RJSFSchema,
9
+ StrictRJSFSchema,
10
+ } from '@rjsf/utils';
3
11
 
4
12
  // Matches a string that ends in a . character, optionally followed by a sequence of
5
13
  // digits followed by any number of 0 characters up until the end of the line.
@@ -44,7 +52,7 @@ function NumberField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
44
52
  * @param value - The current value for the change occurring
45
53
  */
46
54
  const handleChange = useCallback(
47
- (value: FieldProps<T, S, F>['value'], errorSchema?: ErrorSchema<T>, id?: string) => {
55
+ (value: FieldProps<T, S, F>['value'], path: FieldPathList, errorSchema?: ErrorSchema<T>, id?: string) => {
48
56
  // Cache the original value in component state
49
57
  setLastValue(value);
50
58
 
@@ -62,7 +70,7 @@ function NumberField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
62
70
  ? asNumber(value.replace(trailingCharMatcher, ''))
63
71
  : asNumber(value);
64
72
 
65
- onChange(processed as unknown as T, errorSchema, id);
73
+ onChange(processed as unknown as T, path, errorSchema, id);
66
74
  },
67
75
  [onChange],
68
76
  );