@rjsf/core 6.6.1 → 6.7.0
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.
- package/LICENSE.md +201 -0
- package/dist/core.umd.js +694 -584
- package/dist/index.cjs +1191 -1071
- package/dist/index.cjs.map +4 -4
- package/dist/index.esm.js +1030 -984
- package/dist/index.esm.js.map +4 -4
- package/lib/components/Form.d.ts +16 -13
- package/lib/components/Form.d.ts.map +1 -1
- package/lib/components/Form.js +126 -76
- package/lib/components/RichDescription.d.ts +3 -3
- package/lib/components/RichDescription.d.ts.map +1 -1
- package/lib/components/RichDescription.js +1 -1
- package/lib/components/RichHelp.d.ts +3 -3
- package/lib/components/RichHelp.d.ts.map +1 -1
- package/lib/components/RichHelp.js +1 -1
- package/lib/components/SchemaExamples.d.ts +2 -2
- package/lib/components/SchemaExamples.d.ts.map +1 -1
- package/lib/components/SchemaExamples.js +1 -3
- package/lib/components/fields/ArrayField.d.ts +2 -2
- package/lib/components/fields/ArrayField.d.ts.map +1 -1
- package/lib/components/fields/ArrayField.js +87 -88
- package/lib/components/fields/BooleanField.d.ts +2 -2
- package/lib/components/fields/BooleanField.d.ts.map +1 -1
- package/lib/components/fields/BooleanField.js +2 -5
- package/lib/components/fields/CyclicSchemaField.d.ts +8 -0
- package/lib/components/fields/CyclicSchemaField.d.ts.map +1 -0
- package/lib/components/fields/CyclicSchemaField.js +21 -0
- package/lib/components/fields/FallbackField.d.ts +2 -2
- package/lib/components/fields/FallbackField.d.ts.map +1 -1
- package/lib/components/fields/FallbackField.js +2 -2
- package/lib/components/fields/LayoutGridField.d.ts +8 -10
- package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
- package/lib/components/fields/LayoutGridField.js +5 -4
- package/lib/components/fields/LayoutHeaderField.d.ts +2 -2
- package/lib/components/fields/LayoutHeaderField.d.ts.map +1 -1
- package/lib/components/fields/LayoutHeaderField.js +1 -1
- package/lib/components/fields/LayoutMultiSchemaField.d.ts +2 -2
- package/lib/components/fields/LayoutMultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/LayoutMultiSchemaField.js +4 -1
- package/lib/components/fields/MultiSchemaField.d.ts +2 -41
- package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/MultiSchemaField.js +116 -122
- package/lib/components/fields/NullField.d.ts +1 -1
- package/lib/components/fields/NullField.d.ts.map +1 -1
- package/lib/components/fields/NumberField.d.ts +2 -2
- package/lib/components/fields/NumberField.d.ts.map +1 -1
- package/lib/components/fields/NumberField.js +7 -9
- package/lib/components/fields/ObjectField.d.ts +2 -2
- package/lib/components/fields/ObjectField.d.ts.map +1 -1
- package/lib/components/fields/ObjectField.js +28 -26
- package/lib/components/fields/OptionalDataControlsField.d.ts +2 -2
- package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -1
- package/lib/components/fields/SchemaField.d.ts +14 -8
- package/lib/components/fields/SchemaField.d.ts.map +1 -1
- package/lib/components/fields/SchemaField.js +43 -30
- package/lib/components/fields/StringField.d.ts +2 -2
- package/lib/components/fields/StringField.d.ts.map +1 -1
- package/lib/components/fields/StringField.js +2 -5
- package/lib/components/fields/index.d.ts +1 -1
- package/lib/components/fields/index.d.ts.map +1 -1
- package/lib/components/fields/index.js +2 -0
- package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +2 -2
- package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldDescriptionTemplate.js +1 -1
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts +2 -2
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +1 -1
- package/lib/components/templates/ArrayFieldItemTemplate.d.ts +2 -2
- package/lib/components/templates/ArrayFieldItemTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldItemTemplate.js +1 -1
- package/lib/components/templates/ArrayFieldTemplate.d.ts +2 -2
- package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTemplate.js +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +2 -2
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.js +1 -1
- package/lib/components/templates/BaseInputTemplate.d.ts +2 -2
- package/lib/components/templates/BaseInputTemplate.d.ts.map +1 -1
- package/lib/components/templates/BaseInputTemplate.js +7 -6
- package/lib/components/templates/ButtonTemplates/AddButton.d.ts +2 -2
- package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -1
- package/lib/components/templates/ButtonTemplates/IconButton.d.ts +14 -7
- package/lib/components/templates/ButtonTemplates/IconButton.d.ts.map +1 -1
- package/lib/components/templates/ButtonTemplates/IconButton.js +14 -6
- package/lib/components/templates/ButtonTemplates/SubmitButton.d.ts +2 -2
- package/lib/components/templates/ButtonTemplates/SubmitButton.d.ts.map +1 -1
- package/lib/components/templates/ButtonTemplates/index.d.ts +1 -1
- package/lib/components/templates/ButtonTemplates/index.d.ts.map +1 -1
- package/lib/components/templates/CyclicSchemaExpandTemplate.d.ts +7 -0
- package/lib/components/templates/CyclicSchemaExpandTemplate.d.ts.map +1 -0
- package/lib/components/templates/CyclicSchemaExpandTemplate.js +12 -0
- package/lib/components/templates/DescriptionField.d.ts +2 -2
- package/lib/components/templates/DescriptionField.d.ts.map +1 -1
- package/lib/components/templates/ErrorList.d.ts +2 -2
- package/lib/components/templates/ErrorList.d.ts.map +1 -1
- package/lib/components/templates/ErrorList.js +2 -4
- package/lib/components/templates/FallbackFieldTemplate.d.ts +2 -2
- package/lib/components/templates/FallbackFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldErrorTemplate.d.ts +2 -2
- package/lib/components/templates/FieldErrorTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldErrorTemplate.js +1 -3
- package/lib/components/templates/FieldHelpTemplate.d.ts +2 -2
- package/lib/components/templates/FieldHelpTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldTemplate/FieldTemplate.d.ts +2 -2
- package/lib/components/templates/FieldTemplate/FieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/FieldTemplate/FieldTemplate.js +1 -1
- package/lib/components/templates/FieldTemplate/Label.d.ts +3 -3
- package/lib/components/templates/FieldTemplate/Label.d.ts.map +1 -1
- package/lib/components/templates/GridTemplate.d.ts +2 -2
- package/lib/components/templates/GridTemplate.d.ts.map +1 -1
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +2 -2
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ObjectFieldTemplate.d.ts +2 -2
- package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ObjectFieldTemplate.js +1 -1
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts +2 -2
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -1
- package/lib/components/templates/OptionalDataControlsTemplate.js +1 -1
- package/lib/components/templates/TitleField.d.ts +2 -2
- package/lib/components/templates/TitleField.d.ts.map +1 -1
- package/lib/components/templates/UnsupportedField.d.ts +2 -2
- package/lib/components/templates/UnsupportedField.d.ts.map +1 -1
- package/lib/components/templates/UnsupportedField.js +2 -2
- package/lib/components/templates/WrapIfAdditionalTemplate.d.ts +2 -2
- package/lib/components/templates/WrapIfAdditionalTemplate.d.ts.map +1 -1
- package/lib/components/templates/WrapIfAdditionalTemplate.js +1 -1
- package/lib/components/templates/index.d.ts +1 -1
- package/lib/components/templates/index.d.ts.map +1 -1
- package/lib/components/templates/index.js +2 -0
- package/lib/components/widgets/AltDateTimeWidget.d.ts +2 -2
- package/lib/components/widgets/AltDateTimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/AltDateWidget.d.ts +2 -2
- package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
- package/lib/components/widgets/AltDateWidget.js +2 -2
- package/lib/components/widgets/CheckboxWidget.d.ts +2 -2
- package/lib/components/widgets/CheckboxWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxesWidget.d.ts +2 -2
- package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxesWidget.js +4 -4
- package/lib/components/widgets/ColorWidget.d.ts +2 -2
- package/lib/components/widgets/ColorWidget.d.ts.map +1 -1
- package/lib/components/widgets/DateTimeWidget.d.ts +2 -2
- package/lib/components/widgets/DateTimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/DateTimeWidget.js +2 -2
- package/lib/components/widgets/DateWidget.d.ts +2 -2
- package/lib/components/widgets/DateWidget.d.ts.map +1 -1
- package/lib/components/widgets/EmailWidget.d.ts +2 -2
- package/lib/components/widgets/EmailWidget.d.ts.map +1 -1
- package/lib/components/widgets/FileWidget.d.ts +2 -2
- package/lib/components/widgets/FileWidget.d.ts.map +1 -1
- package/lib/components/widgets/FileWidget.js +6 -4
- package/lib/components/widgets/HiddenWidget.d.ts +2 -2
- package/lib/components/widgets/HiddenWidget.d.ts.map +1 -1
- package/lib/components/widgets/PasswordWidget.d.ts +2 -2
- package/lib/components/widgets/PasswordWidget.d.ts.map +1 -1
- package/lib/components/widgets/RadioWidget.d.ts +2 -2
- package/lib/components/widgets/RadioWidget.d.ts.map +1 -1
- package/lib/components/widgets/RadioWidget.js +4 -4
- package/lib/components/widgets/RangeWidget.d.ts +2 -2
- package/lib/components/widgets/RangeWidget.d.ts.map +1 -1
- package/lib/components/widgets/RatingWidget.d.ts +2 -2
- package/lib/components/widgets/RatingWidget.d.ts.map +1 -1
- package/lib/components/widgets/RatingWidget.js +15 -15
- package/lib/components/widgets/SelectWidget.d.ts +2 -2
- package/lib/components/widgets/SelectWidget.d.ts.map +1 -1
- package/lib/components/widgets/SelectWidget.js +9 -8
- package/lib/components/widgets/TextWidget.d.ts +2 -2
- package/lib/components/widgets/TextWidget.d.ts.map +1 -1
- package/lib/components/widgets/TextareaWidget.d.ts +2 -2
- package/lib/components/widgets/TextareaWidget.d.ts.map +1 -1
- package/lib/components/widgets/TextareaWidget.js +5 -5
- package/lib/components/widgets/TimeWidget.d.ts +2 -2
- package/lib/components/widgets/TimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/URLWidget.d.ts +2 -2
- package/lib/components/widgets/URLWidget.d.ts.map +1 -1
- package/lib/components/widgets/UpDownWidget.d.ts +2 -2
- package/lib/components/widgets/UpDownWidget.d.ts.map +1 -1
- package/lib/components/widgets/index.d.ts +1 -1
- package/lib/components/widgets/index.d.ts.map +1 -1
- package/lib/getDefaultRegistry.d.ts +1 -1
- package/lib/getDefaultRegistry.d.ts.map +1 -1
- package/lib/getDefaultRegistry.js +1 -1
- package/lib/getTestRegistry.d.ts +1 -1
- package/lib/getTestRegistry.d.ts.map +1 -1
- package/lib/getTestRegistry.js +1 -1
- package/lib/index.d.ts +10 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/withTheme.d.ts +3 -3
- package/lib/withTheme.d.ts.map +1 -1
- package/lib/withTheme.js +6 -6
- package/package.json +28 -28
- package/src/components/Form.tsx +197 -111
- package/src/components/RichDescription.tsx +3 -10
- package/src/components/RichHelp.tsx +3 -10
- package/src/components/SchemaExamples.tsx +6 -4
- package/src/components/fields/ArrayField.tsx +122 -108
- package/src/components/fields/BooleanField.tsx +4 -9
- package/src/components/fields/CyclicSchemaField.tsx +40 -0
- package/src/components/fields/FallbackField.tsx +4 -8
- package/src/components/fields/LayoutGridField.tsx +21 -19
- package/src/components/fields/LayoutHeaderField.tsx +2 -10
- package/src/components/fields/LayoutMultiSchemaField.tsx +15 -10
- package/src/components/fields/MultiSchemaField.tsx +202 -218
- package/src/components/fields/NullField.tsx +1 -1
- package/src/components/fields/NumberField.tsx +8 -10
- package/src/components/fields/ObjectField.tsx +52 -40
- package/src/components/fields/OptionalDataControlsField.tsx +6 -4
- package/src/components/fields/SchemaField.tsx +66 -51
- package/src/components/fields/StringField.tsx +4 -15
- package/src/components/fields/index.ts +3 -1
- package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +2 -9
- package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +2 -7
- package/src/components/templates/ArrayFieldItemTemplate.tsx +3 -9
- package/src/components/templates/ArrayFieldTemplate.tsx +2 -9
- package/src/components/templates/ArrayFieldTitleTemplate.tsx +2 -10
- package/src/components/templates/BaseInputTemplate.tsx +16 -23
- package/src/components/templates/ButtonTemplates/AddButton.tsx +2 -1
- package/src/components/templates/ButtonTemplates/IconButton.tsx +16 -7
- package/src/components/templates/ButtonTemplates/SubmitButton.tsx +2 -1
- package/src/components/templates/ButtonTemplates/index.ts +1 -1
- package/src/components/templates/CyclicSchemaExpandTemplate.tsx +31 -0
- package/src/components/templates/DescriptionField.tsx +1 -1
- package/src/components/templates/ErrorList.tsx +8 -15
- package/src/components/templates/FallbackFieldTemplate.tsx +2 -1
- package/src/components/templates/FieldErrorTemplate.tsx +8 -8
- package/src/components/templates/FieldHelpTemplate.tsx +2 -1
- package/src/components/templates/FieldTemplate/FieldTemplate.tsx +2 -8
- package/src/components/templates/FieldTemplate/Label.tsx +2 -2
- package/src/components/templates/GridTemplate.tsx +1 -1
- package/src/components/templates/MultiSchemaFieldTemplate.tsx +1 -1
- package/src/components/templates/ObjectFieldTemplate.tsx +2 -7
- package/src/components/templates/OptionalDataControlsTemplate.tsx +3 -2
- package/src/components/templates/TitleField.tsx +1 -1
- package/src/components/templates/UnsupportedField.tsx +4 -3
- package/src/components/templates/WrapIfAdditionalTemplate.tsx +2 -9
- package/src/components/templates/index.ts +3 -1
- package/src/components/widgets/AltDateTimeWidget.tsx +1 -1
- package/src/components/widgets/AltDateWidget.tsx +7 -13
- package/src/components/widgets/CheckboxWidget.tsx +3 -5
- package/src/components/widgets/CheckboxesWidget.tsx +8 -10
- package/src/components/widgets/ColorWidget.tsx +2 -1
- package/src/components/widgets/DateTimeWidget.tsx +3 -10
- package/src/components/widgets/DateWidget.tsx +2 -1
- package/src/components/widgets/EmailWidget.tsx +2 -1
- package/src/components/widgets/FileWidget.tsx +9 -8
- package/src/components/widgets/HiddenWidget.tsx +1 -1
- package/src/components/widgets/PasswordWidget.tsx +2 -1
- package/src/components/widgets/RadioWidget.tsx +8 -10
- package/src/components/widgets/RangeWidget.tsx +1 -1
- package/src/components/widgets/RatingWidget.tsx +48 -47
- package/src/components/widgets/SelectWidget.tsx +48 -31
- package/src/components/widgets/TextWidget.tsx +2 -1
- package/src/components/widgets/TextareaWidget.tsx +10 -7
- package/src/components/widgets/TimeWidget.tsx +2 -1
- package/src/components/widgets/URLWidget.tsx +2 -1
- package/src/components/widgets/UpDownWidget.tsx +2 -1
- package/src/components/widgets/index.ts +1 -1
- package/src/getDefaultRegistry.ts +2 -9
- package/src/getTestRegistry.tsx +2 -7
- package/src/index.ts +10 -5
- package/src/withTheme.tsx +14 -9
package/src/components/Form.tsx
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import type { ElementType, FormEvent, ReactNode, Ref, RefObject } from 'react';
|
|
2
|
+
import { Component, createRef } from 'react';
|
|
3
|
+
import type {
|
|
4
4
|
CustomValidator,
|
|
5
|
-
deepEquals,
|
|
6
5
|
ErrorSchema,
|
|
7
|
-
ErrorSchemaBuilder,
|
|
8
6
|
ErrorTransformer,
|
|
9
7
|
FieldPathId,
|
|
10
8
|
FieldPathList,
|
|
11
9
|
FormContextType,
|
|
12
|
-
getChangedFields,
|
|
13
|
-
getTemplate,
|
|
14
|
-
getUiOptions,
|
|
15
|
-
isObject,
|
|
16
|
-
mergeObjects,
|
|
17
10
|
PathSchema,
|
|
18
11
|
StrictRJSFSchema,
|
|
19
12
|
Registry,
|
|
@@ -22,26 +15,37 @@ import {
|
|
|
22
15
|
RJSFSchema,
|
|
23
16
|
RJSFValidationError,
|
|
24
17
|
SchemaUtilsType,
|
|
18
|
+
TemplatesType,
|
|
19
|
+
UiSchema,
|
|
20
|
+
ValidationData,
|
|
21
|
+
ValidatorType,
|
|
22
|
+
Experimental_DefaultFormStateBehavior,
|
|
23
|
+
Experimental_CustomMergeAllOf,
|
|
24
|
+
GlobalFormOptions,
|
|
25
|
+
NameGeneratorFunction,
|
|
26
|
+
} from '@rjsf/utils';
|
|
27
|
+
import {
|
|
28
|
+
createSchemaUtils,
|
|
29
|
+
deepEquals,
|
|
30
|
+
ErrorSchemaBuilder,
|
|
31
|
+
getChangedFields,
|
|
32
|
+
getTemplate,
|
|
33
|
+
getUiOptions,
|
|
34
|
+
hashObject,
|
|
35
|
+
isObject,
|
|
36
|
+
mergeObjects,
|
|
25
37
|
shouldRender,
|
|
26
38
|
SUBMIT_BTN_OPTIONS_KEY,
|
|
27
|
-
TemplatesType,
|
|
28
39
|
toErrorList,
|
|
29
40
|
toFieldPathId,
|
|
30
|
-
UiSchema,
|
|
31
41
|
UI_DEFINITIONS_KEY,
|
|
32
42
|
UI_GLOBAL_OPTIONS_KEY,
|
|
33
43
|
UI_OPTIONS_KEY,
|
|
34
|
-
ValidationData,
|
|
35
44
|
validationDataMerge,
|
|
36
|
-
ValidatorType,
|
|
37
|
-
Experimental_DefaultFormStateBehavior,
|
|
38
|
-
Experimental_CustomMergeAllOf,
|
|
39
45
|
DEFAULT_ID_SEPARATOR,
|
|
40
46
|
DEFAULT_ID_PREFIX,
|
|
41
|
-
GlobalFormOptions,
|
|
42
47
|
ERRORS_KEY,
|
|
43
48
|
ID_KEY,
|
|
44
|
-
NameGeneratorFunction,
|
|
45
49
|
getUsedFormData,
|
|
46
50
|
getFieldNames,
|
|
47
51
|
ANY_OF_KEY,
|
|
@@ -193,6 +197,7 @@ export interface FormProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
193
197
|
*
|
|
194
198
|
* NOTE: In a future major release, the `boolean` options for this flag will be removed
|
|
195
199
|
*/
|
|
200
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
196
201
|
liveValidate?: 'onChange' | 'onBlur' | DeprecatedBooleanOption;
|
|
197
202
|
/** Flag that describes when live omit will be performed. Live omit happens only when `omitExtraData` is also set to
|
|
198
203
|
* to `true` and the form's data is updated by the user.
|
|
@@ -204,6 +209,7 @@ export interface FormProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
204
209
|
*
|
|
205
210
|
* NOTE: In a future major release, the `boolean` options for this flag will be removed
|
|
206
211
|
*/
|
|
212
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
207
213
|
liveOmit?: 'onChange' | 'onBlur' | DeprecatedBooleanOption;
|
|
208
214
|
/** If set to true, then extra form data values that are not in any form field will be removed whenever `onSubmit` is
|
|
209
215
|
* called. Set to `false` by default.
|
|
@@ -315,7 +321,7 @@ export interface FormState<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
315
321
|
/** The registry (re)computed only when props changed */
|
|
316
322
|
registry: Registry<T, S, F>;
|
|
317
323
|
/** Tracks the previous `extraErrors` prop reference so that `getDerivedStateFromProps` can detect changes */
|
|
318
|
-
|
|
324
|
+
prevExtraErrors?: ErrorSchema<T>;
|
|
319
325
|
}
|
|
320
326
|
|
|
321
327
|
/** The event data passed when changes have been made to the form, includes everything from the `FormState` except
|
|
@@ -380,7 +386,7 @@ export default class Form<
|
|
|
380
386
|
/** Flag to track when we're processing a user-initiated field change.
|
|
381
387
|
* This prevents componentDidUpdate from reverting oneOf/anyOf option switches.
|
|
382
388
|
*/
|
|
383
|
-
private
|
|
389
|
+
private isProcessingUserChange = false;
|
|
384
390
|
|
|
385
391
|
/** When the `extraErrors` prop changes, re-merges `schemaValidationErrors` + `extraErrors` + `customErrors` into
|
|
386
392
|
* state before render, ensuring the updated errors are visible immediately in a single render cycle.
|
|
@@ -393,10 +399,10 @@ export default class Form<
|
|
|
393
399
|
props: FormProps<T, S, F>,
|
|
394
400
|
state: FormState<T, S, F>,
|
|
395
401
|
): Partial<FormState<T, S, F>> | null {
|
|
396
|
-
if (props.extraErrors !== state.
|
|
402
|
+
if (props.extraErrors !== state.prevExtraErrors) {
|
|
397
403
|
const baseErrors: ValidationData<T> = {
|
|
398
404
|
errors: state.schemaValidationErrors || [],
|
|
399
|
-
errorSchema:
|
|
405
|
+
errorSchema: state.schemaValidationErrorSchema || {},
|
|
400
406
|
};
|
|
401
407
|
let { errors, errorSchema } = baseErrors;
|
|
402
408
|
if (props.extraErrors) {
|
|
@@ -409,7 +415,7 @@ export default class Form<
|
|
|
409
415
|
true,
|
|
410
416
|
));
|
|
411
417
|
}
|
|
412
|
-
return {
|
|
418
|
+
return { prevExtraErrors: props.extraErrors, errors, errorSchema };
|
|
413
419
|
}
|
|
414
420
|
return null;
|
|
415
421
|
}
|
|
@@ -431,7 +437,7 @@ export default class Form<
|
|
|
431
437
|
const formData = propsFormData ?? initialFormData;
|
|
432
438
|
this.state = {
|
|
433
439
|
...this.getStateFromProps(props, formData, undefined, undefined, undefined, true),
|
|
434
|
-
|
|
440
|
+
prevExtraErrors: props.extraErrors,
|
|
435
441
|
};
|
|
436
442
|
if (onChange && !deepEquals(this.state.formData, formData)) {
|
|
437
443
|
onChange(toIChangeEvent(this.state));
|
|
@@ -515,8 +521,8 @@ export default class Form<
|
|
|
515
521
|
// Prevent oneOf/anyOf option switches from reverting when getStateFromProps
|
|
516
522
|
// re-evaluates and produces stale formData.
|
|
517
523
|
const nextStateDiffersFromProps = !deepEquals(nextState.formData, this.props.formData);
|
|
518
|
-
const wasProcessingUserChange = this.
|
|
519
|
-
this.
|
|
524
|
+
const wasProcessingUserChange = this.isProcessingUserChange;
|
|
525
|
+
this.isProcessingUserChange = false;
|
|
520
526
|
|
|
521
527
|
if (wasProcessingUserChange && nextStateDiffersFromProps) {
|
|
522
528
|
// Skip - the user's option switch is already applied via processPendingChange
|
|
@@ -526,6 +532,7 @@ export default class Form<
|
|
|
526
532
|
if (nextStateDiffersFromProps && !deepEquals(nextState.formData, prevState.formData) && this.props.onChange) {
|
|
527
533
|
this.props.onChange(toIChangeEvent(nextState));
|
|
528
534
|
}
|
|
535
|
+
// oxlint-disable-next-line react/no-did-update-set-state -- guarded to prevent infinite loop
|
|
529
536
|
this.setState(nextState);
|
|
530
537
|
}
|
|
531
538
|
}
|
|
@@ -537,9 +544,10 @@ export default class Form<
|
|
|
537
544
|
* @param props - The props passed to the `Form`
|
|
538
545
|
* @param inputFormData - The new or current data for the `Form`
|
|
539
546
|
* @param retrievedSchema - An expanded schema, if not provided, it will be retrieved from the `schema` and `formData`.
|
|
540
|
-
* @param isSchemaChanged - A flag indicating whether the schema has changed.
|
|
541
|
-
* @param formDataChangedFields - The changed fields of `formData`
|
|
542
|
-
* @param skipLiveValidate - Optional flag, if true, means that we are not running live validation
|
|
547
|
+
* @param [isSchemaChanged=false] - A flag indicating whether the schema has changed.
|
|
548
|
+
* @param [formDataChangedFields=[]] - The changed fields of `formData`
|
|
549
|
+
* @param [skipLiveValidate=false] - Optional flag, if true, means that we are not running live validation
|
|
550
|
+
* @param [shouldSanitize=false] - Optional flag, if true, means that we should attempt to sanitize formData
|
|
543
551
|
* @returns - The new state for the `Form`
|
|
544
552
|
*/
|
|
545
553
|
getStateFromProps(
|
|
@@ -549,6 +557,7 @@ export default class Form<
|
|
|
549
557
|
isSchemaChanged = false,
|
|
550
558
|
formDataChangedFields: string[] = [],
|
|
551
559
|
skipLiveValidate = false,
|
|
560
|
+
shouldSanitize = false,
|
|
552
561
|
): FormState<T, S, F> {
|
|
553
562
|
const state: FormState<T, S, F> = this.state || {};
|
|
554
563
|
const schema = 'schema' in props ? props.schema : this.props.schema;
|
|
@@ -557,6 +566,7 @@ export default class Form<
|
|
|
557
566
|
const isUncontrolled = props.formData === undefined && this.props.formData === undefined;
|
|
558
567
|
const edit = typeof inputFormData !== 'undefined';
|
|
559
568
|
const liveValidate = 'liveValidate' in props ? props.liveValidate : this.props.liveValidate;
|
|
569
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
560
570
|
const mustValidate = edit && !props.noValidate && liveValidate;
|
|
561
571
|
const experimental_defaultFormStateBehavior =
|
|
562
572
|
'experimental_defaultFormStateBehavior' in props
|
|
@@ -566,7 +576,7 @@ export default class Form<
|
|
|
566
576
|
'experimental_customMergeAllOf' in props
|
|
567
577
|
? props.experimental_customMergeAllOf
|
|
568
578
|
: this.props.experimental_customMergeAllOf;
|
|
569
|
-
let schemaUtils
|
|
579
|
+
let { schemaUtils } = state;
|
|
570
580
|
if (
|
|
571
581
|
!schemaUtils ||
|
|
572
582
|
schemaUtils.doesSchemaUtilsDiffer(
|
|
@@ -593,21 +603,57 @@ export default class Form<
|
|
|
593
603
|
} else if (inputFormData === undefined && isUncontrolled) {
|
|
594
604
|
defaultsFormData = state.formData;
|
|
595
605
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
606
|
+
let formData: T;
|
|
607
|
+
let computedRetrievedSchema: S;
|
|
608
|
+
let wasSanitized = false;
|
|
609
|
+
const preventInfiniteSanitize: string[] = [];
|
|
610
|
+
do {
|
|
611
|
+
formData = schemaUtils.getDefaultFormState(
|
|
612
|
+
rootSchema,
|
|
613
|
+
defaultsFormData,
|
|
614
|
+
false,
|
|
615
|
+
state.initialDefaultsGenerated,
|
|
616
|
+
) as T;
|
|
617
|
+
// Only hash when sanitizing, wrapping `formData` in an object to deal with a scalar/undefined value
|
|
618
|
+
const formHash = shouldSanitize ? hashObject({ formData }) : '';
|
|
619
|
+
computedRetrievedSchema = this.updateRetrievedSchema(
|
|
620
|
+
retrievedSchema ?? schemaUtils.retrieveSchema(rootSchema, formData),
|
|
621
|
+
);
|
|
622
|
+
if (
|
|
623
|
+
shouldSanitize &&
|
|
624
|
+
!preventInfiniteSanitize.includes(formHash) &&
|
|
625
|
+
!deepEquals(computedRetrievedSchema, state.retrievedSchema)
|
|
626
|
+
) {
|
|
627
|
+
// Sanitize the form data if shouldSanitize is true, we haven't already processed this same formData AND
|
|
628
|
+
// we have a different retrieved schema from when we last ran the state
|
|
629
|
+
const sanitizedFormData = schemaUtils.sanitizeDataForNewSchema(
|
|
630
|
+
computedRetrievedSchema,
|
|
631
|
+
state.retrievedSchema,
|
|
632
|
+
formData,
|
|
633
|
+
);
|
|
634
|
+
wasSanitized = !deepEquals(sanitizedFormData, formData);
|
|
635
|
+
if (wasSanitized) {
|
|
636
|
+
// Update both the formData AND defaultsFormData due to the sanitize so the loop works with the new data
|
|
637
|
+
formData = sanitizedFormData;
|
|
638
|
+
defaultsFormData = sanitizedFormData;
|
|
639
|
+
const sanitizedFormHash = hashObject({ formData: sanitizedFormData });
|
|
640
|
+
// If we've seen the sanitized data before, we are done
|
|
641
|
+
wasSanitized = !preventInfiniteSanitize.includes(sanitizedFormHash);
|
|
642
|
+
preventInfiniteSanitize.push(sanitizedFormHash);
|
|
643
|
+
}
|
|
644
|
+
preventInfiniteSanitize.push(formHash);
|
|
645
|
+
} else {
|
|
646
|
+
wasSanitized = false;
|
|
647
|
+
}
|
|
648
|
+
} while (wasSanitized);
|
|
605
649
|
|
|
606
650
|
const getCurrentErrors = (): ValidationData<T> => {
|
|
607
651
|
// If the `props.noValidate` option is set or the schema has changed, we reset the error state.
|
|
652
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
608
653
|
if (props.noValidate || isSchemaChanged) {
|
|
609
654
|
return { errors: [], errorSchema: {} };
|
|
610
|
-
}
|
|
655
|
+
}
|
|
656
|
+
if (!props.liveValidate) {
|
|
611
657
|
return {
|
|
612
658
|
errors: state.schemaValidationErrors || [],
|
|
613
659
|
errorSchema: state.schemaValidationErrorSchema || {},
|
|
@@ -621,8 +667,7 @@ export default class Form<
|
|
|
621
667
|
|
|
622
668
|
let errors: RJSFValidationError[];
|
|
623
669
|
let errorSchema: ErrorSchema<T> | undefined;
|
|
624
|
-
let schemaValidationErrors
|
|
625
|
-
let schemaValidationErrorSchema: ErrorSchema<T> = state.schemaValidationErrorSchema;
|
|
670
|
+
let { schemaValidationErrors, schemaValidationErrorSchema } = state;
|
|
626
671
|
// If we are skipping live validate, it means that the state has already been updated with live validation errors
|
|
627
672
|
if (mustValidate && !skipLiveValidate) {
|
|
628
673
|
const liveValidation = this.liveValidate(
|
|
@@ -647,26 +692,24 @@ export default class Form<
|
|
|
647
692
|
errorSchema = currentErrors.errorSchema;
|
|
648
693
|
// We only update the error schema for changed fields if mustValidate is false
|
|
649
694
|
if (formDataChangedFields.length > 0 && !mustValidate) {
|
|
650
|
-
const newErrorSchema = formDataChangedFields.reduce(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
{} as Record<string, undefined>,
|
|
656
|
-
);
|
|
657
|
-
errorSchema = schemaValidationErrorSchema = mergeObjects(
|
|
695
|
+
const newErrorSchema = formDataChangedFields.reduce<Record<string, undefined>>((acc, key) => {
|
|
696
|
+
acc[key] = undefined;
|
|
697
|
+
return acc;
|
|
698
|
+
}, {});
|
|
699
|
+
schemaValidationErrorSchema = mergeObjects(
|
|
658
700
|
currentErrors.errorSchema,
|
|
659
701
|
newErrorSchema,
|
|
660
702
|
'preventDuplicates',
|
|
661
703
|
) as ErrorSchema<T>;
|
|
704
|
+
errorSchema = schemaValidationErrorSchema;
|
|
662
705
|
}
|
|
663
|
-
const mergedErrors =
|
|
706
|
+
const mergedErrors = Form.mergeErrors<T>({ errorSchema, errors }, props.extraErrors, state.customErrors);
|
|
664
707
|
errors = mergedErrors.errors;
|
|
665
708
|
errorSchema = mergedErrors.errorSchema;
|
|
666
709
|
}
|
|
667
710
|
|
|
668
711
|
// Only store a new registry when the props cause a different one to be created
|
|
669
|
-
const newRegistry =
|
|
712
|
+
const newRegistry = Form.getRegistry(props, rootSchema, schemaUtils);
|
|
670
713
|
const registry = deepEquals(state.registry, newRegistry) ? state.registry : newRegistry;
|
|
671
714
|
|
|
672
715
|
// Only compute a new `fieldPathId` when the `idPrefix` is different than the existing fieldPathId's ID_KEY
|
|
@@ -685,7 +728,7 @@ export default class Form<
|
|
|
685
728
|
errorSchema,
|
|
686
729
|
schemaValidationErrors,
|
|
687
730
|
schemaValidationErrorSchema,
|
|
688
|
-
retrievedSchema:
|
|
731
|
+
retrievedSchema: computedRetrievedSchema,
|
|
689
732
|
initialDefaultsGenerated: true,
|
|
690
733
|
registry,
|
|
691
734
|
};
|
|
@@ -717,12 +760,19 @@ export default class Form<
|
|
|
717
760
|
altSchemaUtils?: SchemaUtilsType<T, S, F>,
|
|
718
761
|
retrievedSchema?: S,
|
|
719
762
|
): ValidationData<T> {
|
|
720
|
-
const schemaUtils = altSchemaUtils
|
|
763
|
+
const schemaUtils = altSchemaUtils || this.state.schemaUtils;
|
|
721
764
|
const { customValidate, transformErrors, uiSchema } = this.props;
|
|
722
|
-
|
|
765
|
+
// When a pre-resolved schema is provided (e.g., from live validation), use it directly.
|
|
766
|
+
// Otherwise validate against the original schema so AJV sees the full constraint set.
|
|
767
|
+
const validationSchema = retrievedSchema ?? schema;
|
|
768
|
+
|
|
769
|
+
// JSON.stringify drops keys with `undefined` values; JSON.parse on the result gives AJV a clean
|
|
770
|
+
// object that avoids spurious type errors for `type: "string"` fields that were cleared (#4518).
|
|
771
|
+
const validationFormData = formData ? JSON.parse(JSON.stringify(formData)) : undefined;
|
|
772
|
+
|
|
723
773
|
return schemaUtils
|
|
724
774
|
.getValidator()
|
|
725
|
-
.validateFormData(
|
|
775
|
+
.validateFormData(validationFormData, validationSchema, customValidate, transformErrors, uiSchema);
|
|
726
776
|
}
|
|
727
777
|
|
|
728
778
|
/** Renders any errors contained in the `state` in using the `ErrorList`, if not disabled by `showErrorList`. */
|
|
@@ -731,7 +781,7 @@ export default class Form<
|
|
|
731
781
|
const options = getUiOptions<T, S, F>(uiSchema);
|
|
732
782
|
const ErrorListTemplate = getTemplate<'ErrorListTemplate', T, S, F>('ErrorListTemplate', registry, options);
|
|
733
783
|
|
|
734
|
-
if (errors
|
|
784
|
+
if (errors?.length) {
|
|
735
785
|
return (
|
|
736
786
|
<ErrorListTemplate
|
|
737
787
|
errors={errors}
|
|
@@ -753,13 +803,12 @@ export default class Form<
|
|
|
753
803
|
* @return - The `extraErrors` and `customErrors` merged into the `schemaValidation`
|
|
754
804
|
* @private
|
|
755
805
|
*/
|
|
756
|
-
private mergeErrors(
|
|
806
|
+
private static mergeErrors<T = any>(
|
|
757
807
|
schemaValidation: ValidationData<T>,
|
|
758
808
|
extraErrors?: FormProps['extraErrors'],
|
|
759
809
|
customErrors?: ErrorSchemaBuilder,
|
|
760
810
|
): ValidationData<T> {
|
|
761
|
-
let errorSchema
|
|
762
|
-
let errors: RJSFValidationError[] = schemaValidation.errors;
|
|
811
|
+
let { errorSchema, errors } = schemaValidation;
|
|
763
812
|
if (extraErrors) {
|
|
764
813
|
const merged = validationDataMerge(schemaValidation, extraErrors);
|
|
765
814
|
errorSchema = merged.errorSchema;
|
|
@@ -798,8 +847,8 @@ export default class Form<
|
|
|
798
847
|
mergeIntoOriginalErrorSchema = false,
|
|
799
848
|
) {
|
|
800
849
|
const schemaValidation = this.validate(formData, rootSchema, schemaUtils, retrievedSchema);
|
|
801
|
-
const errors = schemaValidation
|
|
802
|
-
let errorSchema = schemaValidation
|
|
850
|
+
const { errors } = schemaValidation;
|
|
851
|
+
let { errorSchema } = schemaValidation;
|
|
803
852
|
// We merge 'originalErrorSchema' with 'schemaValidation.errorSchema.'; This done to display the raised field error.
|
|
804
853
|
if (mergeIntoOriginalErrorSchema) {
|
|
805
854
|
errorSchema = mergeObjects(
|
|
@@ -810,7 +859,7 @@ export default class Form<
|
|
|
810
859
|
}
|
|
811
860
|
const schemaValidationErrors = errors;
|
|
812
861
|
const schemaValidationErrorSchema = errorSchema;
|
|
813
|
-
const mergedErrors =
|
|
862
|
+
const mergedErrors = Form.mergeErrors<T>({ errorSchema, errors }, extraErrors, customErrors);
|
|
814
863
|
return { ...mergedErrors, schemaValidationErrors, schemaValidationErrorSchema };
|
|
815
864
|
}
|
|
816
865
|
|
|
@@ -820,9 +869,8 @@ export default class Form<
|
|
|
820
869
|
* @param fields - The fields to keep while filtering
|
|
821
870
|
* @deprecated - To be removed as an exported `Form` function in a future release; there isn't a planned replacement
|
|
822
871
|
*/
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
};
|
|
872
|
+
// oxlint-disable-next-line class-methods-use-this, typescript/no-deprecated
|
|
873
|
+
getUsedFormData = (formData: T | undefined, fields: string[]): T | undefined => getUsedFormData(formData, fields);
|
|
826
874
|
|
|
827
875
|
/** Returns the list of field names from inspecting the `pathSchema` as well as using the `formData`
|
|
828
876
|
*
|
|
@@ -830,9 +878,8 @@ export default class Form<
|
|
|
830
878
|
* @param [formData] - The form data to use while checking for empty objects/arrays
|
|
831
879
|
* @deprecated - To be removed as an exported `Form` function in a future release; there isn't a planned replacement
|
|
832
880
|
*/
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
};
|
|
881
|
+
// oxlint-disable-next-line class-methods-use-this, typescript/no-deprecated
|
|
882
|
+
getFieldNames = (pathSchema: PathSchema<T>, formData?: T): string[][] => getFieldNames(pathSchema, formData);
|
|
836
883
|
|
|
837
884
|
/** Returns the `formData` after filtering to remove any extra data not in a form field
|
|
838
885
|
*
|
|
@@ -890,20 +937,20 @@ export default class Form<
|
|
|
890
937
|
}
|
|
891
938
|
// Mark that we're processing a user-initiated change.
|
|
892
939
|
// This prevents componentDidUpdate from reverting oneOf/anyOf option switches.
|
|
893
|
-
this.
|
|
940
|
+
this.isProcessingUserChange = true;
|
|
894
941
|
const { newValue, path, id } = this.pendingChanges[0];
|
|
895
942
|
const { newErrorSchema } = this.pendingChanges[0];
|
|
896
|
-
|
|
943
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
944
|
+
const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange, disabled, readonly } = this.props;
|
|
897
945
|
const { formData: oldFormData, schemaUtils, schema, fieldPathId, schemaValidationErrorSchema, errors } = this.state;
|
|
898
|
-
let { customErrors } = this.state;
|
|
946
|
+
let { customErrors, retrievedSchema } = this.state;
|
|
899
947
|
// Use the un-merged AJV-only schema as the base for re-merging extraErrors. Mirrors the
|
|
900
948
|
// pattern in getStateFromProps/getDerivedStateFromProps and avoids the duplication that
|
|
901
949
|
// happened when state.errorSchema (already containing merged extraErrors) was passed in.
|
|
902
|
-
let mergeBaseErrorSchema: ErrorSchema<T> = schemaValidationErrorSchema
|
|
950
|
+
let mergeBaseErrorSchema: ErrorSchema<T> = schemaValidationErrorSchema;
|
|
903
951
|
const rootPathId = fieldPathId.path[0] || '';
|
|
904
952
|
|
|
905
953
|
const isRootPath = !path || path.length === 0 || (path.length === 1 && path[0] === rootPathId);
|
|
906
|
-
let retrievedSchema = this.state.retrievedSchema;
|
|
907
954
|
let formData = isRootPath ? newValue : _cloneDeep(oldFormData);
|
|
908
955
|
|
|
909
956
|
// When switching from null to an object option in oneOf, MultiSchemaField sends
|
|
@@ -918,12 +965,18 @@ export default class Form<
|
|
|
918
965
|
const inputForDefaults = hasOnlyUndefinedValues && wasPreviouslyNull ? undefined : formData;
|
|
919
966
|
|
|
920
967
|
if (isObject(formData) || Array.isArray(formData)) {
|
|
968
|
+
// Tracks if the user cleared a plain (non-oneOf/anyOf) leaf field.
|
|
969
|
+
// The key is removed twice: once before getStateFromProps so inputForDefaults
|
|
970
|
+
// reflects an empty field for conditional schema resolution, and once after so
|
|
971
|
+
// the user's clear overrides any schema default getStateFromProps re-applied (#5125)
|
|
972
|
+
// and AJV never receives { key: undefined } for type:"string" fields (#4518).
|
|
973
|
+
let plainLeafWasCleared = false;
|
|
974
|
+
|
|
921
975
|
if (newValue === ADDITIONAL_PROPERTY_KEY_REMOVE) {
|
|
922
|
-
// For additional properties,
|
|
976
|
+
// For additional properties, this key was explicitly removed, so unset it
|
|
923
977
|
_unset(formData, path);
|
|
924
978
|
} else if (!isRootPath) {
|
|
925
|
-
//
|
|
926
|
-
let unsetPath = false;
|
|
979
|
+
// Set the new value at its path in the form data.
|
|
927
980
|
let valueForPath: T | null | undefined = newValue;
|
|
928
981
|
|
|
929
982
|
if (newValue === undefined) {
|
|
@@ -935,37 +988,54 @@ export default class Form<
|
|
|
935
988
|
const { field } = schemaUtils.findFieldInSchema(schema, path, oldFormData);
|
|
936
989
|
const leaf = field as RJSFSchema | undefined;
|
|
937
990
|
const isOneOfOrAnyOfLeaf = leaf && (ONE_OF_KEY in leaf || ANY_OF_KEY in leaf);
|
|
938
|
-
//
|
|
939
|
-
//
|
|
940
|
-
//
|
|
941
|
-
// mergeDefaults does not immediately re-apply a branch default when clearing those widgets.
|
|
991
|
+
// oneOf/anyOf and unresolved leaves keep `undefined` so mergeDefaults doesn't
|
|
992
|
+
// re-apply a branch default when the user clears the widget.
|
|
993
|
+
// Plain resolved leaves use plainLeafWasCleared instead (see below).
|
|
942
994
|
if (!isOneOfOrAnyOfLeaf && leaf !== undefined) {
|
|
943
|
-
|
|
995
|
+
plainLeafWasCleared = true;
|
|
944
996
|
}
|
|
945
997
|
}
|
|
946
998
|
}
|
|
947
999
|
|
|
948
|
-
if (
|
|
949
|
-
|
|
1000
|
+
if (plainLeafWasCleared) {
|
|
1001
|
+
_set(formData, path, undefined);
|
|
950
1002
|
} else {
|
|
951
1003
|
_set(formData, path, valueForPath);
|
|
952
1004
|
}
|
|
953
1005
|
}
|
|
954
|
-
|
|
955
|
-
|
|
1006
|
+
const shouldSanitize =
|
|
1007
|
+
retrievedSchema && !isRootPath && !isObject(newValue) && !Array.isArray(newValue) && !disabled && !readonly;
|
|
1008
|
+
// Skip live validation here; it runs later in this function.
|
|
1009
|
+
const newState = this.getStateFromProps(
|
|
1010
|
+
this.props,
|
|
1011
|
+
inputForDefaults,
|
|
1012
|
+
undefined,
|
|
1013
|
+
undefined,
|
|
1014
|
+
undefined,
|
|
1015
|
+
true,
|
|
1016
|
+
shouldSanitize,
|
|
1017
|
+
);
|
|
956
1018
|
formData = newState.formData;
|
|
957
1019
|
retrievedSchema = newState.retrievedSchema;
|
|
1020
|
+
|
|
1021
|
+
// Re-set to undefined after merging defaults so the user's clear is preserved in
|
|
1022
|
+
// state (#5125 regression: without this, clearing a second field re-applies the
|
|
1023
|
+
// default to previously-cleared fields). The undefined key is stripped from the
|
|
1024
|
+
// formData copy passed to AJV via JSON.parse(JSON.stringify(...)) so the validator never
|
|
1025
|
+
// sees { [key]: undefined } for type:"string" or patternProperties fields (#4518).
|
|
1026
|
+
if (plainLeafWasCleared && formData) {
|
|
1027
|
+
_set(formData, path, undefined);
|
|
1028
|
+
}
|
|
958
1029
|
}
|
|
959
1030
|
|
|
960
1031
|
const mustValidate = !noValidate && (liveValidate === true || liveValidate === 'onChange');
|
|
961
|
-
let state: Partial<FormState<T, S, F>> = { formData,
|
|
1032
|
+
let state: Partial<FormState<T, S, F>> = { formData, retrievedSchema };
|
|
962
1033
|
let newFormData = formData;
|
|
963
1034
|
|
|
964
1035
|
if (omitExtraData === true && (liveOmit === true || liveOmit === 'onChange')) {
|
|
1036
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
965
1037
|
newFormData = this.omitExtraData(formData);
|
|
966
|
-
state = {
|
|
967
|
-
formData: newFormData,
|
|
968
|
-
};
|
|
1038
|
+
state = { ...state, formData: newFormData };
|
|
969
1039
|
}
|
|
970
1040
|
|
|
971
1041
|
if (newErrorSchema) {
|
|
@@ -977,20 +1047,20 @@ export default class Form<
|
|
|
977
1047
|
// Apply the user-supplied newErrorSchema onto a clone of the AJV-only base, so that
|
|
978
1048
|
// mergeErrors below sees the user's error at this path without mutating shared state.
|
|
979
1049
|
if (!isRootPath) {
|
|
980
|
-
mergeBaseErrorSchema = _cloneDeep(schemaValidationErrorSchema)
|
|
1050
|
+
mergeBaseErrorSchema = _cloneDeep(schemaValidationErrorSchema);
|
|
981
1051
|
_set(mergeBaseErrorSchema, path, newErrorSchema);
|
|
982
1052
|
} else {
|
|
983
|
-
mergeBaseErrorSchema = newErrorSchema
|
|
1053
|
+
mergeBaseErrorSchema = newErrorSchema;
|
|
984
1054
|
}
|
|
985
1055
|
} else {
|
|
986
1056
|
if (!customErrors) {
|
|
987
1057
|
customErrors = new ErrorSchemaBuilder<T>();
|
|
988
1058
|
}
|
|
989
1059
|
if (isRootPath) {
|
|
990
|
-
const
|
|
991
|
-
if (
|
|
1060
|
+
const pathErrors = _get(newErrorSchema, ERRORS_KEY);
|
|
1061
|
+
if (pathErrors) {
|
|
992
1062
|
// only set errors when there are some
|
|
993
|
-
customErrors.setErrors(
|
|
1063
|
+
customErrors.setErrors(pathErrors);
|
|
994
1064
|
}
|
|
995
1065
|
} else {
|
|
996
1066
|
_set(customErrors.ErrorSchema, path, newErrorSchema);
|
|
@@ -1011,16 +1081,17 @@ export default class Form<
|
|
|
1011
1081
|
customErrors,
|
|
1012
1082
|
retrievedSchema,
|
|
1013
1083
|
);
|
|
1014
|
-
state = { formData: newFormData, ...liveValidation, customErrors };
|
|
1084
|
+
state = { ...state, formData: newFormData, ...liveValidation, customErrors };
|
|
1015
1085
|
} else if (!noValidate && newErrorSchema) {
|
|
1016
1086
|
// Merging 'newErrorSchema' into 'errorSchema' to display the custom raised errors.
|
|
1017
|
-
const mergedErrors =
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
...mergedErrors,
|
|
1087
|
+
const mergedErrors = Form.mergeErrors<T>(
|
|
1088
|
+
{ errorSchema: mergeBaseErrorSchema, errors },
|
|
1089
|
+
extraErrors,
|
|
1021
1090
|
customErrors,
|
|
1022
|
-
|
|
1091
|
+
);
|
|
1092
|
+
state = { ...state, formData: newFormData, ...mergedErrors, customErrors };
|
|
1023
1093
|
}
|
|
1094
|
+
|
|
1024
1095
|
this.setState(state as FormState<T, S, F>, () => {
|
|
1025
1096
|
if (onChange) {
|
|
1026
1097
|
onChange(toIChangeEvent({ ...this.state, ...state }), id);
|
|
@@ -1073,7 +1144,7 @@ export default class Form<
|
|
|
1073
1144
|
customErrors: undefined,
|
|
1074
1145
|
} as FormState<T, S, F>;
|
|
1075
1146
|
|
|
1076
|
-
this.setState(state, () => onChange
|
|
1147
|
+
this.setState(state, () => onChange?.(toIChangeEvent({ ...this.state, ...state })));
|
|
1077
1148
|
};
|
|
1078
1149
|
|
|
1079
1150
|
/** Callback function to handle when a field on the form is blurred. Calls the `onBlur` callback for the `Form` if it
|
|
@@ -1094,6 +1165,7 @@ export default class Form<
|
|
|
1094
1165
|
let newFormData: T | undefined = formData;
|
|
1095
1166
|
let state: Partial<FormState<T, S, F>> = { formData: newFormData };
|
|
1096
1167
|
if (omitExtraData === true && liveOmit === 'onBlur') {
|
|
1168
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
1097
1169
|
newFormData = this.omitExtraData(formData);
|
|
1098
1170
|
state = { formData: newFormData };
|
|
1099
1171
|
}
|
|
@@ -1154,10 +1226,12 @@ export default class Form<
|
|
|
1154
1226
|
}
|
|
1155
1227
|
|
|
1156
1228
|
event.persist();
|
|
1229
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
1157
1230
|
const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;
|
|
1158
1231
|
let { formData: newFormData } = this.state;
|
|
1159
1232
|
|
|
1160
1233
|
if (omitExtraData === true) {
|
|
1234
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
1161
1235
|
newFormData = this.omitExtraData(newFormData);
|
|
1162
1236
|
}
|
|
1163
1237
|
|
|
@@ -1189,7 +1263,11 @@ export default class Form<
|
|
|
1189
1263
|
* @returns - The `GlobalFormOptions` from the props
|
|
1190
1264
|
* @private
|
|
1191
1265
|
*/
|
|
1192
|
-
private getGlobalFormOptions
|
|
1266
|
+
private static getGlobalFormOptions<
|
|
1267
|
+
T = any,
|
|
1268
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
1269
|
+
F extends FormContextType = any,
|
|
1270
|
+
>(props: FormProps<T, S, F>): GlobalFormOptions {
|
|
1193
1271
|
const {
|
|
1194
1272
|
uiSchema = {},
|
|
1195
1273
|
experimental_componentUpdateStrategy,
|
|
@@ -1198,6 +1276,7 @@ export default class Form<
|
|
|
1198
1276
|
nameGenerator,
|
|
1199
1277
|
useFallbackUiForUnsupportedType = false,
|
|
1200
1278
|
} = props;
|
|
1279
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
1201
1280
|
const rootFieldId = uiSchema['ui:rootFieldId'];
|
|
1202
1281
|
// Omit any options that are undefined or null
|
|
1203
1282
|
return {
|
|
@@ -1210,7 +1289,11 @@ export default class Form<
|
|
|
1210
1289
|
}
|
|
1211
1290
|
|
|
1212
1291
|
/** Computed the registry for the form using the given `props`, `schema` and `schemaUtils` */
|
|
1213
|
-
getRegistry
|
|
1292
|
+
static getRegistry<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
1293
|
+
props: FormProps<T, S, F>,
|
|
1294
|
+
schema: S,
|
|
1295
|
+
schemaUtils: SchemaUtilsType<T, S, F>,
|
|
1296
|
+
): Registry<T, S, F> {
|
|
1214
1297
|
const { translateString: customTranslateString, uiSchema = {} } = props;
|
|
1215
1298
|
const { fields, templates, widgets, formContext, translateString } = getDefaultRegistry<T, S, F>();
|
|
1216
1299
|
return {
|
|
@@ -1229,7 +1312,7 @@ export default class Form<
|
|
|
1229
1312
|
schemaUtils,
|
|
1230
1313
|
translateString: customTranslateString || translateString,
|
|
1231
1314
|
globalUiOptions: uiSchema[UI_GLOBAL_OPTIONS_KEY],
|
|
1232
|
-
globalFormOptions:
|
|
1315
|
+
globalFormOptions: Form.getGlobalFormOptions(props),
|
|
1233
1316
|
uiSchemaDefinitions: uiSchema[UI_DEFINITIONS_KEY] ?? {},
|
|
1234
1317
|
};
|
|
1235
1318
|
}
|
|
@@ -1271,8 +1354,9 @@ export default class Form<
|
|
|
1271
1354
|
// some themes (e.g. shadcn) use button elements instead of native inputs for radio groups
|
|
1272
1355
|
field = this.formElement.current.querySelector(`input[id^="${elementId}"], button[id^="${elementId}"]`);
|
|
1273
1356
|
}
|
|
1274
|
-
if (field
|
|
1357
|
+
if (field?.length) {
|
|
1275
1358
|
// If we got a list with length > 0
|
|
1359
|
+
// oxlint-disable-next-line prefer-destructuring
|
|
1276
1360
|
field = field[0];
|
|
1277
1361
|
}
|
|
1278
1362
|
if (field) {
|
|
@@ -1291,7 +1375,7 @@ export default class Form<
|
|
|
1291
1375
|
const { errors: prevErrors } = this.state;
|
|
1292
1376
|
const schemaValidation = this.validate(formData);
|
|
1293
1377
|
// Always merge extraErrors so they remain visible in state regardless of extraErrorsBlockSubmit.
|
|
1294
|
-
const { errors, errorSchema } = extraErrors ?
|
|
1378
|
+
const { errors, errorSchema } = extraErrors ? Form.mergeErrors<T>(schemaValidation, extraErrors) : schemaValidation;
|
|
1295
1379
|
// hasError gates submission: schema errors always block; extraErrors only block when
|
|
1296
1380
|
// extraErrorsBlockSubmit is set (non-breaking default: extraErrors are informational only).
|
|
1297
1381
|
const hasError = schemaValidation.errors.length > 0 || (extraErrors && extraErrorsBlockSubmit);
|
|
@@ -1314,6 +1398,7 @@ export default class Form<
|
|
|
1314
1398
|
if (onError) {
|
|
1315
1399
|
onError(errors);
|
|
1316
1400
|
} else {
|
|
1401
|
+
// oxlint-disable-next-line no-console
|
|
1317
1402
|
console.error('Form validation failed', errors);
|
|
1318
1403
|
}
|
|
1319
1404
|
},
|
|
@@ -1347,6 +1432,7 @@ export default class Form<
|
|
|
1347
1432
|
const { omitExtraData } = this.props;
|
|
1348
1433
|
let { formData: newFormData } = this.state;
|
|
1349
1434
|
if (omitExtraData === true) {
|
|
1435
|
+
// oxlint-disable-next-line typescript/no-deprecated
|
|
1350
1436
|
newFormData = this.omitExtraData(newFormData);
|
|
1351
1437
|
}
|
|
1352
1438
|
return this.validateFormWithFormData(newFormData);
|
|
@@ -1376,7 +1462,7 @@ export default class Form<
|
|
|
1376
1462
|
} = this.props;
|
|
1377
1463
|
|
|
1378
1464
|
const { schema, uiSchema, formData, errorSchema, fieldPathId, registry } = this.state;
|
|
1379
|
-
const { SchemaField:
|
|
1465
|
+
const { SchemaField: SchemaFieldComponent } = registry.fields;
|
|
1380
1466
|
const { SubmitButton } = registry.templates.ButtonTemplates;
|
|
1381
1467
|
// The `semantic-ui` and `material-ui` themes have `_internalFormWrapper`s that take an `as` prop that is the
|
|
1382
1468
|
// PropTypes.elementType to use for the inner tag, so we'll need to pass `tagName` along if it is provided.
|
|
@@ -1392,7 +1478,7 @@ export default class Form<
|
|
|
1392
1478
|
|
|
1393
1479
|
return (
|
|
1394
1480
|
<FormTag
|
|
1395
|
-
className={className
|
|
1481
|
+
className={className || 'rjsf'}
|
|
1396
1482
|
id={id}
|
|
1397
1483
|
name={name}
|
|
1398
1484
|
method={method}
|
|
@@ -1407,7 +1493,7 @@ export default class Form<
|
|
|
1407
1493
|
ref={this.formElement}
|
|
1408
1494
|
>
|
|
1409
1495
|
{showErrorList === 'top' && this.renderErrors(registry)}
|
|
1410
|
-
<
|
|
1496
|
+
<SchemaFieldComponent
|
|
1411
1497
|
name=''
|
|
1412
1498
|
schema={schema}
|
|
1413
1499
|
uiSchema={uiSchema}
|
|
@@ -1422,7 +1508,7 @@ export default class Form<
|
|
|
1422
1508
|
readonly={readonly}
|
|
1423
1509
|
/>
|
|
1424
1510
|
|
|
1425
|
-
{children
|
|
1511
|
+
{children || <SubmitButton uiSchema={submitUiSchema} registry={registry} />}
|
|
1426
1512
|
{showErrorList === 'bottom' && this.renderErrors(registry)}
|
|
1427
1513
|
</FormTag>
|
|
1428
1514
|
);
|