@rjsf/core 6.0.0-beta.2 → 6.0.0-beta.20

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 (93) hide show
  1. package/dist/core.umd.js +469 -360
  2. package/dist/{index.js → index.cjs} +640 -519
  3. package/dist/index.cjs.map +7 -0
  4. package/dist/index.esm.js +706 -566
  5. package/dist/index.esm.js.map +4 -4
  6. package/lib/components/Form.d.ts +66 -16
  7. package/lib/components/Form.d.ts.map +1 -1
  8. package/lib/components/Form.js +138 -59
  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 +92 -59
  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 -53
  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 +18 -25
  28. package/lib/components/fields/SchemaField.d.ts.map +1 -1
  29. package/lib/components/fields/SchemaField.js +17 -17
  30. package/lib/components/fields/StringField.d.ts.map +1 -1
  31. package/lib/components/fields/StringField.js +7 -2
  32. package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -1
  33. package/lib/components/templates/ArrayFieldDescriptionTemplate.js +3 -3
  34. package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +2 -2
  35. package/lib/components/templates/ArrayFieldTemplate.js +3 -3
  36. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
  37. package/lib/components/templates/ArrayFieldTitleTemplate.js +3 -3
  38. package/lib/components/templates/FieldErrorTemplate.js +2 -2
  39. package/lib/components/templates/FieldHelpTemplate.js +2 -2
  40. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +8 -0
  41. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -0
  42. package/lib/components/templates/MultiSchemaFieldTemplate.js +10 -0
  43. package/lib/components/templates/ObjectFieldTemplate.js +2 -2
  44. package/lib/components/templates/UnsupportedField.js +3 -3
  45. package/lib/components/templates/index.d.ts.map +1 -1
  46. package/lib/components/templates/index.js +2 -0
  47. package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
  48. package/lib/components/widgets/AltDateWidget.js +15 -18
  49. package/lib/components/widgets/CheckboxesWidget.js +2 -2
  50. package/lib/getDefaultRegistry.d.ts.map +1 -1
  51. package/lib/getDefaultRegistry.js +2 -1
  52. package/lib/getTestRegistry.d.ts +5 -0
  53. package/lib/getTestRegistry.d.ts.map +1 -0
  54. package/lib/getTestRegistry.js +19 -0
  55. package/lib/index.d.ts +2 -1
  56. package/lib/index.d.ts.map +1 -1
  57. package/lib/index.js +2 -1
  58. package/lib/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +18 -19
  60. package/src/components/Form.tsx +183 -73
  61. package/src/components/fields/ArrayField.tsx +99 -67
  62. package/src/components/fields/BooleanField.tsx +12 -3
  63. package/src/components/fields/LayoutGridField.tsx +95 -82
  64. package/src/components/fields/LayoutHeaderField.tsx +3 -3
  65. package/src/components/fields/LayoutMultiSchemaField.tsx +5 -5
  66. package/src/components/fields/MultiSchemaField.tsx +51 -35
  67. package/src/components/fields/NullField.tsx +3 -3
  68. package/src/components/fields/NumberField.tsx +11 -3
  69. package/src/components/fields/ObjectField.tsx +19 -36
  70. package/src/components/fields/SchemaField.tsx +24 -30
  71. package/src/components/fields/StringField.tsx +12 -3
  72. package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +3 -3
  73. package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +5 -5
  74. package/src/components/templates/ArrayFieldTemplate.tsx +5 -5
  75. package/src/components/templates/ArrayFieldTitleTemplate.tsx +3 -3
  76. package/src/components/templates/BaseInputTemplate.tsx +3 -3
  77. package/src/components/templates/FieldErrorTemplate.tsx +2 -2
  78. package/src/components/templates/FieldHelpTemplate.tsx +2 -2
  79. package/src/components/templates/MultiSchemaFieldTemplate.tsx +20 -0
  80. package/src/components/templates/ObjectFieldTemplate.tsx +5 -5
  81. package/src/components/templates/UnsupportedField.tsx +3 -3
  82. package/src/components/templates/WrapIfAdditionalTemplate.tsx +1 -1
  83. package/src/components/templates/index.ts +2 -0
  84. package/src/components/widgets/AltDateWidget.tsx +21 -23
  85. package/src/components/widgets/CheckboxWidget.tsx +2 -2
  86. package/src/components/widgets/CheckboxesWidget.tsx +3 -3
  87. package/src/components/widgets/RadioWidget.tsx +1 -1
  88. package/src/components/widgets/SelectWidget.tsx +1 -1
  89. package/src/components/widgets/TextareaWidget.tsx +1 -1
  90. package/src/getDefaultRegistry.ts +10 -1
  91. package/src/getTestRegistry.tsx +34 -0
  92. package/src/index.ts +2 -1
  93. 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,18 +13,18 @@ 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
29
  import cloneDeep from 'lodash/cloneDeep';
28
30
  import each from 'lodash/each';
@@ -38,6 +40,7 @@ import isObject from 'lodash/isObject';
38
40
  import isPlainObject from 'lodash/isPlainObject';
39
41
  import isString from 'lodash/isString';
40
42
  import isUndefined from 'lodash/isUndefined';
43
+ import last from 'lodash/last';
41
44
  import set from 'lodash/set';
42
45
 
43
46
  /** The enumeration of the three different Layout GridTemplate type values
@@ -101,10 +104,6 @@ export const LAYOUT_GRID_UI_OPTION = 'layoutGrid';
101
104
  */
102
105
  export const LAYOUT_GRID_OPTION = `ui:${LAYOUT_GRID_UI_OPTION}`;
103
106
 
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
107
  /** Type used to return options list and whether it has a discriminator */
109
108
  type OneOfOptionsInfoType<S extends StrictRJSFSchema = RJSFSchema> = { options: S[]; hasDiscriminator: boolean };
110
109
 
@@ -133,6 +132,15 @@ function getNonNullishValue<T = unknown>(value?: T, fallback?: T): T | undefined
133
132
  return value ?? fallback;
134
133
  }
135
134
 
135
+ /** Detects if a `str` is made up entirely of numeric characters
136
+ *
137
+ * @param str - The string to check to see if it is a numeric index
138
+ * @return - True if the string consists entirely of numeric characters
139
+ */
140
+ function isNumericIndex(str: string) {
141
+ return /^\d+?$/.test(str); // Matches positive integers
142
+ }
143
+
136
144
  /** The `LayoutGridField` will render a schema, uiSchema and formData combination out into a GridTemplate in the shape
137
145
  * described in the uiSchema. To define the grid to use to render the elements within a field in the schema, provide in
138
146
  * the uiSchema for that field the object contained under a `ui:layoutGrid` element. E.g. (as a JSON object):
@@ -386,7 +394,7 @@ export default class LayoutGridField<
386
394
  schemaReadonly?: boolean,
387
395
  forceReadonly?: boolean,
388
396
  ) {
389
- const globalUiOptions = get(uiSchema, [UI_GLOBAL_OPTIONS], {});
397
+ const globalUiOptions = get(uiSchema, [UI_GLOBAL_OPTIONS_KEY], {});
390
398
  const localUiSchema = get(uiSchema, field);
391
399
  const localUiOptions = { ...get(localUiSchema, [UI_OPTIONS_KEY], {}), ...uiProps, ...globalUiOptions };
392
400
  const fieldUiSchema = { ...localUiSchema };
@@ -395,7 +403,7 @@ export default class LayoutGridField<
395
403
  }
396
404
  if (!isEmpty(globalUiOptions)) {
397
405
  // 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);
406
+ set(fieldUiSchema, [UI_GLOBAL_OPTIONS_KEY], globalUiOptions);
399
407
  }
400
408
  let { readonly: uiReadonly } = getUiOptions<T, S, F>(fieldUiSchema);
401
409
  if (forceReadonly === true || (isUndefined(uiReadonly) && schemaReadonly === true)) {
@@ -478,25 +486,46 @@ export default class LayoutGridField<
478
486
  return { children: children as LayoutGridSchemaType[], gridProps };
479
487
  }
480
488
 
481
- /** Generates an idSchema for the `schema` using `@rjsf`'s `toIdSchema` util, passing the `baseIdSchema`'s `$id` value
482
- * as the id prefix.
489
+ /** Computes the `rawSchema` and `fieldPathId` for a `schema` and a `potentialIndex`. If the `schema` is of type array,
490
+ * has an `ITEMS_KEY` element and `potentialIndex` represents a numeric value, the element at `ITEMS_KEY` is checked
491
+ * to see if it is an array. If it is AND the `potentialIndex`th element is available, it is used as the `rawSchema`,
492
+ * otherwise the last value of the element is used. If it is not, then the element is used as the `rawSchema`. In
493
+ * either case, an `fieldPathId` is computed for the array index. If the `schema` does not represent an array or the
494
+ * `potentialIndex` is not a numeric value, then `rawSchema` is returned as undefined and given `fieldPathId` is returned
495
+ * as is.
483
496
  *
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`
497
+ * @param schema - The schema to generate the fieldPathId for
498
+ * @param fieldPathId - The FieldPathId for the schema
499
+ * @param potentialIndex - A string containing a potential index
500
+ * @returns - An object containing the `rawSchema` and `fieldPathId` of an array item, otherwise an undefined `rawSchema`
490
501
  */
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);
502
+ static computeArraySchemasIfPresent<S extends StrictRJSFSchema = RJSFSchema>(
503
+ schema: S | undefined,
504
+ fieldPathId: FieldPathId,
505
+ potentialIndex: string,
506
+ ): {
507
+ rawSchema?: S;
508
+ fieldPathId: FieldPathId;
509
+ } {
510
+ let rawSchema: S | undefined;
511
+ if (isNumericIndex(potentialIndex) && schema && schema?.type === 'array' && has(schema, ITEMS_KEY)) {
512
+ const index = Number(potentialIndex);
513
+ const items = schema[ITEMS_KEY];
514
+ if (Array.isArray(items)) {
515
+ if (index > items.length) {
516
+ rawSchema = last(items) as S;
517
+ } else {
518
+ rawSchema = items[index] as S;
519
+ }
520
+ } else {
521
+ rawSchema = items as S;
522
+ }
523
+ fieldPathId = {
524
+ [ID_KEY]: fieldPathId[ID_KEY],
525
+ path: [...fieldPathId.path.slice(0, fieldPathId.path.length - 1), index],
526
+ };
527
+ }
528
+ return { rawSchema, fieldPathId };
500
529
  }
501
530
 
502
531
  /** Given a `dottedPath` to a field in the `initialSchema`, iterate through each individual path in the schema until
@@ -504,31 +533,30 @@ export default class LayoutGridField<
504
533
  * element in the path. If the leaf schema element happens to be a oneOf/anyOf then also return the oneOf/anyOf as
505
534
  * `options`.
506
535
  *
507
- * @param schemaUtils - The `SchemaUtilsType` used to call `retrieveSchema`
536
+ * @param registry - The registry
508
537
  * @param dottedPath - The dotted-path to the field for which to get the schema
509
538
  * @param initialSchema - The initial schema to start the search from
510
539
  * @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
540
+ * @param initialFieldIdPath - The initial fieldPathId to start the search from
513
541
  * @returns - An object containing the destination schema, isRequired and isReadonly flags for the field and options
514
542
  * info if a oneOf/anyOf
515
543
  */
516
544
  static getSchemaDetailsForField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
517
- schemaUtils: SchemaUtilsType<T, S, F>,
545
+ registry: Registry<T, S, F>,
518
546
  dottedPath: string,
519
547
  initialSchema: S,
520
548
  formData: FieldProps<T, S, F>['formData'],
521
- initialIdSchema: IdSchema<T>,
522
- idSeparator?: string,
549
+ initialFieldIdPath: FieldPathId,
523
550
  ): {
524
551
  schema?: S;
525
552
  isRequired: boolean;
526
553
  isReadonly?: boolean;
527
554
  optionsInfo?: OneOfOptionsInfoType<S>;
528
- idSchema: IdSchema<T>;
555
+ fieldPathId: FieldPathId;
529
556
  } {
557
+ const { schemaUtils, globalFormOptions } = registry;
530
558
  let rawSchema: S = initialSchema;
531
- let idSchema = initialIdSchema;
559
+ let fieldPathId = initialFieldIdPath;
532
560
  const parts: string[] = dottedPath.split('.');
533
561
  const leafPath: string | undefined = parts.pop(); // pop off the last element in the list as the leaf
534
562
  let schema: S | undefined = schemaUtils.retrieveSchema(rawSchema, formData); // always returns an object
@@ -538,24 +566,18 @@ export default class LayoutGridField<
538
566
  // For all the remaining path parts
539
567
  parts.forEach((part) => {
540
568
  // dive into the properties of the current schema (when it exists) and get the schema for the next part
569
+ fieldPathId = toFieldPathId(part, globalFormOptions, fieldPathId);
541
570
  if (has(schema, PROPERTIES_KEY)) {
542
571
  rawSchema = get(schema, [PROPERTIES_KEY, part], {}) as S;
543
- idSchema = get(idSchema, part, {}) as IdSchema<T>;
544
572
  } else if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
545
573
  const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
546
574
  // When the schema represents a oneOf/anyOf, find the selected schema for it and grab the inner part
547
575
  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
576
  rawSchema = get(selectedSchema, [PROPERTIES_KEY, part], {}) as S;
556
- idSchema = get(selectedIdSchema, part, {}) as IdSchema<T>;
557
577
  } else {
558
- rawSchema = {} as S;
578
+ const result = LayoutGridField.computeArraySchemasIfPresent<S>(schema, fieldPathId, part);
579
+ rawSchema = result.rawSchema ?? ({} as S);
580
+ fieldPathId = result.fieldPathId;
559
581
  }
560
582
  // Now drill into the innerData for the part, returning an empty object by default if it doesn't exist
561
583
  innerData = get(innerData, part, {}) as T;
@@ -576,16 +598,19 @@ export default class LayoutGridField<
576
598
  const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
577
599
  // Grab the selected schema for the oneOf/anyOf value for the leafPath using the innerData
578
600
  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
601
  }
602
+ fieldPathId = toFieldPathId(leafPath, globalFormOptions, fieldPathId);
583
603
  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>;
604
+ const result = LayoutGridField.computeArraySchemasIfPresent<S>(schema, fieldPathId, leafPath);
605
+ if (result.rawSchema) {
606
+ schema = result.rawSchema;
607
+ fieldPathId = result.fieldPathId;
608
+ } else {
609
+ // Now grab the schema from the leafPath of the current schema properties
610
+ schema = get(schema, [PROPERTIES_KEY, leafPath]) as S | undefined;
611
+ // Resolve any `$ref`s for the current schema
612
+ schema = schema ? schemaUtils.retrieveSchema(schema) : schema;
613
+ }
589
614
  isReadonly = getNonNullishValue(schema?.readOnly, isReadonly);
590
615
  if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
591
616
  const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
@@ -595,7 +620,7 @@ export default class LayoutGridField<
595
620
  }
596
621
  }
597
622
 
598
- return { schema, isRequired, isReadonly, optionsInfo, idSchema };
623
+ return { schema, isRequired, isReadonly, optionsInfo, fieldPathId };
599
624
  }
600
625
 
601
626
  /** Gets the custom render component from the `render`, by either determining that it is either already a function or
@@ -640,7 +665,7 @@ export default class LayoutGridField<
640
665
  if (isString(gridSchema) || isUndefined(gridSchema)) {
641
666
  name = gridSchema ?? '';
642
667
  } else {
643
- const { name: innerName, render, ...innerProps } = gridSchema;
668
+ const { name: innerName = '', render, ...innerProps } = gridSchema;
644
669
  name = innerName;
645
670
  uiProps = innerProps;
646
671
  if (!isEmpty(uiProps)) {
@@ -676,19 +701,17 @@ export default class LayoutGridField<
676
701
  * elements, they will then be passed on to the `onChange` handler of the `LayoutFieldGrid`.
677
702
  *
678
703
  * @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
704
+ * @returns - The `onChange` handling function for the `dottedPath` field of the `schemaType` type
680
705
  */
681
706
  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));
707
+ return (value: T | undefined, path: FieldPathList, errSchema?: ErrorSchema<T>, id?: string) => {
708
+ const { onChange, errorSchema } = this.props;
685
709
  let newErrorSchema = errorSchema;
686
710
  if (errSchema && errorSchema) {
687
711
  newErrorSchema = cloneDeep(errorSchema);
688
712
  set(newErrorSchema, dottedPath, errSchema);
689
713
  }
690
- set(newFormData as object, dottedPath, value);
691
- onChange(newFormData, newErrorSchema, id);
714
+ onChange(value, path, newErrorSchema, id);
692
715
  };
693
716
  };
694
717
 
@@ -792,21 +815,21 @@ export default class LayoutGridField<
792
815
  );
793
816
  }
794
817
 
795
- /** Iterates through all the `childrenLayoutGridSchema`, rendering a nested `LayoutGridField` for each item in the
818
+ /** Iterates through all the `childrenLayoutGrfieldPathId`, rendering a nested `LayoutGridField` for each item in the
796
819
  * list, passing all the props for the current `LayoutGridField` along, updating the `schema` by calling
797
820
  * `retrieveSchema()` on it to resolve any `$ref`s. In addition to the updated `schema`, each item in
798
- * `childrenLayoutGridSchema` is passed as `layoutGridSchema`.
821
+ * `childrenLayoutGrfieldPathId` is passed as `layoutGridSchema`.
799
822
  *
800
- * @param childrenLayoutGridSchema - The list of strings or objects that represents the configurations for the
823
+ * @param childrenLayoutGrfieldPathId - The list of strings or objects that represents the configurations for the
801
824
  * children fields
802
825
  * @returns - The nested `LayoutGridField`s
803
826
  */
804
- renderChildren(childrenLayoutGridSchema: LayoutGridSchemaType[]) {
827
+ renderChildren(childrenLayoutGrfieldPathId: LayoutGridSchemaType[]) {
805
828
  const { registry, schema: rawSchema, formData } = this.props;
806
829
  const { schemaUtils } = registry;
807
830
  const schema = schemaUtils.retrieveSchema(rawSchema, formData);
808
831
 
809
- return childrenLayoutGridSchema.map((layoutGridSchema) => (
832
+ return childrenLayoutGrfieldPathId.map((layoutGridSchema) => (
810
833
  <LayoutGridField<T, S, F>
811
834
  {...this.props}
812
835
  key={`layoutGrid-${hashObject(layoutGridSchema)}`}
@@ -822,7 +845,7 @@ export default class LayoutGridField<
822
845
  * specified props for that component. If `name` exists, we take the name, the initial & root schemas and the formData
823
846
  * and get the destination schema, is required state and optional oneOf/anyOf options for it. If the destination
824
847
  * 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
848
+ * `uiSchema`, `errorSchema`, `fieldPathId` and `formData` drilled down to the dotted-path field, spreading any other
826
849
  * props from `gridSchema` into the `ui:options`. If the destination schema located without any oneOf/anyOf options,
827
850
  * then a `SchemaField` will be rendered with the same props as mentioned in the previous sentence. If no destination
828
851
  * schema was located, but a custom render component was found, then it will be rendered with many of the non-event
@@ -836,17 +859,16 @@ export default class LayoutGridField<
836
859
  schema: initialSchema,
837
860
  uiSchema,
838
861
  errorSchema,
839
- idSchema,
862
+ fieldPathId,
840
863
  onBlur,
841
864
  onFocus,
842
865
  formData,
843
866
  readonly,
844
867
  registry,
845
- idSeparator,
846
868
  layoutGridSchema, // Used to pull this out of otherProps since we don't want to pass it through
847
869
  ...otherProps
848
870
  } = this.props;
849
- const { fields, schemaUtils } = registry;
871
+ const { fields } = registry;
850
872
  const { SchemaField, LayoutMultiSchemaField } = fields;
851
873
  const uiComponentProps = LayoutGridField.computeUIComponentPropsFromGridSchema(registry, gridSchema);
852
874
  if (uiComponentProps.rendered) {
@@ -858,15 +880,8 @@ export default class LayoutGridField<
858
880
  isRequired,
859
881
  isReadonly,
860
882
  optionsInfo,
861
- idSchema: fieldIdSchema,
862
- } = LayoutGridField.getSchemaDetailsForField<T, S, F>(
863
- schemaUtils,
864
- name,
865
- initialSchema,
866
- formData,
867
- idSchema,
868
- idSeparator,
869
- );
883
+ fieldPathId: fieldIdSchema,
884
+ } = LayoutGridField.getSchemaDetailsForField<T, S, F>(registry, name, initialSchema, formData, fieldPathId);
870
885
 
871
886
  if (schema) {
872
887
  const Field = optionsInfo?.hasDiscriminator ? LayoutMultiSchemaField : SchemaField;
@@ -897,8 +912,7 @@ export default class LayoutGridField<
897
912
  schema={schema}
898
913
  uiSchema={fieldUiSchema}
899
914
  errorSchema={get(errorSchema, name)}
900
- idSchema={fieldIdSchema}
901
- idSeparator={idSeparator}
915
+ fieldPathId={fieldIdSchema}
902
916
  formData={get(formData, name)}
903
917
  onChange={this.onFieldChange(name)}
904
918
  onBlur={onBlur}
@@ -921,8 +935,7 @@ export default class LayoutGridField<
921
935
  errorSchema={errorSchema}
922
936
  uiSchema={uiSchema}
923
937
  schema={initialSchema}
924
- idSchema={idSchema}
925
- idSeparator={idSeparator}
938
+ fieldPathId={fieldPathId}
926
939
  onBlur={onBlur}
927
940
  onFocus={onFocus}
928
941
  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 { name, formData, onChange } = props;
13
13
  useEffect(() => {
14
14
  if (formData === undefined) {
15
- onChange(null as unknown as T);
15
+ onChange(null as unknown as T, [name]);
16
16
  }
17
- }, [formData, onChange]);
17
+ }, [name, 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
  );