@rjsf/core 6.0.0-beta.21 → 6.0.0-beta.22

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 (83) hide show
  1. package/dist/core.umd.js +1369 -1574
  2. package/dist/index.cjs +1438 -1646
  3. package/dist/index.cjs.map +3 -3
  4. package/dist/index.esm.js +1465 -1660
  5. package/dist/index.esm.js.map +3 -3
  6. package/lib/components/Form.d.ts +9 -4
  7. package/lib/components/Form.d.ts.map +1 -1
  8. package/lib/components/Form.js +48 -22
  9. package/lib/components/fields/ArrayField.d.ts +2 -197
  10. package/lib/components/fields/ArrayField.d.ts.map +1 -1
  11. package/lib/components/fields/ArrayField.js +518 -536
  12. package/lib/components/fields/BooleanField.js +2 -2
  13. package/lib/components/fields/LayoutGridField.d.ts +109 -191
  14. package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
  15. package/lib/components/fields/LayoutGridField.js +417 -444
  16. package/lib/components/fields/LayoutMultiSchemaField.d.ts.map +1 -1
  17. package/lib/components/fields/LayoutMultiSchemaField.js +2 -3
  18. package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
  19. package/lib/components/fields/MultiSchemaField.js +5 -3
  20. package/lib/components/fields/ObjectField.d.ts +2 -68
  21. package/lib/components/fields/ObjectField.d.ts.map +1 -1
  22. package/lib/components/fields/ObjectField.js +166 -168
  23. package/lib/components/fields/SchemaField.d.ts.map +1 -1
  24. package/lib/components/fields/SchemaField.js +31 -12
  25. package/lib/components/fields/StringField.js +2 -2
  26. package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts +3 -3
  27. package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts.map +1 -1
  28. package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +3 -8
  29. package/lib/components/templates/ArrayFieldItemTemplate.d.ts +3 -3
  30. package/lib/components/templates/ArrayFieldItemTemplate.d.ts.map +1 -1
  31. package/lib/components/templates/ArrayFieldItemTemplate.js +1 -1
  32. package/lib/components/templates/ArrayFieldTemplate.d.ts +1 -1
  33. package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
  34. package/lib/components/templates/ArrayFieldTemplate.js +2 -4
  35. package/lib/components/templates/BaseInputTemplate.js +2 -2
  36. package/lib/components/templates/ObjectFieldTemplate.js +2 -2
  37. package/lib/components/templates/WrapIfAdditionalTemplate.js +2 -2
  38. package/lib/components/widgets/CheckboxWidget.d.ts +1 -1
  39. package/lib/components/widgets/CheckboxWidget.d.ts.map +1 -1
  40. package/lib/components/widgets/CheckboxWidget.js +2 -2
  41. package/lib/components/widgets/CheckboxesWidget.d.ts +1 -1
  42. package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -1
  43. package/lib/components/widgets/CheckboxesWidget.js +2 -2
  44. package/lib/components/widgets/HiddenWidget.d.ts +1 -1
  45. package/lib/components/widgets/HiddenWidget.d.ts.map +1 -1
  46. package/lib/components/widgets/HiddenWidget.js +2 -2
  47. package/lib/components/widgets/RadioWidget.d.ts +1 -1
  48. package/lib/components/widgets/RadioWidget.d.ts.map +1 -1
  49. package/lib/components/widgets/RadioWidget.js +2 -2
  50. package/lib/components/widgets/RatingWidget.d.ts +1 -1
  51. package/lib/components/widgets/RatingWidget.d.ts.map +1 -1
  52. package/lib/components/widgets/RatingWidget.js +2 -2
  53. package/lib/components/widgets/SelectWidget.d.ts +1 -1
  54. package/lib/components/widgets/SelectWidget.d.ts.map +1 -1
  55. package/lib/components/widgets/SelectWidget.js +2 -2
  56. package/lib/components/widgets/TextareaWidget.d.ts +1 -1
  57. package/lib/components/widgets/TextareaWidget.d.ts.map +1 -1
  58. package/lib/components/widgets/TextareaWidget.js +2 -2
  59. package/lib/tsconfig.tsbuildinfo +1 -1
  60. package/package.json +5 -5
  61. package/src/components/Form.tsx +61 -26
  62. package/src/components/fields/ArrayField.tsx +849 -758
  63. package/src/components/fields/BooleanField.tsx +2 -2
  64. package/src/components/fields/LayoutGridField.tsx +613 -600
  65. package/src/components/fields/LayoutMultiSchemaField.tsx +4 -5
  66. package/src/components/fields/MultiSchemaField.tsx +30 -25
  67. package/src/components/fields/ObjectField.tsx +315 -242
  68. package/src/components/fields/OptionalDataControlsField.tsx +1 -1
  69. package/src/components/fields/SchemaField.tsx +43 -46
  70. package/src/components/fields/StringField.tsx +2 -2
  71. package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +11 -16
  72. package/src/components/templates/ArrayFieldItemTemplate.tsx +3 -3
  73. package/src/components/templates/ArrayFieldTemplate.tsx +2 -13
  74. package/src/components/templates/BaseInputTemplate.tsx +2 -2
  75. package/src/components/templates/ObjectFieldTemplate.tsx +2 -2
  76. package/src/components/templates/WrapIfAdditionalTemplate.tsx +4 -4
  77. package/src/components/widgets/CheckboxWidget.tsx +2 -1
  78. package/src/components/widgets/CheckboxesWidget.tsx +2 -1
  79. package/src/components/widgets/HiddenWidget.tsx +2 -1
  80. package/src/components/widgets/RadioWidget.tsx +2 -1
  81. package/src/components/widgets/RatingWidget.tsx +2 -1
  82. package/src/components/widgets/SelectWidget.tsx +2 -1
  83. package/src/components/widgets/TextareaWidget.tsx +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rjsf/core",
3
- "version": "6.0.0-beta.21",
3
+ "version": "6.0.0-beta.22",
4
4
  "description": "A simple React component capable of building HTML forms out of a JSON schema.",
5
5
  "scripts": {
6
6
  "compileReplacer": "tsc -p tsconfig.replacer.json && move-file lodashReplacer.js lodashReplacer.cjs",
@@ -66,7 +66,7 @@
66
66
  "node": ">=20"
67
67
  },
68
68
  "peerDependencies": {
69
- "@rjsf/utils": "^6.0.0-beta.21",
69
+ "@rjsf/utils": "^6.0.0-beta.22",
70
70
  "react": ">=18"
71
71
  },
72
72
  "dependencies": {
@@ -76,9 +76,9 @@
76
76
  "prop-types": "^15.8.1"
77
77
  },
78
78
  "devDependencies": {
79
- "@rjsf/snapshot-tests": "^6.0.0-beta.21",
80
- "@rjsf/utils": "^6.0.0-beta.21",
81
- "@rjsf/validator-ajv8": "^6.0.0-beta.21",
79
+ "@rjsf/snapshot-tests": "^6.0.0-beta.22",
80
+ "@rjsf/utils": "^6.0.0-beta.22",
81
+ "@rjsf/validator-ajv8": "^6.0.0-beta.22",
82
82
  "@testing-library/jest-dom": "^6.9.1",
83
83
  "@testing-library/react": "^16.3.0",
84
84
  "@testing-library/user-event": "^14.6.1",
@@ -42,6 +42,8 @@ import {
42
42
  DEFAULT_ID_PREFIX,
43
43
  GlobalFormOptions,
44
44
  ERRORS_KEY,
45
+ ID_KEY,
46
+ NameGeneratorFunction,
45
47
  } from '@rjsf/utils';
46
48
  import _cloneDeep from 'lodash/cloneDeep';
47
49
  import _get from 'lodash/get';
@@ -195,6 +197,9 @@ export interface FormProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
195
197
  * to put the second parameter before the first in its translation.
196
198
  */
197
199
  translateString?: Registry['translateString'];
200
+ /** Optional function to generate custom HTML `name` attributes for form fields.
201
+ */
202
+ nameGenerator?: NameGeneratorFunction;
198
203
  /** Optional configuration object with flags, if provided, allows users to override default form state behavior
199
204
  * Currently only affecting minItems on array fields and handling of setting defaults based on the value of
200
205
  * `emptyObjectFields`
@@ -269,24 +274,38 @@ export interface FormState<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
269
274
  retrievedSchema: S;
270
275
  /** Flag indicating whether the initial form defaults have been generated */
271
276
  initialDefaultsGenerated: boolean;
277
+ /** The registry (re)computed only when props changed */
278
+ registry: Registry<T, S, F>;
272
279
  }
273
280
 
274
281
  /** The event data passed when changes have been made to the form, includes everything from the `FormState` except
275
282
  * the schema validation errors. An additional `status` is added when returned from `onSubmit`
276
283
  */
277
284
  export interface IChangeEvent<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
278
- extends Omit<
285
+ extends Pick<
279
286
  FormState<T, S, F>,
280
- | 'schemaValidationErrors'
281
- | 'schemaValidationErrorSchema'
282
- | 'retrievedSchema'
283
- | 'customErrors'
284
- | 'initialDefaultsGenerated'
287
+ 'schema' | 'uiSchema' | 'fieldPathId' | 'schemaUtils' | 'formData' | 'edit' | 'errors' | 'errorSchema'
285
288
  > {
286
289
  /** The status of the form when submitted */
287
290
  status?: 'submitted';
288
291
  }
289
292
 
293
+ /** Converts the full `FormState` into the `IChangeEvent` version by picking out the public values
294
+ *
295
+ * @param state - The state of the form
296
+ * @param status - The status provided by the onSubmit
297
+ * @returns - The `IChangeEvent` for the state
298
+ */
299
+ function toIChangeEvent<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
300
+ state: FormState<T, S, F>,
301
+ status?: IChangeEvent['status'],
302
+ ): IChangeEvent<T, S, F> {
303
+ return {
304
+ ..._pick(state, ['schema', 'uiSchema', 'fieldPathId', 'schemaUtils', 'formData', 'edit', 'errors', 'errorSchema']),
305
+ ...(status !== undefined && { status }),
306
+ };
307
+ }
308
+
290
309
  /** The definition of a pending change that will be processed in the `onChange` handler
291
310
  */
292
311
  interface PendingChange<T> {
@@ -330,7 +349,7 @@ export default class Form<
330
349
 
331
350
  this.state = this.getStateFromProps(props, props.formData);
332
351
  if (this.props.onChange && !deepEquals(this.state.formData, this.props.formData)) {
333
- this.props.onChange(this.state);
352
+ this.props.onChange(toIChangeEvent(this.state));
334
353
  }
335
354
  this.formElement = createRef();
336
355
  }
@@ -413,7 +432,7 @@ export default class Form<
413
432
  !deepEquals(nextState.formData, prevState.formData) &&
414
433
  this.props.onChange
415
434
  ) {
416
- this.props.onChange(nextState);
435
+ this.props.onChange(toIChangeEvent(nextState));
417
436
  }
418
437
  this.setState(nextState);
419
438
  }
@@ -545,7 +564,14 @@ export default class Form<
545
564
  errorSchema = mergedErrors.errorSchema;
546
565
  }
547
566
 
548
- const fieldPathId = toFieldPathId('', this.getGlobalFormOptions(this.props));
567
+ // Only store a new registry when the props cause a different one to be created
568
+ const newRegistry = this.getRegistry(props, rootSchema, schemaUtils);
569
+ const registry = deepEquals(state.registry, newRegistry) ? state.registry : newRegistry;
570
+ // Only compute a new `fieldPathId` when the `idPrefix` is different than the existing fieldPathId's ID_KEY
571
+ const fieldPathId =
572
+ state.fieldPathId && state.fieldPathId?.[ID_KEY] === registry.globalFormOptions.idPrefix
573
+ ? state.fieldPathId
574
+ : toFieldPathId('', registry.globalFormOptions);
549
575
  const nextState: FormState<T, S, F> = {
550
576
  schemaUtils,
551
577
  schema: rootSchema,
@@ -559,6 +585,7 @@ export default class Form<
559
585
  schemaValidationErrorSchema,
560
586
  retrievedSchema: _retrievedSchema,
561
587
  initialDefaultsGenerated: true,
588
+ registry,
562
589
  };
563
590
  return nextState;
564
591
  }
@@ -835,7 +862,11 @@ export default class Form<
835
862
  customErrors = new ErrorSchemaBuilder<T>();
836
863
  }
837
864
  if (isRootPath) {
838
- customErrors.setErrors(_get(newErrorSchema, ERRORS_KEY, ''));
865
+ const errors = _get(newErrorSchema, ERRORS_KEY);
866
+ if (errors) {
867
+ // only set errors when there are some
868
+ customErrors.setErrors(errors);
869
+ }
839
870
  } else {
840
871
  _set(customErrors.ErrorSchema, path, newErrorSchema);
841
872
  }
@@ -867,7 +898,7 @@ export default class Form<
867
898
  }
868
899
  this.setState(state as FormState<T, S, F>, () => {
869
900
  if (onChange) {
870
- onChange({ ...this.state, ...state }, id);
901
+ onChange(toIChangeEvent({ ...this.state, ...state }), id);
871
902
  }
872
903
  // Now remove the change we just completed and call this again
873
904
  this.pendingChanges.shift();
@@ -909,7 +940,7 @@ export default class Form<
909
940
  customErrors: undefined,
910
941
  } as FormState<T, S, F>;
911
942
 
912
- this.setState(state, () => onChange && onChange({ ...this.state, ...state }));
943
+ this.setState(state, () => onChange && onChange(toIChangeEvent({ ...this.state, ...state })));
913
944
  };
914
945
 
915
946
  /** Callback function to handle when a field on the form is blurred. Calls the `onBlur` callback for the `Form` if it
@@ -975,7 +1006,7 @@ export default class Form<
975
1006
  },
976
1007
  () => {
977
1008
  if (onSubmit) {
978
- onSubmit({ ...this.state, formData: newFormData, status: 'submitted' }, event);
1009
+ onSubmit(toIChangeEvent({ ...this.state, formData: newFormData }, 'submitted'), event);
979
1010
  }
980
1011
  },
981
1012
  );
@@ -994,34 +1025,39 @@ export default class Form<
994
1025
  experimental_componentUpdateStrategy,
995
1026
  idSeparator = DEFAULT_ID_SEPARATOR,
996
1027
  idPrefix = DEFAULT_ID_PREFIX,
1028
+ nameGenerator,
997
1029
  } = props;
998
1030
  const rootFieldId = uiSchema['ui:rootFieldId'];
999
1031
  // Omit any options that are undefined or null
1000
- return { idPrefix: rootFieldId || idPrefix, idSeparator, experimental_componentUpdateStrategy };
1032
+ return {
1033
+ idPrefix: rootFieldId || idPrefix,
1034
+ idSeparator,
1035
+ ...(experimental_componentUpdateStrategy !== undefined && { experimental_componentUpdateStrategy }),
1036
+ ...(nameGenerator !== undefined && { nameGenerator }),
1037
+ };
1001
1038
  }
1002
1039
 
1003
- /** Returns the registry for the form */
1004
- getRegistry(): Registry<T, S, F> {
1005
- const { translateString: customTranslateString, uiSchema = {} } = this.props;
1006
- const { schema, schemaUtils } = this.state;
1040
+ /** Computed the registry for the form using the given `props`, `schema` and `schemaUtils` */
1041
+ getRegistry(props: FormProps<T, S, F>, schema: S, schemaUtils: SchemaUtilsType<T, S, F>): Registry<T, S, F> {
1042
+ const { translateString: customTranslateString, uiSchema = {} } = props;
1007
1043
  const { fields, templates, widgets, formContext, translateString } = getDefaultRegistry<T, S, F>();
1008
1044
  return {
1009
- fields: { ...fields, ...this.props.fields },
1045
+ fields: { ...fields, ...props.fields },
1010
1046
  templates: {
1011
1047
  ...templates,
1012
- ...this.props.templates,
1048
+ ...props.templates,
1013
1049
  ButtonTemplates: {
1014
1050
  ...templates.ButtonTemplates,
1015
- ...this.props.templates?.ButtonTemplates,
1051
+ ...props.templates?.ButtonTemplates,
1016
1052
  },
1017
1053
  },
1018
- widgets: { ...widgets, ...this.props.widgets },
1054
+ widgets: { ...widgets, ...props.widgets },
1019
1055
  rootSchema: schema,
1020
- formContext: this.props.formContext || formContext,
1056
+ formContext: props.formContext || formContext,
1021
1057
  schemaUtils,
1022
1058
  translateString: customTranslateString || translateString,
1023
1059
  globalUiOptions: uiSchema[UI_GLOBAL_OPTIONS_KEY],
1024
- globalFormOptions: this.getGlobalFormOptions(this.props),
1060
+ globalFormOptions: this.getGlobalFormOptions(props),
1025
1061
  };
1026
1062
  }
1027
1063
 
@@ -1162,8 +1198,7 @@ export default class Form<
1162
1198
  _internalFormWrapper,
1163
1199
  } = this.props;
1164
1200
 
1165
- const { schema, uiSchema, formData, errorSchema, fieldPathId } = this.state;
1166
- const registry = this.getRegistry();
1201
+ const { schema, uiSchema, formData, errorSchema, fieldPathId, registry } = this.state;
1167
1202
  const { SchemaField: _SchemaField } = registry.fields;
1168
1203
  const { SubmitButton } = registry.templates.ButtonTemplates;
1169
1204
  // The `semantic-ui` and `material-ui` themes have `_internalFormWrapper`s that take an `as` prop that is the