@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.
- package/dist/core.umd.js +705 -471
- package/dist/{index.js → index.cjs} +1094 -844
- package/dist/index.cjs.map +7 -0
- package/dist/index.esm.js +1053 -774
- package/dist/index.esm.js.map +4 -4
- package/lib/components/Form.d.ts +88 -23
- package/lib/components/Form.d.ts.map +1 -1
- package/lib/components/Form.js +213 -151
- package/lib/components/fields/ArrayField.d.ts +17 -7
- package/lib/components/fields/ArrayField.d.ts.map +1 -1
- package/lib/components/fields/ArrayField.js +116 -70
- package/lib/components/fields/BooleanField.d.ts.map +1 -1
- package/lib/components/fields/BooleanField.js +7 -2
- package/lib/components/fields/LayoutGridField.d.ts +27 -25
- package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
- package/lib/components/fields/LayoutGridField.js +83 -59
- package/lib/components/fields/LayoutHeaderField.d.ts +1 -1
- package/lib/components/fields/LayoutHeaderField.js +3 -3
- package/lib/components/fields/LayoutMultiSchemaField.js +6 -5
- package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
- package/lib/components/fields/MultiSchemaField.js +13 -9
- package/lib/components/fields/NullField.js +3 -3
- package/lib/components/fields/NumberField.d.ts.map +1 -1
- package/lib/components/fields/NumberField.js +3 -3
- package/lib/components/fields/ObjectField.d.ts +3 -3
- package/lib/components/fields/ObjectField.d.ts.map +1 -1
- package/lib/components/fields/ObjectField.js +34 -34
- package/lib/components/fields/OptionalDataControlsField.d.ts +8 -0
- package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -0
- package/lib/components/fields/OptionalDataControlsField.js +43 -0
- package/lib/components/fields/SchemaField.d.ts.map +1 -1
- package/lib/components/fields/SchemaField.js +17 -17
- package/lib/components/fields/StringField.d.ts.map +1 -1
- package/lib/components/fields/StringField.js +7 -2
- 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 +1 -1
- package/lib/components/templates/ArrayFieldDescriptionTemplate.js +3 -3
- package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +2 -2
- package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTemplate.js +4 -3
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
- package/lib/components/templates/ArrayFieldTitleTemplate.js +3 -3
- package/lib/components/templates/ButtonTemplates/AddButton.d.ts +1 -1
- package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -1
- package/lib/components/templates/ButtonTemplates/AddButton.js +2 -2
- package/lib/components/templates/FieldErrorTemplate.js +2 -2
- package/lib/components/templates/FieldHelpTemplate.js +2 -2
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +8 -0
- package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -0
- package/lib/components/templates/MultiSchemaFieldTemplate.js +10 -0
- package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
- package/lib/components/templates/ObjectFieldTemplate.js +3 -2
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts +11 -0
- package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -0
- package/lib/components/templates/OptionalDataControlsTemplate.js +20 -0
- package/lib/components/templates/TitleField.d.ts.map +1 -1
- package/lib/components/templates/TitleField.js +2 -2
- package/lib/components/templates/UnsupportedField.js +3 -3
- package/lib/components/templates/index.d.ts.map +1 -1
- package/lib/components/templates/index.js +4 -0
- package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
- package/lib/components/widgets/AltDateWidget.js +15 -18
- package/lib/components/widgets/CheckboxesWidget.js +2 -2
- package/lib/getDefaultRegistry.d.ts.map +1 -1
- package/lib/getDefaultRegistry.js +2 -1
- package/lib/getTestRegistry.d.ts +5 -0
- package/lib/getTestRegistry.d.ts.map +1 -0
- package/lib/getTestRegistry.js +19 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -19
- package/src/components/Form.tsx +306 -177
- package/src/components/fields/ArrayField.tsx +127 -80
- package/src/components/fields/BooleanField.tsx +12 -3
- package/src/components/fields/LayoutGridField.tsx +95 -88
- package/src/components/fields/LayoutHeaderField.tsx +3 -3
- package/src/components/fields/LayoutMultiSchemaField.tsx +5 -5
- package/src/components/fields/MultiSchemaField.tsx +51 -35
- package/src/components/fields/NullField.tsx +3 -3
- package/src/components/fields/NumberField.tsx +11 -3
- package/src/components/fields/ObjectField.tsx +47 -53
- package/src/components/fields/OptionalDataControlsField.tsx +84 -0
- package/src/components/fields/SchemaField.tsx +24 -30
- package/src/components/fields/StringField.tsx +12 -3
- package/src/components/fields/index.ts +2 -0
- package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +3 -3
- package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +5 -5
- package/src/components/templates/ArrayFieldTemplate.tsx +9 -5
- package/src/components/templates/ArrayFieldTitleTemplate.tsx +4 -3
- package/src/components/templates/BaseInputTemplate.tsx +3 -3
- package/src/components/templates/ButtonTemplates/AddButton.tsx +2 -0
- package/src/components/templates/FieldErrorTemplate.tsx +2 -2
- package/src/components/templates/FieldHelpTemplate.tsx +2 -2
- package/src/components/templates/MultiSchemaFieldTemplate.tsx +20 -0
- package/src/components/templates/ObjectFieldTemplate.tsx +10 -5
- package/src/components/templates/OptionalDataControlsTemplate.tsx +43 -0
- package/src/components/templates/TitleField.tsx +6 -1
- package/src/components/templates/UnsupportedField.tsx +3 -3
- package/src/components/templates/WrapIfAdditionalTemplate.tsx +1 -1
- package/src/components/templates/index.ts +4 -0
- package/src/components/widgets/AltDateWidget.tsx +21 -23
- package/src/components/widgets/CheckboxWidget.tsx +2 -2
- package/src/components/widgets/CheckboxesWidget.tsx +3 -3
- package/src/components/widgets/RadioWidget.tsx +1 -1
- package/src/components/widgets/SelectWidget.tsx +1 -1
- package/src/components/widgets/TextareaWidget.tsx +1 -1
- package/src/getDefaultRegistry.ts +10 -1
- package/src/getTestRegistry.tsx +34 -0
- package/src/index.ts +2 -1
- package/dist/index.js.map +0 -7
package/src/components/Form.tsx
CHANGED
|
@@ -4,13 +4,15 @@ import {
|
|
|
4
4
|
CustomValidator,
|
|
5
5
|
deepEquals,
|
|
6
6
|
ErrorSchema,
|
|
7
|
+
ErrorSchemaBuilder,
|
|
7
8
|
ErrorTransformer,
|
|
9
|
+
FieldPathId,
|
|
10
|
+
FieldPathList,
|
|
8
11
|
FormContextType,
|
|
9
12
|
GenericObjectType,
|
|
10
13
|
getChangedFields,
|
|
11
14
|
getTemplate,
|
|
12
15
|
getUiOptions,
|
|
13
|
-
IdSchema,
|
|
14
16
|
isObject,
|
|
15
17
|
mergeObjects,
|
|
16
18
|
NAME_KEY,
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
SUBMIT_BTN_OPTIONS_KEY,
|
|
28
30
|
TemplatesType,
|
|
29
31
|
toErrorList,
|
|
32
|
+
toFieldPathId,
|
|
30
33
|
UiSchema,
|
|
31
34
|
UI_GLOBAL_OPTIONS_KEY,
|
|
32
35
|
UI_OPTIONS_KEY,
|
|
@@ -35,14 +38,16 @@ import {
|
|
|
35
38
|
ValidatorType,
|
|
36
39
|
Experimental_DefaultFormStateBehavior,
|
|
37
40
|
Experimental_CustomMergeAllOf,
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
DEFAULT_ID_SEPARATOR,
|
|
42
|
+
DEFAULT_ID_PREFIX,
|
|
43
|
+
GlobalFormOptions,
|
|
44
|
+
ERRORS_KEY,
|
|
40
45
|
} from '@rjsf/utils';
|
|
41
|
-
import
|
|
46
|
+
import _cloneDeep from 'lodash/cloneDeep';
|
|
42
47
|
import _get from 'lodash/get';
|
|
43
48
|
import _isEmpty from 'lodash/isEmpty';
|
|
44
|
-
import _isNil from 'lodash/isNil';
|
|
45
49
|
import _pick from 'lodash/pick';
|
|
50
|
+
import _set from 'lodash/set';
|
|
46
51
|
import _toPath from 'lodash/toPath';
|
|
47
52
|
|
|
48
53
|
import getDefaultRegistry from '../getDefaultRegistry';
|
|
@@ -195,6 +200,17 @@ export interface FormProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
195
200
|
* `emptyObjectFields`
|
|
196
201
|
*/
|
|
197
202
|
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior;
|
|
203
|
+
/**
|
|
204
|
+
* Controls the component update strategy used by the Form's `shouldComponentUpdate` lifecycle method.
|
|
205
|
+
*
|
|
206
|
+
* - `'customDeep'`: Uses RJSF's custom deep equality checks via the `deepEquals` utility function,
|
|
207
|
+
* which treats all functions as equivalent and provides optimized performance for form data comparisons.
|
|
208
|
+
* - `'shallow'`: Uses shallow comparison of props and state (only compares direct properties). This matches React's PureComponent behavior.
|
|
209
|
+
* - `'always'`: Always rerenders when called. This matches React's Component behavior.
|
|
210
|
+
*
|
|
211
|
+
* @default 'customDeep'
|
|
212
|
+
*/
|
|
213
|
+
experimental_componentUpdateStrategy?: 'customDeep' | 'shallow' | 'always';
|
|
198
214
|
/** Optional function that allows for custom merging of `allOf` schemas
|
|
199
215
|
*/
|
|
200
216
|
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>;
|
|
@@ -226,10 +242,10 @@ export interface FormState<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
226
242
|
schema: S;
|
|
227
243
|
/** The uiSchema for the form */
|
|
228
244
|
uiSchema: UiSchema<T, S, F>;
|
|
229
|
-
/** The `
|
|
245
|
+
/** The `FieldPathId` for the form, computed from the `schema`, the `rootFieldId`, the `idPrefix` and
|
|
230
246
|
* `idSeparator` props.
|
|
231
247
|
*/
|
|
232
|
-
|
|
248
|
+
fieldPathId: FieldPathId;
|
|
233
249
|
/** The schemaUtils implementation used by the `Form`, created from the `validator` and the `schema` */
|
|
234
250
|
schemaUtils: SchemaUtilsType<T, S, F>;
|
|
235
251
|
/** The current data for the form, computed from the `formData` prop and the changes made by the user */
|
|
@@ -240,26 +256,50 @@ export interface FormState<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
240
256
|
errors: RJSFValidationError[];
|
|
241
257
|
/** The current errors, in `ErrorSchema` format, for the form, includes `extraErrors` */
|
|
242
258
|
errorSchema: ErrorSchema<T>;
|
|
259
|
+
// Private
|
|
243
260
|
/** The current list of errors for the form directly from schema validation, does NOT include `extraErrors` */
|
|
244
261
|
schemaValidationErrors: RJSFValidationError[];
|
|
245
262
|
/** The current errors, in `ErrorSchema` format, for the form directly from schema validation, does NOT include
|
|
246
263
|
* `extraErrors`
|
|
247
264
|
*/
|
|
248
265
|
schemaValidationErrorSchema: ErrorSchema<T>;
|
|
249
|
-
|
|
266
|
+
/** A container used to handle custom errors provided via `onChange` */
|
|
267
|
+
customErrors?: ErrorSchemaBuilder<T>;
|
|
250
268
|
/** @description result of schemaUtils.retrieveSchema(schema, formData). This a memoized value to avoid re calculate at internal functions (getStateFromProps, onChange) */
|
|
251
269
|
retrievedSchema: S;
|
|
270
|
+
/** Flag indicating whether the initial form defaults have been generated */
|
|
271
|
+
initialDefaultsGenerated: boolean;
|
|
252
272
|
}
|
|
253
273
|
|
|
254
274
|
/** The event data passed when changes have been made to the form, includes everything from the `FormState` except
|
|
255
275
|
* the schema validation errors. An additional `status` is added when returned from `onSubmit`
|
|
256
276
|
*/
|
|
257
277
|
export interface IChangeEvent<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>
|
|
258
|
-
extends Omit<
|
|
278
|
+
extends Omit<
|
|
279
|
+
FormState<T, S, F>,
|
|
280
|
+
| 'schemaValidationErrors'
|
|
281
|
+
| 'schemaValidationErrorSchema'
|
|
282
|
+
| 'retrievedSchema'
|
|
283
|
+
| 'customErrors'
|
|
284
|
+
| 'initialDefaultsGenerated'
|
|
285
|
+
> {
|
|
259
286
|
/** The status of the form when submitted */
|
|
260
287
|
status?: 'submitted';
|
|
261
288
|
}
|
|
262
289
|
|
|
290
|
+
/** The definition of a pending change that will be processed in the `onChange` handler
|
|
291
|
+
*/
|
|
292
|
+
interface PendingChange<T> {
|
|
293
|
+
/** The path into the formData/errorSchema at which the `newValue`/`newErrorSchema` will be set */
|
|
294
|
+
path: FieldPathList;
|
|
295
|
+
/** The new value to set into the formData */
|
|
296
|
+
newValue?: T;
|
|
297
|
+
/** The new errors to be set into the errorSchema, if any */
|
|
298
|
+
newErrorSchema?: ErrorSchema<T>;
|
|
299
|
+
/** The optional id of the field for which the change is being made */
|
|
300
|
+
id?: string;
|
|
301
|
+
}
|
|
302
|
+
|
|
263
303
|
/** The `Form` component renders the outer form and all the fields defined in the `schema` */
|
|
264
304
|
export default class Form<
|
|
265
305
|
T = any,
|
|
@@ -271,6 +311,10 @@ export default class Form<
|
|
|
271
311
|
*/
|
|
272
312
|
formElement: RefObject<any>;
|
|
273
313
|
|
|
314
|
+
/** The list of pending changes
|
|
315
|
+
*/
|
|
316
|
+
pendingChanges: PendingChange<T>[] = [];
|
|
317
|
+
|
|
274
318
|
/** Constructs the `Form` from the `props`. Will setup the initial state from the props. It will also call the
|
|
275
319
|
* `onChange` handler if the initially provided `formData` is modified to add missing default values as part of the
|
|
276
320
|
* state construction.
|
|
@@ -314,12 +358,18 @@ export default class Form<
|
|
|
314
358
|
prevState: FormState<T, S, F>,
|
|
315
359
|
): { nextState: FormState<T, S, F>; shouldUpdate: true } | { shouldUpdate: false } {
|
|
316
360
|
if (!deepEquals(this.props, prevProps)) {
|
|
361
|
+
// Compare the previous props formData against the current props formData
|
|
317
362
|
const formDataChangedFields = getChangedFields(this.props.formData, prevProps.formData);
|
|
363
|
+
// Compare the current props formData against the current state's formData to determine if the new props were the
|
|
364
|
+
// result of the onChange from the existing state formData
|
|
365
|
+
const stateDataChangedFields = getChangedFields(this.props.formData, this.state.formData);
|
|
318
366
|
const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema);
|
|
319
367
|
// When formData is not an object, getChangedFields returns an empty array.
|
|
320
368
|
// In this case, deepEquals is most needed to check again.
|
|
321
369
|
const isFormDataChanged =
|
|
322
370
|
formDataChangedFields.length > 0 || !deepEquals(prevProps.formData, this.props.formData);
|
|
371
|
+
const isStateDataChanged =
|
|
372
|
+
stateDataChangedFields.length > 0 || !deepEquals(this.state.formData, this.props.formData);
|
|
323
373
|
const nextState = this.getStateFromProps(
|
|
324
374
|
this.props,
|
|
325
375
|
this.props.formData,
|
|
@@ -329,6 +379,8 @@ export default class Form<
|
|
|
329
379
|
isSchemaChanged || isFormDataChanged ? undefined : this.state.retrievedSchema,
|
|
330
380
|
isSchemaChanged,
|
|
331
381
|
formDataChangedFields,
|
|
382
|
+
// Skip live validation for this request if no form data has changed from the last state
|
|
383
|
+
!isStateDataChanged,
|
|
332
384
|
);
|
|
333
385
|
const shouldUpdate = !deepEquals(nextState, prevState);
|
|
334
386
|
return { nextState, shouldUpdate };
|
|
@@ -376,6 +428,7 @@ export default class Form<
|
|
|
376
428
|
* @param retrievedSchema - An expanded schema, if not provided, it will be retrieved from the `schema` and `formData`.
|
|
377
429
|
* @param isSchemaChanged - A flag indicating whether the schema has changed.
|
|
378
430
|
* @param formDataChangedFields - The changed fields of `formData`
|
|
431
|
+
* @param skipLiveValidate - Optional flag, if true, means that we are not running live validation
|
|
379
432
|
* @returns - The new state for the `Form`
|
|
380
433
|
*/
|
|
381
434
|
getStateFromProps(
|
|
@@ -384,14 +437,15 @@ export default class Form<
|
|
|
384
437
|
retrievedSchema?: S,
|
|
385
438
|
isSchemaChanged = false,
|
|
386
439
|
formDataChangedFields: string[] = [],
|
|
440
|
+
skipLiveValidate = false,
|
|
387
441
|
): FormState<T, S, F> {
|
|
388
442
|
const state: FormState<T, S, F> = this.state || {};
|
|
389
443
|
const schema = 'schema' in props ? props.schema : this.props.schema;
|
|
444
|
+
const validator = 'validator' in props ? props.validator : this.props.validator;
|
|
390
445
|
const uiSchema: UiSchema<T, S, F> = ('uiSchema' in props ? props.uiSchema! : this.props.uiSchema!) || {};
|
|
391
446
|
const edit = typeof inputFormData !== 'undefined';
|
|
392
447
|
const liveValidate = 'liveValidate' in props ? props.liveValidate : this.props.liveValidate;
|
|
393
448
|
const mustValidate = edit && !props.noValidate && liveValidate;
|
|
394
|
-
const rootSchema = schema;
|
|
395
449
|
const experimental_defaultFormStateBehavior =
|
|
396
450
|
'experimental_defaultFormStateBehavior' in props
|
|
397
451
|
? props.experimental_defaultFormStateBehavior
|
|
@@ -404,22 +458,29 @@ export default class Form<
|
|
|
404
458
|
if (
|
|
405
459
|
!schemaUtils ||
|
|
406
460
|
schemaUtils.doesSchemaUtilsDiffer(
|
|
407
|
-
|
|
408
|
-
|
|
461
|
+
validator,
|
|
462
|
+
schema,
|
|
409
463
|
experimental_defaultFormStateBehavior,
|
|
410
464
|
experimental_customMergeAllOf,
|
|
411
465
|
)
|
|
412
466
|
) {
|
|
413
467
|
schemaUtils = createSchemaUtils<T, S, F>(
|
|
414
|
-
|
|
415
|
-
|
|
468
|
+
validator,
|
|
469
|
+
schema,
|
|
416
470
|
experimental_defaultFormStateBehavior,
|
|
417
471
|
experimental_customMergeAllOf,
|
|
418
472
|
);
|
|
419
473
|
}
|
|
420
|
-
|
|
474
|
+
|
|
475
|
+
const rootSchema = schemaUtils.getRootSchema();
|
|
476
|
+
const formData: T = schemaUtils.getDefaultFormState(
|
|
477
|
+
rootSchema,
|
|
478
|
+
inputFormData,
|
|
479
|
+
false,
|
|
480
|
+
state.initialDefaultsGenerated,
|
|
481
|
+
) as T;
|
|
421
482
|
const _retrievedSchema = this.updateRetrievedSchema(
|
|
422
|
-
retrievedSchema ?? schemaUtils.retrieveSchema(
|
|
483
|
+
retrievedSchema ?? schemaUtils.retrieveSchema(rootSchema, formData),
|
|
423
484
|
);
|
|
424
485
|
|
|
425
486
|
const getCurrentErrors = (): ValidationData<T> => {
|
|
@@ -442,27 +503,30 @@ export default class Form<
|
|
|
442
503
|
let errorSchema: ErrorSchema<T> | undefined;
|
|
443
504
|
let schemaValidationErrors: RJSFValidationError[] = state.schemaValidationErrors;
|
|
444
505
|
let schemaValidationErrorSchema: ErrorSchema<T> = state.schemaValidationErrorSchema;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
506
|
+
// If we are skipping live validate, it means that the state has already been updated with live validation errors
|
|
507
|
+
if (mustValidate && !skipLiveValidate) {
|
|
508
|
+
const liveValidation = this.liveValidate(
|
|
509
|
+
rootSchema,
|
|
510
|
+
schemaUtils,
|
|
511
|
+
state.errorSchema,
|
|
512
|
+
formData,
|
|
513
|
+
undefined,
|
|
514
|
+
state.customErrors,
|
|
515
|
+
retrievedSchema,
|
|
516
|
+
// If retrievedSchema is undefined which means the schema or formData has changed, we do not merge state.
|
|
517
|
+
// Else in the case where it hasn't changed,
|
|
518
|
+
retrievedSchema !== undefined,
|
|
519
|
+
);
|
|
520
|
+
errors = liveValidation.errors;
|
|
521
|
+
errorSchema = liveValidation.errorSchema;
|
|
522
|
+
schemaValidationErrors = liveValidation.schemaValidationErrors;
|
|
523
|
+
schemaValidationErrorSchema = liveValidation.schemaValidationErrorSchema;
|
|
461
524
|
} else {
|
|
462
525
|
const currentErrors = getCurrentErrors();
|
|
463
526
|
errors = currentErrors.errors;
|
|
464
527
|
errorSchema = currentErrors.errorSchema;
|
|
465
|
-
if
|
|
528
|
+
// We only update the error schema for changed fields if mustValidate is false
|
|
529
|
+
if (formDataChangedFields.length > 0 && !mustValidate) {
|
|
466
530
|
const newErrorSchema = formDataChangedFields.reduce(
|
|
467
531
|
(acc, key) => {
|
|
468
532
|
acc[key] = undefined;
|
|
@@ -476,25 +540,17 @@ export default class Form<
|
|
|
476
540
|
'preventDuplicates',
|
|
477
541
|
) as ErrorSchema<T>;
|
|
478
542
|
}
|
|
543
|
+
const mergedErrors = this.mergeErrors({ errorSchema, errors }, props.extraErrors, state.customErrors);
|
|
544
|
+
errors = mergedErrors.errors;
|
|
545
|
+
errorSchema = mergedErrors.errorSchema;
|
|
479
546
|
}
|
|
480
547
|
|
|
481
|
-
|
|
482
|
-
const merged = validationDataMerge({ errorSchema, errors }, props.extraErrors);
|
|
483
|
-
errorSchema = merged.errorSchema;
|
|
484
|
-
errors = merged.errors;
|
|
485
|
-
}
|
|
486
|
-
const idSchema = schemaUtils.toIdSchema(
|
|
487
|
-
_retrievedSchema,
|
|
488
|
-
uiSchema['ui:rootFieldId'],
|
|
489
|
-
formData,
|
|
490
|
-
props.idPrefix,
|
|
491
|
-
props.idSeparator,
|
|
492
|
-
);
|
|
548
|
+
const fieldPathId = toFieldPathId('', this.getGlobalFormOptions(this.props));
|
|
493
549
|
const nextState: FormState<T, S, F> = {
|
|
494
550
|
schemaUtils,
|
|
495
|
-
schema,
|
|
551
|
+
schema: rootSchema,
|
|
496
552
|
uiSchema,
|
|
497
|
-
|
|
553
|
+
fieldPathId,
|
|
498
554
|
formData,
|
|
499
555
|
edit,
|
|
500
556
|
errors,
|
|
@@ -502,6 +558,7 @@ export default class Form<
|
|
|
502
558
|
schemaValidationErrors,
|
|
503
559
|
schemaValidationErrorSchema,
|
|
504
560
|
retrievedSchema: _retrievedSchema,
|
|
561
|
+
initialDefaultsGenerated: true,
|
|
505
562
|
};
|
|
506
563
|
return nextState;
|
|
507
564
|
}
|
|
@@ -513,23 +570,8 @@ export default class Form<
|
|
|
513
570
|
* @returns - True if the component should be updated, false otherwise
|
|
514
571
|
*/
|
|
515
572
|
shouldComponentUpdate(nextProps: FormProps<T, S, F>, nextState: FormState<T, S, F>): boolean {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
/** Gets the previously raised customValidate errors.
|
|
520
|
-
*
|
|
521
|
-
* @returns the previous customValidate errors
|
|
522
|
-
*/
|
|
523
|
-
private getPreviousCustomValidateErrors(): ErrorSchema<T> {
|
|
524
|
-
const { customValidate, uiSchema } = this.props;
|
|
525
|
-
const prevFormData = this.state.formData as T;
|
|
526
|
-
let customValidateErrors = {};
|
|
527
|
-
if (typeof customValidate === 'function') {
|
|
528
|
-
const errorHandler = customValidate(prevFormData, createErrorHandler<T>(prevFormData), uiSchema);
|
|
529
|
-
const userErrorSchema = unwrapErrorHandler<T>(errorHandler);
|
|
530
|
-
customValidateErrors = userErrorSchema;
|
|
531
|
-
}
|
|
532
|
-
return customValidateErrors;
|
|
573
|
+
const { experimental_componentUpdateStrategy = 'customDeep' } = this.props;
|
|
574
|
+
return shouldRender(this, nextProps, nextState, experimental_componentUpdateStrategy);
|
|
533
575
|
}
|
|
534
576
|
|
|
535
577
|
/** Validates the `formData` against the `schema` using the `altSchemaUtils` (if provided otherwise it uses the
|
|
@@ -537,11 +579,12 @@ export default class Form<
|
|
|
537
579
|
*
|
|
538
580
|
* @param formData - The new form data to validate
|
|
539
581
|
* @param schema - The schema used to validate against
|
|
540
|
-
* @param altSchemaUtils - The alternate schemaUtils to use for validation
|
|
582
|
+
* @param [altSchemaUtils] - The alternate schemaUtils to use for validation
|
|
583
|
+
* @param [retrievedSchema] - An optionally retrieved schema for per
|
|
541
584
|
*/
|
|
542
585
|
validate(
|
|
543
586
|
formData: T | undefined,
|
|
544
|
-
schema = this.
|
|
587
|
+
schema = this.state.schema,
|
|
545
588
|
altSchemaUtils?: SchemaUtilsType<T, S, F>,
|
|
546
589
|
retrievedSchema?: S,
|
|
547
590
|
): ValidationData<T> {
|
|
@@ -556,7 +599,6 @@ export default class Form<
|
|
|
556
599
|
/** Renders any errors contained in the `state` in using the `ErrorList`, if not disabled by `showErrorList`. */
|
|
557
600
|
renderErrors(registry: Registry<T, S, F>) {
|
|
558
601
|
const { errors, errorSchema, schema, uiSchema } = this.state;
|
|
559
|
-
const { formContext } = this.props;
|
|
560
602
|
const options = getUiOptions<T, S, F>(uiSchema);
|
|
561
603
|
const ErrorListTemplate = getTemplate<'ErrorListTemplate', T, S, F>('ErrorListTemplate', registry, options);
|
|
562
604
|
|
|
@@ -567,7 +609,6 @@ export default class Form<
|
|
|
567
609
|
errorSchema={errorSchema || {}}
|
|
568
610
|
schema={schema}
|
|
569
611
|
uiSchema={uiSchema}
|
|
570
|
-
formContext={formContext}
|
|
571
612
|
registry={registry}
|
|
572
613
|
/>
|
|
573
614
|
);
|
|
@@ -575,6 +616,75 @@ export default class Form<
|
|
|
575
616
|
return null;
|
|
576
617
|
}
|
|
577
618
|
|
|
619
|
+
/** Merges any `extraErrors` or `customErrors` into the given `schemaValidation` object, returning the result
|
|
620
|
+
*
|
|
621
|
+
* @param schemaValidation - The `ValidationData` object into which additional errors are merged
|
|
622
|
+
* @param [extraErrors] - The extra errors from the props
|
|
623
|
+
* @param [customErrors] - The customErrors from custom components
|
|
624
|
+
* @return - The `extraErrors` and `customErrors` merged into the `schemaValidation`
|
|
625
|
+
* @private
|
|
626
|
+
*/
|
|
627
|
+
private mergeErrors(
|
|
628
|
+
schemaValidation: ValidationData<T>,
|
|
629
|
+
extraErrors?: FormProps['extraErrors'],
|
|
630
|
+
customErrors?: ErrorSchemaBuilder,
|
|
631
|
+
): ValidationData<T> {
|
|
632
|
+
let errorSchema: ErrorSchema<T> = schemaValidation.errorSchema;
|
|
633
|
+
let errors: RJSFValidationError[] = schemaValidation.errors;
|
|
634
|
+
if (extraErrors) {
|
|
635
|
+
const merged = validationDataMerge(schemaValidation, extraErrors);
|
|
636
|
+
errorSchema = merged.errorSchema;
|
|
637
|
+
errors = merged.errors;
|
|
638
|
+
}
|
|
639
|
+
if (customErrors) {
|
|
640
|
+
const merged = validationDataMerge(schemaValidation, customErrors.ErrorSchema, true);
|
|
641
|
+
errorSchema = merged.errorSchema;
|
|
642
|
+
errors = merged.errors;
|
|
643
|
+
}
|
|
644
|
+
return { errors, errorSchema };
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** Performs live validation and then updates and returns the errors and error schemas by potentially merging in
|
|
648
|
+
* `extraErrors` and `customErrors`.
|
|
649
|
+
*
|
|
650
|
+
* @param rootSchema - The `rootSchema` from the state
|
|
651
|
+
* @param schemaUtils - The `SchemaUtilsType` from the state
|
|
652
|
+
* @param originalErrorSchema - The original `ErrorSchema` from the state
|
|
653
|
+
* @param [formData] - The new form data to validate
|
|
654
|
+
* @param [extraErrors] - The extra errors from the props
|
|
655
|
+
* @param [customErrors] - The customErrors from custom components
|
|
656
|
+
* @param [retrievedSchema] - An expanded schema, if not provided, it will be retrieved from the `schema` and `formData`
|
|
657
|
+
* @param [mergeIntoOriginalErrorSchema=false] - Optional flag indicating whether we merge into original schema
|
|
658
|
+
* @returns - An object containing `errorSchema`, `errors`, `schemaValidationErrors` and `schemaValidationErrorSchema`
|
|
659
|
+
* @private
|
|
660
|
+
*/
|
|
661
|
+
private liveValidate(
|
|
662
|
+
rootSchema: S,
|
|
663
|
+
schemaUtils: SchemaUtilsType<T, S, F>,
|
|
664
|
+
originalErrorSchema: ErrorSchema<S>,
|
|
665
|
+
formData?: T,
|
|
666
|
+
extraErrors?: FormProps['extraErrors'],
|
|
667
|
+
customErrors?: ErrorSchemaBuilder<T>,
|
|
668
|
+
retrievedSchema?: S,
|
|
669
|
+
mergeIntoOriginalErrorSchema = false,
|
|
670
|
+
) {
|
|
671
|
+
const schemaValidation = this.validate(formData, rootSchema, schemaUtils, retrievedSchema);
|
|
672
|
+
const errors = schemaValidation.errors;
|
|
673
|
+
let errorSchema = schemaValidation.errorSchema;
|
|
674
|
+
// We merge 'originalErrorSchema' with 'schemaValidation.errorSchema.'; This done to display the raised field error.
|
|
675
|
+
if (mergeIntoOriginalErrorSchema) {
|
|
676
|
+
errorSchema = mergeObjects(
|
|
677
|
+
originalErrorSchema,
|
|
678
|
+
schemaValidation.errorSchema,
|
|
679
|
+
'preventDuplicates',
|
|
680
|
+
) as ErrorSchema<T>;
|
|
681
|
+
}
|
|
682
|
+
const schemaValidationErrors = errors;
|
|
683
|
+
const schemaValidationErrorSchema = errorSchema;
|
|
684
|
+
const mergedErrors = this.mergeErrors({ errorSchema, errors }, extraErrors, customErrors);
|
|
685
|
+
return { ...mergedErrors, schemaValidationErrors, schemaValidationErrorSchema };
|
|
686
|
+
}
|
|
687
|
+
|
|
578
688
|
/** Returns the `formData` with only the elements specified in the `fields` list
|
|
579
689
|
*
|
|
580
690
|
* @param formData - The data for the `Form`
|
|
@@ -601,25 +711,28 @@ export default class Form<
|
|
|
601
711
|
* @param [formData] - The form data to use while checking for empty objects/arrays
|
|
602
712
|
*/
|
|
603
713
|
getFieldNames = (pathSchema: PathSchema<T>, formData?: T): string[][] => {
|
|
714
|
+
const formValueHasData = (value: T, isLeaf: boolean) =>
|
|
715
|
+
typeof value !== 'object' || _isEmpty(value) || (isLeaf && !_isEmpty(value));
|
|
604
716
|
const getAllPaths = (_obj: GenericObjectType, acc: string[][] = [], paths: string[][] = [[]]) => {
|
|
605
|
-
Object.keys(_obj)
|
|
606
|
-
|
|
717
|
+
const objKeys = Object.keys(_obj);
|
|
718
|
+
objKeys.forEach((key: string) => {
|
|
719
|
+
const data = _obj[key];
|
|
720
|
+
if (typeof data === 'object') {
|
|
607
721
|
const newPaths = paths.map((path) => [...path, key]);
|
|
608
722
|
// If an object is marked with additionalProperties, all its keys are valid
|
|
609
|
-
if (
|
|
610
|
-
acc.push(
|
|
723
|
+
if (data[RJSF_ADDITIONAL_PROPERTIES_FLAG] && data[NAME_KEY] !== '') {
|
|
724
|
+
acc.push(data[NAME_KEY]);
|
|
611
725
|
} else {
|
|
612
|
-
getAllPaths(
|
|
726
|
+
getAllPaths(data, acc, newPaths);
|
|
613
727
|
}
|
|
614
|
-
} else if (key === NAME_KEY &&
|
|
728
|
+
} else if (key === NAME_KEY && data !== '') {
|
|
615
729
|
paths.forEach((path) => {
|
|
616
730
|
const formValue = _get(formData, path);
|
|
617
|
-
|
|
618
|
-
// or an empty object/array
|
|
731
|
+
const isLeaf = objKeys.length === 1;
|
|
732
|
+
// adds path to fieldNames if it points to a value or an empty object/array which is not a leaf
|
|
619
733
|
if (
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
(Array.isArray(formValue) && formValue.every((val) => typeof val !== 'object'))
|
|
734
|
+
formValueHasData(formValue, isLeaf) ||
|
|
735
|
+
(Array.isArray(formValue) && formValue.every((val) => formValueHasData(val, isLeaf)))
|
|
623
736
|
) {
|
|
624
737
|
acc.push(path);
|
|
625
738
|
}
|
|
@@ -642,74 +755,54 @@ export default class Form<
|
|
|
642
755
|
const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);
|
|
643
756
|
const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData);
|
|
644
757
|
const fieldNames = this.getFieldNames(pathSchema, formData);
|
|
645
|
-
|
|
646
|
-
return newFormData;
|
|
758
|
+
return this.getUsedFormData(formData, fieldNames);
|
|
647
759
|
};
|
|
648
760
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
761
|
+
/** Pushes the given change information into the `pendingChanges` array and then calls `processPendingChanges()` if
|
|
762
|
+
* the array only contains a single pending change.
|
|
763
|
+
*
|
|
764
|
+
* @param newValue - The new form data from a change to a field
|
|
765
|
+
* @param path - The path to the change into which to set the formData
|
|
766
|
+
* @param [newErrorSchema] - The new `ErrorSchema` based on the field change
|
|
767
|
+
* @param [id] - The id of the field that caused the change
|
|
768
|
+
*/
|
|
769
|
+
onChange = (newValue: T | undefined, path: FieldPathList, newErrorSchema?: ErrorSchema<T>, id?: string) => {
|
|
770
|
+
this.pendingChanges.push({ newValue, path, newErrorSchema, id });
|
|
771
|
+
if (this.pendingChanges.length === 1) {
|
|
772
|
+
this.processPendingChange();
|
|
659
773
|
}
|
|
774
|
+
};
|
|
660
775
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
return !prevCustomErrors.includes(error);
|
|
670
|
-
});
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
// Removing undefined, null and empty errors.
|
|
674
|
-
const filterNilOrEmptyErrors = (errors: any, previousCustomValidateErrors: any = {}): ErrorSchema<T> => {
|
|
675
|
-
_forEach(errors, (errorAtKey: ErrorSchema<T>['__errors'] | undefined, errorKey: keyof typeof errors) => {
|
|
676
|
-
const prevCustomValidateErrorAtKey: ErrorSchema<T> | undefined = previousCustomValidateErrors[errorKey];
|
|
677
|
-
if (_isNil(errorAtKey) || (Array.isArray(errorAtKey) && errorAtKey.length === 0)) {
|
|
678
|
-
delete errors[errorKey];
|
|
679
|
-
} else if (
|
|
680
|
-
isObject(errorAtKey) &&
|
|
681
|
-
isObject(prevCustomValidateErrorAtKey) &&
|
|
682
|
-
Array.isArray(prevCustomValidateErrorAtKey?.__errors)
|
|
683
|
-
) {
|
|
684
|
-
// if previous customValidate error is an object and has __errors array, filter out the errors previous customValidate errors.
|
|
685
|
-
errors[errorKey] = filterPreviousCustomErrors(errorAtKey.__errors, prevCustomValidateErrorAtKey.__errors);
|
|
686
|
-
} else if (typeof errorAtKey === 'object' && !Array.isArray(errorAtKey.__errors)) {
|
|
687
|
-
filterNilOrEmptyErrors(errorAtKey, previousCustomValidateErrors[errorKey]);
|
|
688
|
-
}
|
|
689
|
-
});
|
|
690
|
-
return errors;
|
|
691
|
-
};
|
|
692
|
-
return filterNilOrEmptyErrors(filteredErrors, prevCustomValidateErrors);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the
|
|
696
|
-
* `formData` along with a new `ErrorSchema`. It will first update the `formData` with any missing default fields and
|
|
697
|
-
* then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not
|
|
698
|
-
* in a form field. Then, the resulting formData will be validated if required. The state will be updated with the new
|
|
699
|
-
* updated (potentially filtered) `formData`, any errors that resulted from validation. Finally the `onChange`
|
|
700
|
-
* callback will be called if specified with the updated state.
|
|
701
|
-
*
|
|
702
|
-
* @param formData - The new form data from a change to a field
|
|
703
|
-
* @param newErrorSchema - The new `ErrorSchema` based on the field change
|
|
704
|
-
* @param id - The id of the field that caused the change
|
|
776
|
+
/** Function to handle changes made to a field in the `Form`. This handler gets the first change from the
|
|
777
|
+
* `pendingChanges` list, containing the `newValue` for the `formData` and the `path` at which the `newValue` is to be
|
|
778
|
+
* updated, along with a new, optional `ErrorSchema` for that same `path` and potentially the `id` of the field being
|
|
779
|
+
* changed. It will first update the `formData` with any missing default fields and then, if `omitExtraData` and
|
|
780
|
+
* `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not in a form field. Then, the
|
|
781
|
+
* resulting `formData` will be validated if required. The state will be updated with the new updated (potentially
|
|
782
|
+
* filtered) `formData`, any errors that resulted from validation. Finally the `onChange` callback will be called, if
|
|
783
|
+
* specified, with the updated state and the `processPendingChange()` function is called again.
|
|
705
784
|
*/
|
|
706
|
-
|
|
785
|
+
processPendingChange() {
|
|
786
|
+
if (this.pendingChanges.length === 0) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const { newValue, path, id } = this.pendingChanges[0];
|
|
790
|
+
const { newErrorSchema } = this.pendingChanges[0];
|
|
707
791
|
const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } = this.props;
|
|
708
|
-
const { schemaUtils, schema } = this.state;
|
|
792
|
+
const { formData: oldFormData, schemaUtils, schema, fieldPathId, schemaValidationErrorSchema, errors } = this.state;
|
|
793
|
+
let { customErrors, errorSchema: originalErrorSchema } = this.state;
|
|
794
|
+
const rootPathId = fieldPathId.path[0] || '';
|
|
709
795
|
|
|
796
|
+
const isRootPath = !path || path.length === 0 || (path.length === 1 && path[0] === rootPathId);
|
|
710
797
|
let retrievedSchema = this.state.retrievedSchema;
|
|
798
|
+
let formData = isRootPath ? newValue : _cloneDeep(oldFormData);
|
|
711
799
|
if (isObject(formData) || Array.isArray(formData)) {
|
|
712
|
-
|
|
800
|
+
if (!isRootPath) {
|
|
801
|
+
// If the newValue is not on the root path, then set it into the form data
|
|
802
|
+
_set(formData, path, newValue);
|
|
803
|
+
}
|
|
804
|
+
// Pass true to skip live validation in `getStateFromProps()` since we will do it a bit later
|
|
805
|
+
const newState = this.getStateFromProps(this.props, formData, undefined, undefined, undefined, true);
|
|
713
806
|
formData = newState.formData;
|
|
714
807
|
retrievedSchema = newState.retrievedSchema;
|
|
715
808
|
}
|
|
@@ -725,41 +818,62 @@ export default class Form<
|
|
|
725
818
|
};
|
|
726
819
|
}
|
|
727
820
|
|
|
728
|
-
if (
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
821
|
+
if (newErrorSchema) {
|
|
822
|
+
// First check to see if there is an existing validation error on this path...
|
|
823
|
+
// @ts-expect-error TS2590, because getting from the error schema is confusing TS
|
|
824
|
+
const oldValidationError = !isRootPath ? _get(schemaValidationErrorSchema, path) : schemaValidationErrorSchema;
|
|
825
|
+
// If there is an old validation error for this path, assume we are updating it directly
|
|
826
|
+
if (!_isEmpty(oldValidationError)) {
|
|
827
|
+
// Update the originalErrorSchema "in place" or replace it if it is the root
|
|
828
|
+
if (!isRootPath) {
|
|
829
|
+
_set(originalErrorSchema, path, newErrorSchema);
|
|
830
|
+
} else {
|
|
831
|
+
originalErrorSchema = newErrorSchema;
|
|
832
|
+
}
|
|
833
|
+
} else {
|
|
834
|
+
if (!customErrors) {
|
|
835
|
+
customErrors = new ErrorSchemaBuilder<T>();
|
|
836
|
+
}
|
|
837
|
+
if (isRootPath) {
|
|
838
|
+
customErrors.setErrors(_get(newErrorSchema, ERRORS_KEY, ''));
|
|
839
|
+
} else {
|
|
840
|
+
_set(customErrors.ErrorSchema, path, newErrorSchema);
|
|
841
|
+
}
|
|
743
842
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
843
|
+
} else if (customErrors && _get(customErrors.ErrorSchema, [...path, ERRORS_KEY])) {
|
|
844
|
+
// If we have custom errors and the path has an error, then we need to clear it
|
|
845
|
+
customErrors.clearErrors(path);
|
|
846
|
+
}
|
|
847
|
+
// If there are pending changes in the queue, skip live validation since it will happen with the last change
|
|
848
|
+
if (mustValidate && this.pendingChanges.length === 1) {
|
|
849
|
+
const liveValidation = this.liveValidate(
|
|
850
|
+
schema,
|
|
851
|
+
schemaUtils,
|
|
852
|
+
originalErrorSchema,
|
|
853
|
+
newFormData,
|
|
854
|
+
extraErrors,
|
|
855
|
+
customErrors,
|
|
856
|
+
retrievedSchema,
|
|
857
|
+
);
|
|
858
|
+
state = { formData: newFormData, ...liveValidation, customErrors };
|
|
751
859
|
} else if (!noValidate && newErrorSchema) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
: newErrorSchema;
|
|
860
|
+
// Merging 'newErrorSchema' into 'errorSchema' to display the custom raised errors.
|
|
861
|
+
const mergedErrors = this.mergeErrors({ errorSchema: originalErrorSchema, errors }, extraErrors, customErrors);
|
|
755
862
|
state = {
|
|
756
863
|
formData: newFormData,
|
|
757
|
-
|
|
758
|
-
|
|
864
|
+
...mergedErrors,
|
|
865
|
+
customErrors,
|
|
759
866
|
};
|
|
760
867
|
}
|
|
761
|
-
this.setState(state as FormState<T, S, F>, () =>
|
|
762
|
-
|
|
868
|
+
this.setState(state as FormState<T, S, F>, () => {
|
|
869
|
+
if (onChange) {
|
|
870
|
+
onChange({ ...this.state, ...state }, id);
|
|
871
|
+
}
|
|
872
|
+
// Now remove the change we just completed and call this again
|
|
873
|
+
this.pendingChanges.shift();
|
|
874
|
+
this.processPendingChange();
|
|
875
|
+
});
|
|
876
|
+
}
|
|
763
877
|
|
|
764
878
|
/**
|
|
765
879
|
* If the retrievedSchema has changed the new retrievedSchema is returned.
|
|
@@ -791,6 +905,8 @@ export default class Form<
|
|
|
791
905
|
errors: [] as unknown,
|
|
792
906
|
schemaValidationErrors: [] as unknown,
|
|
793
907
|
schemaValidationErrorSchema: {},
|
|
908
|
+
initialDefaultsGenerated: false,
|
|
909
|
+
customErrors: undefined,
|
|
794
910
|
} as FormState<T, S, F>;
|
|
795
911
|
|
|
796
912
|
this.setState(state, () => onChange && onChange({ ...this.state, ...state }));
|
|
@@ -866,10 +982,28 @@ export default class Form<
|
|
|
866
982
|
}
|
|
867
983
|
};
|
|
868
984
|
|
|
985
|
+
/** Extracts the `GlobalFormOptions` from the given Form `props`
|
|
986
|
+
*
|
|
987
|
+
* @param props - The form props to extract the global form options from
|
|
988
|
+
* @returns - The `GlobalFormOptions` from the props
|
|
989
|
+
* @private
|
|
990
|
+
*/
|
|
991
|
+
private getGlobalFormOptions(props: FormProps<T, S, F>): GlobalFormOptions {
|
|
992
|
+
const {
|
|
993
|
+
uiSchema = {},
|
|
994
|
+
experimental_componentUpdateStrategy,
|
|
995
|
+
idSeparator = DEFAULT_ID_SEPARATOR,
|
|
996
|
+
idPrefix = DEFAULT_ID_PREFIX,
|
|
997
|
+
} = props;
|
|
998
|
+
const rootFieldId = uiSchema['ui:rootFieldId'];
|
|
999
|
+
// Omit any options that are undefined or null
|
|
1000
|
+
return { idPrefix: rootFieldId || idPrefix, idSeparator, experimental_componentUpdateStrategy };
|
|
1001
|
+
}
|
|
1002
|
+
|
|
869
1003
|
/** Returns the registry for the form */
|
|
870
1004
|
getRegistry(): Registry<T, S, F> {
|
|
871
1005
|
const { translateString: customTranslateString, uiSchema = {} } = this.props;
|
|
872
|
-
const { schemaUtils } = this.state;
|
|
1006
|
+
const { schema, schemaUtils } = this.state;
|
|
873
1007
|
const { fields, templates, widgets, formContext, translateString } = getDefaultRegistry<T, S, F>();
|
|
874
1008
|
return {
|
|
875
1009
|
fields: { ...fields, ...this.props.fields },
|
|
@@ -882,11 +1016,12 @@ export default class Form<
|
|
|
882
1016
|
},
|
|
883
1017
|
},
|
|
884
1018
|
widgets: { ...widgets, ...this.props.widgets },
|
|
885
|
-
rootSchema:
|
|
1019
|
+
rootSchema: schema,
|
|
886
1020
|
formContext: this.props.formContext || formContext,
|
|
887
1021
|
schemaUtils,
|
|
888
1022
|
translateString: customTranslateString || translateString,
|
|
889
1023
|
globalUiOptions: uiSchema[UI_GLOBAL_OPTIONS_KEY],
|
|
1024
|
+
globalFormOptions: this.getGlobalFormOptions(this.props),
|
|
890
1025
|
};
|
|
891
1026
|
}
|
|
892
1027
|
|
|
@@ -1011,8 +1146,6 @@ export default class Form<
|
|
|
1011
1146
|
const {
|
|
1012
1147
|
children,
|
|
1013
1148
|
id,
|
|
1014
|
-
idPrefix,
|
|
1015
|
-
idSeparator,
|
|
1016
1149
|
className = '',
|
|
1017
1150
|
tagName,
|
|
1018
1151
|
name,
|
|
@@ -1025,12 +1158,11 @@ export default class Form<
|
|
|
1025
1158
|
noHtml5Validate = false,
|
|
1026
1159
|
disabled,
|
|
1027
1160
|
readonly,
|
|
1028
|
-
formContext,
|
|
1029
1161
|
showErrorList = 'top',
|
|
1030
1162
|
_internalFormWrapper,
|
|
1031
1163
|
} = this.props;
|
|
1032
1164
|
|
|
1033
|
-
const { schema, uiSchema, formData, errorSchema,
|
|
1165
|
+
const { schema, uiSchema, formData, errorSchema, fieldPathId } = this.state;
|
|
1034
1166
|
const registry = this.getRegistry();
|
|
1035
1167
|
const { SchemaField: _SchemaField } = registry.fields;
|
|
1036
1168
|
const { SubmitButton } = registry.templates.ButtonTemplates;
|
|
@@ -1068,10 +1200,7 @@ export default class Form<
|
|
|
1068
1200
|
schema={schema}
|
|
1069
1201
|
uiSchema={uiSchema}
|
|
1070
1202
|
errorSchema={errorSchema}
|
|
1071
|
-
|
|
1072
|
-
idPrefix={idPrefix}
|
|
1073
|
-
idSeparator={idSeparator}
|
|
1074
|
-
formContext={formContext}
|
|
1203
|
+
fieldPathId={fieldPathId}
|
|
1075
1204
|
formData={formData}
|
|
1076
1205
|
onChange={this.onChange}
|
|
1077
1206
|
onBlur={this.onBlur}
|