@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,11 +3,13 @@ import {
3
3
  getTemplate,
4
4
  getUiOptions,
5
5
  orderProperties,
6
+ shouldRenderOptionalField,
7
+ toFieldPathId,
6
8
  ErrorSchema,
9
+ FieldPathList,
7
10
  FieldProps,
8
11
  FormContextType,
9
12
  GenericObjectType,
10
- IdSchema,
11
13
  RJSFSchema,
12
14
  StrictRJSFSchema,
13
15
  TranslatableString,
@@ -16,6 +18,7 @@ import {
16
18
  REF_KEY,
17
19
  ANY_OF_KEY,
18
20
  ONE_OF_KEY,
21
+ isFormDataAvailable,
19
22
  } from '@rjsf/utils';
20
23
  import Markdown from 'markdown-to-jsx';
21
24
  import get from 'lodash/get';
@@ -66,8 +69,8 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
66
69
  * @returns - The onPropertyChange callback for the `name` property
67
70
  */
68
71
  onPropertyChange = (name: string, addedByAdditionalProperties = false) => {
69
- return (value: T | undefined, newErrorSchema?: ErrorSchema<T>, id?: string) => {
70
- const { formData, onChange, errorSchema } = this.props;
72
+ return (value: T | undefined, path: FieldPathList, newErrorSchema?: ErrorSchema<T>, id?: string) => {
73
+ const { onChange } = this.props;
71
74
  if (value === undefined && addedByAdditionalProperties) {
72
75
  // Don't set value = undefined for fields added by
73
76
  // additionalProperties. Doing so removes them from the
@@ -78,16 +81,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
78
81
  // set empty values to the empty string.
79
82
  value = '' as unknown as T;
80
83
  }
81
- const newFormData = { ...formData, [name]: value } as unknown as T;
82
- onChange(
83
- newFormData,
84
- errorSchema &&
85
- errorSchema && {
86
- ...errorSchema,
87
- [name]: newErrorSchema,
88
- },
89
- id,
90
- );
84
+ onChange(value, path, newErrorSchema, id);
91
85
  };
92
86
  };
93
87
 
@@ -100,10 +94,11 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
100
94
  onDropPropertyClick = (key: string) => {
101
95
  return (event: DragEvent) => {
102
96
  event.preventDefault();
103
- const { onChange, formData } = this.props;
97
+ const { onChange, formData, fieldPathId } = this.props;
104
98
  const copiedFormData = { ...formData } as T;
105
99
  unset(copiedFormData, key);
106
- onChange(copiedFormData);
100
+ // drop property will pass the name in `path` array
101
+ onChange(copiedFormData, fieldPathId.path);
107
102
  };
108
103
  };
109
104
 
@@ -133,11 +128,11 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
133
128
  * @returns - The key change callback function
134
129
  */
135
130
  onKeyChange = (oldValue: any) => {
136
- return (value: any, newErrorSchema: ErrorSchema<T>) => {
131
+ return (value: any) => {
137
132
  if (oldValue === value) {
138
133
  return;
139
134
  }
140
- const { formData, onChange, errorSchema } = this.props;
135
+ const { formData, onChange, fieldPathId } = this.props;
141
136
 
142
137
  value = this.getAvailableKey(value, formData);
143
138
  const newFormData: GenericObjectType = {
@@ -152,14 +147,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
152
147
 
153
148
  this.setState({ wasPropertyKeyModified: true });
154
149
 
155
- onChange(
156
- renamedObj,
157
- errorSchema &&
158
- errorSchema && {
159
- ...errorSchema,
160
- [value]: newErrorSchema,
161
- },
162
- );
150
+ onChange(renamedObj, fieldPathId.path);
163
151
  };
164
152
  };
165
153
 
@@ -198,7 +186,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
198
186
  if (!(schema.additionalProperties || schema.patternProperties)) {
199
187
  return;
200
188
  }
201
- const { formData, onChange, registry } = this.props;
189
+ const { formData, onChange, registry, fieldPathId } = this.props;
202
190
  const newFormData = { ...formData } as T;
203
191
  const newKey = this.getAvailableKey('newKey', newFormData);
204
192
  if (schema.patternProperties) {
@@ -230,7 +218,8 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
230
218
  set(newFormData as GenericObjectType, newKey, newValue);
231
219
  }
232
220
 
233
- onChange(newFormData);
221
+ // add will pass the name in `path` array
222
+ onChange(newFormData, fieldPathId.path);
234
223
  };
235
224
 
236
225
  /** Renders the `ObjectField` from the given props
@@ -241,46 +230,51 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
241
230
  uiSchema = {},
242
231
  formData,
243
232
  errorSchema,
244
- idSchema,
233
+ fieldPathId,
245
234
  name,
246
235
  required = false,
247
236
  disabled,
248
237
  readonly,
249
238
  hideError,
250
- idPrefix,
251
- idSeparator,
252
239
  onBlur,
253
240
  onFocus,
254
241
  registry,
255
242
  title,
256
243
  } = this.props;
257
244
 
258
- const { fields, formContext, schemaUtils, translateString, globalUiOptions } = registry;
259
- const { SchemaField } = fields;
260
- const schema: S = schemaUtils.retrieveSchema(rawSchema, formData);
245
+ const { fields, formContext, schemaUtils, translateString, globalFormOptions, globalUiOptions } = registry;
246
+ const { OptionalDataControlsField, SchemaField } = fields;
247
+ const schema: S = schemaUtils.retrieveSchema(rawSchema, formData, true);
261
248
  const uiOptions = getUiOptions<T, S, F>(uiSchema, globalUiOptions);
262
249
  const { properties: schemaProperties = {} } = schema;
263
250
 
264
251
  const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
265
252
  const description = uiOptions.description ?? schema.description;
266
- let orderedProperties: string[];
267
- try {
268
- const properties = Object.keys(schemaProperties);
269
- orderedProperties = orderProperties(properties, uiOptions.order);
270
- } catch (err) {
271
- return (
272
- <div>
273
- <p className='rjsf-config-error' style={{ color: 'red' }}>
274
- <Markdown options={{ disableParsingRawHTML: true }}>
275
- {translateString(TranslatableString.InvalidObjectField, [name || 'root', (err as Error).message])}
276
- </Markdown>
277
- </p>
278
- <pre>{JSON.stringify(schema)}</pre>
279
- </div>
280
- );
253
+ const renderOptionalField = shouldRenderOptionalField(registry, schema, required, uiSchema);
254
+ const hasFormData = isFormDataAvailable(formData);
255
+ let orderedProperties: string[] = [];
256
+ if (!renderOptionalField || hasFormData) {
257
+ try {
258
+ const properties = Object.keys(schemaProperties);
259
+ orderedProperties = orderProperties(properties, uiOptions.order);
260
+ } catch (err) {
261
+ return (
262
+ <div>
263
+ <p className='rjsf-config-error' style={{ color: 'red' }}>
264
+ <Markdown options={{ disableParsingRawHTML: true }}>
265
+ {translateString(TranslatableString.InvalidObjectField, [name || 'root', (err as Error).message])}
266
+ </Markdown>
267
+ </p>
268
+ <pre>{JSON.stringify(schema)}</pre>
269
+ </div>
270
+ );
271
+ }
281
272
  }
282
273
 
283
274
  const Template = getTemplate<'ObjectFieldTemplate', T, S, F>('ObjectFieldTemplate', registry, uiOptions);
275
+ const optionalDataControl = renderOptionalField ? (
276
+ <OptionalDataControlsField {...this.props} schema={schema} />
277
+ ) : undefined;
284
278
 
285
279
  const templateProps = {
286
280
  // getDisplayLabel() always returns false for object types, so just check the `uiOptions.label`
@@ -290,7 +284,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
290
284
  const addedByAdditionalProperties = has(schema, [PROPERTIES_KEY, name, ADDITIONAL_PROPERTY_FLAG]);
291
285
  const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[name];
292
286
  const hidden = getUiOptions<T, S, F>(fieldUiSchema).widget === 'hidden';
293
- const fieldIdSchema: IdSchema<T> = get(idSchema, [name], {});
287
+ const innerFieldIdPathId = toFieldPathId(name, globalFormOptions, fieldPathId);
294
288
 
295
289
  return {
296
290
  content: (
@@ -301,9 +295,7 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
301
295
  schema={get(schema, [PROPERTIES_KEY, name], {}) as S}
302
296
  uiSchema={fieldUiSchema}
303
297
  errorSchema={get(errorSchema, name)}
304
- idSchema={fieldIdSchema}
305
- idPrefix={idPrefix}
306
- idSeparator={idSeparator}
298
+ fieldPathId={innerFieldIdPathId}
307
299
  formData={get(formData, name)}
308
300
  formContext={formContext}
309
301
  wasPropertyKeyModified={this.state.wasPropertyKeyModified}
@@ -328,13 +320,15 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
328
320
  readonly,
329
321
  disabled,
330
322
  required,
331
- idSchema,
323
+ fieldPathId,
332
324
  uiSchema,
333
325
  errorSchema,
334
326
  schema,
335
327
  formData,
336
328
  formContext,
337
329
  registry,
330
+ optionalDataControl,
331
+ className: renderOptionalField ? 'rjsf-optional-object-field' : undefined,
338
332
  };
339
333
  return <Template {...templateProps} onAddClick={this.handleAddClick} />;
340
334
  }
@@ -0,0 +1,84 @@
1
+ import {
2
+ FieldProps,
3
+ FormContextType,
4
+ getSchemaType,
5
+ getTemplate,
6
+ getUiOptions,
7
+ isFormDataAvailable,
8
+ optionalControlsId,
9
+ OptionalDataControlsTemplateProps,
10
+ RJSFSchema,
11
+ StrictRJSFSchema,
12
+ TranslatableString,
13
+ } from '@rjsf/utils';
14
+
15
+ /** The `OptionalDataControlsField` component is used to render the optional data controls for the field associated
16
+ * with the given props.
17
+ *
18
+ * @param props - The `FieldProps` for this template
19
+ */
20
+ export default function OptionalDataControlsField<
21
+ T = any,
22
+ S extends StrictRJSFSchema = RJSFSchema,
23
+ F extends FormContextType = any,
24
+ >(props: FieldProps<T, S, F>) {
25
+ const {
26
+ schema,
27
+ uiSchema = {},
28
+ formData,
29
+ disabled = false,
30
+ readonly = false,
31
+ onChange,
32
+ errorSchema,
33
+ fieldPathId,
34
+ registry,
35
+ } = props;
36
+
37
+ const { globalUiOptions = {}, schemaUtils, translateString } = registry;
38
+ const uiOptions = getUiOptions<T, S, F>(uiSchema, globalUiOptions);
39
+ const OptionalDataControlsTemplate = getTemplate<'OptionalDataControlsTemplate', T, S, F>(
40
+ 'OptionalDataControlsTemplate',
41
+ registry,
42
+ uiOptions,
43
+ );
44
+ const hasFormData = isFormDataAvailable(formData);
45
+ let id: string;
46
+ let label: string | undefined;
47
+ let onAddClick: OptionalDataControlsTemplateProps['onAddClick'];
48
+ let onRemoveClick: OptionalDataControlsTemplateProps['onRemoveClick'];
49
+ if (disabled || readonly) {
50
+ id = optionalControlsId(fieldPathId, 'Msg');
51
+ label = hasFormData ? undefined : translateString(TranslatableString.OptionalObjectEmptyMsg);
52
+ } else {
53
+ const labelEnum = hasFormData ? TranslatableString.OptionalObjectRemove : TranslatableString.OptionalObjectAdd;
54
+ label = translateString(labelEnum);
55
+ if (hasFormData) {
56
+ id = optionalControlsId(fieldPathId, 'Remove');
57
+ onRemoveClick = () => onChange(undefined as T, fieldPathId.path, errorSchema);
58
+ } else {
59
+ id = optionalControlsId(fieldPathId, 'Add');
60
+ onAddClick = () => {
61
+ // If it has form data, store an empty object, otherwise get the default form state and use it
62
+ let newFormData: unknown = schemaUtils.getDefaultFormState(schema, formData, 'excludeObjectChildren');
63
+ if (newFormData === undefined) {
64
+ // If new form data ended up being undefined, and we have pushed the add button we need to actually add data
65
+ newFormData = getSchemaType<S>(schema) === 'array' ? [] : {};
66
+ }
67
+ onChange(newFormData as T, fieldPathId.path, errorSchema);
68
+ };
69
+ }
70
+ }
71
+ return (
72
+ label && (
73
+ <OptionalDataControlsTemplate
74
+ id={id}
75
+ registry={registry}
76
+ schema={schema}
77
+ uiSchema={uiSchema}
78
+ label={label}
79
+ onAddClick={onAddClick}
80
+ onRemoveClick={onRemoveClick}
81
+ />
82
+ )
83
+ );
84
+ }
@@ -1,9 +1,10 @@
1
1
  import { useCallback, Component, ComponentType } from 'react';
2
2
  import {
3
3
  ADDITIONAL_PROPERTY_FLAG,
4
- deepEquals,
5
4
  descriptionId,
6
5
  ErrorSchema,
6
+ FieldPathId,
7
+ FieldPathList,
7
8
  FieldProps,
8
9
  FieldTemplateProps,
9
10
  FormContextType,
@@ -11,10 +12,9 @@ import {
11
12
  getTemplate,
12
13
  getUiOptions,
13
14
  ID_KEY,
14
- IdSchema,
15
- mergeObjects,
16
15
  Registry,
17
16
  RJSFSchema,
17
+ shouldRender,
18
18
  StrictRJSFSchema,
19
19
  TranslatableString,
20
20
  UI_OPTIONS_KEY,
@@ -40,14 +40,14 @@ const COMPONENT_TYPES: { [key: string]: string } = {
40
40
  *
41
41
  * @param schema - The schema from which to obtain the type
42
42
  * @param uiOptions - The UI Options that may affect the component decision
43
- * @param idSchema - The id that is passed to the `UnsupportedFieldTemplate`
43
+ * @param fieldPathId - The id that is passed to the `UnsupportedFieldTemplate`
44
44
  * @param registry - The registry from which fields and templates are obtained
45
45
  * @returns - The `Field` component that is used to render the actual field data
46
46
  */
47
47
  function getFieldComponent<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
48
48
  schema: S,
49
49
  uiOptions: UIOptionsType<T, S, F>,
50
- idSchema: IdSchema<T>,
50
+ fieldPathId: FieldPathId,
51
51
  registry: Registry<T, S, F>,
52
52
  ): ComponentType<FieldProps<T, S, F>> {
53
53
  const field = uiOptions.field;
@@ -87,7 +87,7 @@ function getFieldComponent<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
87
87
  return (
88
88
  <UnsupportedFieldTemplate
89
89
  schema={schema}
90
- idSchema={idSchema}
90
+ fieldPathId={fieldPathId}
91
91
  reason={translateString(TranslatableString.UnknownFieldType, [String(schema.type)])}
92
92
  registry={registry}
93
93
  />
@@ -106,12 +106,10 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
106
106
  ) {
107
107
  const {
108
108
  schema: _schema,
109
- idSchema: _idSchema,
109
+ fieldPathId,
110
110
  uiSchema,
111
111
  formData,
112
112
  errorSchema,
113
- idPrefix,
114
- idSeparator,
115
113
  name,
116
114
  onChange,
117
115
  onKeyChange,
@@ -131,24 +129,20 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
131
129
  const FieldHelpTemplate = getTemplate<'FieldHelpTemplate', T, S, F>('FieldHelpTemplate', registry, uiOptions);
132
130
  const FieldErrorTemplate = getTemplate<'FieldErrorTemplate', T, S, F>('FieldErrorTemplate', registry, uiOptions);
133
131
  const schema = schemaUtils.retrieveSchema(_schema, formData);
134
- const fieldId = _idSchema[ID_KEY];
135
- const idSchema = mergeObjects(
136
- schemaUtils.toIdSchema(schema, fieldId, formData, idPrefix, idSeparator),
137
- _idSchema,
138
- ) as IdSchema<T>;
132
+ const fieldId = fieldPathId[ID_KEY];
139
133
 
140
134
  /** Intermediary `onChange` handler for field components that will inject the `id` of the current field into the
141
135
  * `onChange` chain if it is not already being provided from a deeper level in the hierarchy
142
136
  */
143
137
  const handleFieldComponentChange = useCallback(
144
- (formData: T | undefined, newErrorSchema?: ErrorSchema<T>, id?: string) => {
138
+ (formData: T | undefined, path: FieldPathList, newErrorSchema?: ErrorSchema<T>, id?: string) => {
145
139
  const theId = id || fieldId;
146
- return onChange(formData, newErrorSchema, theId);
140
+ return onChange(formData, path, newErrorSchema, theId);
147
141
  },
148
142
  [fieldId, onChange],
149
143
  );
150
144
 
151
- const FieldComponent = getFieldComponent<T, S, F>(schema, uiOptions, idSchema, registry);
145
+ const FieldComponent = getFieldComponent<T, S, F>(schema, uiOptions, fieldPathId, registry);
152
146
  const disabled = Boolean(uiOptions.disabled ?? props.disabled);
153
147
  const readonly = Boolean(uiOptions.readonly ?? (props.readonly || props.schema.readOnly || schema.readOnly));
154
148
  const uiSchemaHideError = uiOptions.hideError;
@@ -172,7 +166,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
172
166
  <FieldComponent
173
167
  {...props}
174
168
  onChange={handleFieldComponentChange}
175
- idSchema={idSchema}
169
+ fieldPathId={fieldPathId}
176
170
  schema={schema}
177
171
  uiSchema={fieldUiSchema}
178
172
  disabled={disabled}
@@ -185,7 +179,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
185
179
  />
186
180
  );
187
181
 
188
- const id = idSchema[ID_KEY];
182
+ const id = fieldPathId[ID_KEY];
189
183
 
190
184
  // If this schema has a title defined, but the user has set a new key/label, retain their input.
191
185
  let label;
@@ -213,7 +207,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
213
207
  const helpComponent = (
214
208
  <FieldHelpTemplate
215
209
  help={help}
216
- idSchema={idSchema}
210
+ fieldPathId={fieldPathId}
217
211
  schema={schema}
218
212
  uiSchema={uiSchema}
219
213
  hasErrors={!hideError && __errors && __errors.length > 0}
@@ -229,7 +223,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
229
223
  <FieldErrorTemplate
230
224
  errors={__errors}
231
225
  errorSchema={errorSchema}
232
- idSchema={idSchema}
226
+ fieldPathId={fieldPathId}
233
227
  schema={schema}
234
228
  uiSchema={uiSchema}
235
229
  registry={registry}
@@ -238,7 +232,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
238
232
  const fieldProps: Omit<FieldTemplateProps<T, S, F>, 'children'> = {
239
233
  description: (
240
234
  <DescriptionFieldTemplate
241
- id={descriptionId<T>(id)}
235
+ id={descriptionId(id)}
242
236
  description={description}
243
237
  schema={schema}
244
238
  uiSchema={uiSchema}
@@ -263,7 +257,6 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
263
257
  displayLabel,
264
258
  classNames: classNames.join(' ').trim(),
265
259
  style: uiOptions.style,
266
- formContext,
267
260
  formData,
268
261
  schema,
269
262
  uiSchema,
@@ -292,9 +285,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
292
285
  errorSchema={errorSchema}
293
286
  formData={formData}
294
287
  formContext={formContext}
295
- idPrefix={idPrefix}
296
- idSchema={idSchema}
297
- idSeparator={idSeparator}
288
+ fieldPathId={fieldPathId}
298
289
  onBlur={props.onBlur}
299
290
  onChange={props.onChange}
300
291
  onFocus={props.onFocus}
@@ -316,9 +307,7 @@ function SchemaFieldRender<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
316
307
  errorSchema={errorSchema}
317
308
  formData={formData}
318
309
  formContext={formContext}
319
- idPrefix={idPrefix}
320
- idSchema={idSchema}
321
- idSeparator={idSeparator}
310
+ fieldPathId={fieldPathId}
322
311
  onBlur={props.onBlur}
323
312
  onChange={props.onChange}
324
313
  onFocus={props.onFocus}
@@ -343,7 +332,12 @@ class SchemaField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
343
332
  FieldProps<T, S, F>
344
333
  > {
345
334
  shouldComponentUpdate(nextProps: Readonly<FieldProps<T, S, F>>) {
346
- return !deepEquals(this.props, nextProps);
335
+ const {
336
+ registry: { globalFormOptions },
337
+ } = this.props;
338
+ const { experimental_componentUpdateStrategy = 'customDeep' } = globalFormOptions;
339
+
340
+ return shouldRender(this, nextProps, this.state, experimental_componentUpdateStrategy);
347
341
  }
348
342
 
349
343
  render() {
@@ -1,3 +1,4 @@
1
+ import { useCallback } from 'react';
1
2
  import {
2
3
  getWidget,
3
4
  getUiOptions,
@@ -7,6 +8,7 @@ import {
7
8
  FormContextType,
8
9
  RJSFSchema,
9
10
  StrictRJSFSchema,
11
+ ErrorSchema,
10
12
  } from '@rjsf/utils';
11
13
 
12
14
  /** The `StringField` component is used to render a schema field that represents a string type
@@ -20,7 +22,7 @@ function StringField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
20
22
  schema,
21
23
  name,
22
24
  uiSchema,
23
- idSchema,
25
+ fieldPathId,
24
26
  formData,
25
27
  required,
26
28
  disabled = false,
@@ -44,18 +46,25 @@ function StringField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
44
46
  const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
45
47
  const label = uiTitle ?? title ?? name;
46
48
  const Widget = getWidget<T, S, F>(schema, widget, widgets);
49
+ const onWidgetChange = useCallback(
50
+ (value: T | undefined, errorSchema?: ErrorSchema, id?: string) => {
51
+ // String field change passes an empty path array to the parent field which adds the appropriate path
52
+ return onChange(value, fieldPathId.path, errorSchema, id);
53
+ },
54
+ [onChange, fieldPathId],
55
+ );
47
56
  return (
48
57
  <Widget
49
58
  options={{ ...options, enumOptions }}
50
59
  schema={schema}
51
60
  uiSchema={uiSchema}
52
- id={idSchema.$id}
61
+ id={fieldPathId.$id}
53
62
  name={name}
54
63
  label={label}
55
64
  hideLabel={!displayLabel}
56
65
  hideError={hideError}
57
66
  value={formData}
58
- onChange={onChange}
67
+ onChange={onWidgetChange}
59
68
  onBlur={onBlur}
60
69
  onFocus={onFocus}
61
70
  required={required}
@@ -8,6 +8,7 @@ import LayoutMultiSchemaField from './LayoutMultiSchemaField';
8
8
  import MultiSchemaField from './MultiSchemaField';
9
9
  import NumberField from './NumberField';
10
10
  import ObjectField from './ObjectField';
11
+ import OptionalDataControlsField from './OptionalDataControlsField';
11
12
  import SchemaField from './SchemaField';
12
13
  import StringField from './StringField';
13
14
  import NullField from './NullField';
@@ -28,6 +29,7 @@ function fields<
28
29
  NumberField,
29
30
  ObjectField,
30
31
  OneOfField: MultiSchemaField,
32
+ OptionalDataControlsField,
31
33
  SchemaField,
32
34
  StringField,
33
35
  NullField,
@@ -9,7 +9,7 @@ import {
9
9
  } from '@rjsf/utils';
10
10
 
11
11
  /** The `ArrayFieldDescriptionTemplate` component renders a `DescriptionFieldTemplate` with an `id` derived from
12
- * the `idSchema`.
12
+ * the `fieldPathId`.
13
13
  *
14
14
  * @param props - The `ArrayFieldDescriptionProps` for the component
15
15
  */
@@ -18,7 +18,7 @@ export default function ArrayFieldDescriptionTemplate<
18
18
  S extends StrictRJSFSchema = RJSFSchema,
19
19
  F extends FormContextType = any,
20
20
  >(props: ArrayFieldDescriptionProps<T, S, F>) {
21
- const { idSchema, description, registry, schema, uiSchema } = props;
21
+ const { fieldPathId, description, registry, schema, uiSchema } = props;
22
22
  const options = getUiOptions<T, S, F>(uiSchema, registry.globalUiOptions);
23
23
  const { label: displayLabel = true } = options;
24
24
  if (!description || !displayLabel) {
@@ -31,7 +31,7 @@ export default function ArrayFieldDescriptionTemplate<
31
31
  );
32
32
  return (
33
33
  <DescriptionFieldTemplate
34
- id={descriptionId<T>(idSchema)}
34
+ id={descriptionId(fieldPathId)}
35
35
  description={description}
36
36
  schema={schema}
37
37
  uiSchema={uiSchema}
@@ -23,7 +23,7 @@ export default function ArrayFieldItemButtonsTemplate<
23
23
  hasMoveDown,
24
24
  hasMoveUp,
25
25
  hasRemove,
26
- idSchema,
26
+ fieldPathId,
27
27
  index,
28
28
  onCopyIndexClick,
29
29
  onDropIndexClick,
@@ -42,7 +42,7 @@ export default function ArrayFieldItemButtonsTemplate<
42
42
  <>
43
43
  {(hasMoveUp || hasMoveDown) && (
44
44
  <MoveUpButton
45
- id={buttonId<T>(idSchema, 'moveUp')}
45
+ id={buttonId(fieldPathId, 'moveUp')}
46
46
  className='rjsf-array-item-move-up'
47
47
  disabled={disabled || readonly || !hasMoveUp}
48
48
  onClick={onArrowUpClick}
@@ -52,7 +52,7 @@ export default function ArrayFieldItemButtonsTemplate<
52
52
  )}
53
53
  {(hasMoveUp || hasMoveDown) && (
54
54
  <MoveDownButton
55
- id={buttonId<T>(idSchema, 'moveDown')}
55
+ id={buttonId(fieldPathId, 'moveDown')}
56
56
  className='rjsf-array-item-move-down'
57
57
  disabled={disabled || readonly || !hasMoveDown}
58
58
  onClick={onArrowDownClick}
@@ -62,7 +62,7 @@ export default function ArrayFieldItemButtonsTemplate<
62
62
  )}
63
63
  {hasCopy && (
64
64
  <CopyButton
65
- id={buttonId<T>(idSchema, 'copy')}
65
+ id={buttonId(fieldPathId, 'copy')}
66
66
  className='rjsf-array-item-copy'
67
67
  disabled={disabled || readonly}
68
68
  onClick={onCopyClick}
@@ -72,7 +72,7 @@ export default function ArrayFieldItemButtonsTemplate<
72
72
  )}
73
73
  {hasRemove && (
74
74
  <RemoveButton
75
- id={buttonId<T>(idSchema, 'remove')}
75
+ id={buttonId(fieldPathId, 'remove')}
76
76
  className='rjsf-array-item-remove'
77
77
  disabled={disabled || readonly}
78
78
  onClick={onRemoveClick}
@@ -22,9 +22,10 @@ export default function ArrayFieldTemplate<
22
22
  canAdd,
23
23
  className,
24
24
  disabled,
25
- idSchema,
25
+ fieldPathId,
26
26
  uiSchema,
27
27
  items,
28
+ optionalDataControl,
28
29
  onAddClick,
29
30
  readonly,
30
31
  registry,
@@ -49,26 +50,29 @@ export default function ArrayFieldTemplate<
49
50
  uiOptions,
50
51
  );
51
52
  // Button templates are not overridden in the uiSchema
53
+ const showOptionalDataControlInTitle = !readonly && !disabled;
52
54
  const {
53
55
  ButtonTemplates: { AddButton },
54
56
  } = registry.templates;
55
57
  return (
56
- <fieldset className={className} id={idSchema.$id}>
58
+ <fieldset className={className} id={fieldPathId.$id}>
57
59
  <ArrayFieldTitleTemplate
58
- idSchema={idSchema}
60
+ fieldPathId={fieldPathId}
59
61
  title={uiOptions.title || title}
60
62
  required={required}
61
63
  schema={schema}
62
64
  uiSchema={uiSchema}
63
65
  registry={registry}
66
+ optionalDataControl={showOptionalDataControlInTitle ? optionalDataControl : undefined}
64
67
  />
65
68
  <ArrayFieldDescriptionTemplate
66
- idSchema={idSchema}
69
+ fieldPathId={fieldPathId}
67
70
  description={uiOptions.description || schema.description}
68
71
  schema={schema}
69
72
  uiSchema={uiSchema}
70
73
  registry={registry}
71
74
  />
75
+ {!showOptionalDataControlInTitle ? optionalDataControl : undefined}
72
76
  <div className='row array-item-list'>
73
77
  {items &&
74
78
  items.map(({ key, ...itemProps }: ArrayFieldItemTemplateType<T, S, F>) => (
@@ -77,7 +81,7 @@ export default function ArrayFieldTemplate<
77
81
  </div>
78
82
  {canAdd && (
79
83
  <AddButton
80
- id={buttonId<T>(idSchema, 'add')}
84
+ id={buttonId(fieldPathId, 'add')}
81
85
  className='rjsf-array-item-add'
82
86
  onClick={onAddClick}
83
87
  disabled={disabled || readonly}