@rjsf/utils 6.1.2 → 6.2.3

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/lib/types.d.ts CHANGED
@@ -316,6 +316,8 @@ export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
316
316
  MoveUpButton: ComponentType<IconButtonProps<T, S, F>>;
317
317
  /** The template to use for the Remove button used for AdditionalProperties and Array items */
318
318
  RemoveButton: ComponentType<IconButtonProps<T, S, F>>;
319
+ /** The template to use for the Clear button used for input fields */
320
+ ClearButton: ComponentType<IconButtonProps<T, S, F>>;
319
321
  };
320
322
  } & {
321
323
  /** Allow this to support any named `ComponentType` or an object of named `ComponentType`s */
@@ -338,6 +340,9 @@ export type GlobalUISchemaOptions = GenericObjectType & {
338
340
  removable?: boolean;
339
341
  /** Field labels are rendered by default. Labels may be omitted by setting the `label` option to `false` */
340
342
  label?: boolean;
343
+ /** Flag, if set to `true`, will allow the text input fields to be cleared
344
+ */
345
+ allowClearTextInputs?: boolean;
341
346
  /** When using `additionalProperties`, key collision is prevented by appending a unique integer to the duplicate key.
342
347
  * This option allows you to change the separator between the original key name and the integer. Default is "-"
343
348
  */
@@ -447,6 +452,8 @@ export type Field<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
447
452
  };
448
453
  /** The properties that are passed to a `FieldTemplate` implementation */
449
454
  export type FieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & {
455
+ /** The FieldPathId containing the id and path for this field */
456
+ fieldPathId: FieldPathId;
450
457
  /** The id of the field in the hierarchy. You can use it to render a label targeting the wrapped widget */
451
458
  id: string;
452
459
  /** A string containing the base CSS classes, merged with any custom ones defined in your uiSchema */
@@ -914,10 +921,10 @@ export type UiSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
914
921
  */
915
922
  items?: UiSchema<ArrayElement<T>, S, F> | ((itemData: ArrayElement<T>, index: number, formContext?: F) => UiSchema<ArrayElement<T>, S, F>);
916
923
  };
917
- /** A `CustomValidator` function takes in a `formData`, `errors` and `uiSchema` objects and returns the given `errors`
924
+ /** A `CustomValidator` function takes in a `formData`, `errors`, `uiSchema` and `errorSchema` objects and returns the given `errors`
918
925
  * object back, while potentially adding additional messages to the `errors`
919
926
  */
920
- export type CustomValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = (formData: T | undefined, errors: FormValidation<T>, uiSchema?: UiSchema<T, S, F>) => FormValidation<T>;
927
+ export type CustomValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = (formData: T | undefined, errors: FormValidation<T>, uiSchema?: UiSchema<T, S, F>, errorSchema?: ErrorSchema<T>) => FormValidation<T>;
921
928
  /** An `ErrorTransformer` function will take in a list of `errors` & a `uiSchema` and potentially return a
922
929
  * transformation of those errors in what ever way it deems necessary
923
930
  */
@@ -1101,6 +1108,16 @@ export interface SchemaUtilsType<T = any, S extends StrictRJSFSchema = RJSFSchem
1101
1108
  * @returns - True if schema contains a select, otherwise false
1102
1109
  */
1103
1110
  isSelect(schema: S): boolean;
1111
+ /**
1112
+ * The function takes a `schema` and `formData` and returns a copy of the formData with any fields not defined in the schema removed.
1113
+ * This is useful for ensuring that only data that is relevant to the schema is preserved. Objects with `additionalProperties`
1114
+ * keyword set to `true` will not have their extra fields removed.
1115
+ *
1116
+ * @param schema - The schema to use for filtering the `formData`
1117
+ * @param [formData] - The formData to filter
1118
+ * @returns The new form data, with any fields not defined in the schema removed
1119
+ */
1120
+ omitExtraData(schema: S, formData?: T): T | undefined;
1104
1121
  /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and
1105
1122
  * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially
1106
1123
  * recursive resolution.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rjsf/utils",
3
- "version": "6.1.2",
3
+ "version": "6.2.3",
4
4
  "main": "dist/index.js",
5
5
  "module": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -23,6 +23,7 @@ import {
23
23
  isFilesArray,
24
24
  isMultiSelect,
25
25
  isSelect,
26
+ omitExtraData,
26
27
  retrieveSchema,
27
28
  sanitizeDataForNewSchema,
28
29
  toPathSchema,
@@ -297,6 +298,18 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
297
298
  isSelect(schema: S) {
298
299
  return isSelect<T, S, F>(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf);
299
300
  }
301
+ /**
302
+ * The function takes a `schema` and `formData` and returns a copy of the formData with any fields not defined in the schema removed.
303
+ * This is useful for ensuring that only data that is relevant to the schema is preserved. Objects with `additionalProperties`
304
+ * keyword set to `true` will not have their extra fields removed.
305
+ *
306
+ * @param schema - The schema to use for filtering the `formData`
307
+ * @param [formData] - The formData to filter
308
+ * @returns The new form data, with any fields not defined in the schema removed
309
+ */
310
+ omitExtraData(schema: S, formData?: T): T | undefined {
311
+ return omitExtraData<T, S, F>(this.validator, schema, this.rootSchema, formData);
312
+ }
300
313
 
301
314
  /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and
302
315
  * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially
package/src/enums.ts CHANGED
@@ -55,6 +55,8 @@ export enum TranslatableString {
55
55
  Type = 'Type',
56
56
  /** Label for the 'value' field, used by FallbackField */
57
57
  Value = 'Value',
58
+ /** Clear button title, used by IconButton */
59
+ ClearButton = 'clear input',
58
60
  // Strings with replaceable parameters
59
61
  /** Unknown field type reason, where %1 will be replaced with the type as provided by SchemaField */
60
62
  UnknownFieldType = 'Unknown field type %1',
@@ -8,6 +8,7 @@ import {
8
8
  CONST_KEY,
9
9
  DEFAULT_KEY,
10
10
  DEPENDENCIES_KEY,
11
+ IF_KEY,
11
12
  ONE_OF_KEY,
12
13
  PROPERTIES_KEY,
13
14
  REF_KEY,
@@ -251,9 +252,15 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
251
252
  !constIsAjvDataReference(schema)
252
253
  ) {
253
254
  defaults = schema[CONST_KEY] as unknown as T;
254
- } else if (isObject(defaults) && isObject(schema.default)) {
255
+ } else if (
256
+ isObject(defaults) &&
257
+ isObject(schema.default) &&
258
+ !schema[ANY_OF_KEY] &&
259
+ !schema[ONE_OF_KEY] &&
260
+ !schema[REF_KEY]
261
+ ) {
255
262
  // For object defaults, only override parent defaults that are defined in
256
- // schema.default.
263
+ // schema.default. Skip this for anyOf/oneOf/$ref schemas - they need special handling.
257
264
  defaults = mergeObjects(defaults!, schema.default as GenericObjectType) as T;
258
265
  } else if (DEFAULT_KEY in schema && !schema[ANY_OF_KEY] && !schema[ONE_OF_KEY] && !schema[REF_KEY]) {
259
266
  // If the schema has a default value, then we should use it as the default.
@@ -268,8 +275,11 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
268
275
  }
269
276
 
270
277
  // If the referenced schema exists and parentDefaults is not set
271
- // Then set the defaults from the current schema for the referenced schema
272
- if (schemaToCompute && !defaults) {
278
+ // Then set the defaults from the current schema for the referenced schema.
279
+ // Only do this if rawFormData has no meaningful data - we don't want to override user's existing values.
280
+ // Check for undefined OR empty object - rawFormData may be coerced to {} when not an object.
281
+ const hasNoExistingData = rawFormData === undefined || (isObject(rawFormData) && isEmpty(rawFormData));
282
+ if (schemaToCompute && !defaults && hasNoExistingData) {
273
283
  defaults = schema.default as T | undefined;
274
284
  }
275
285
 
@@ -478,12 +488,16 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
478
488
  {
479
489
  const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
480
490
  const schema: S = rawSchema;
481
- // This is a custom addition that fixes this issue:
482
- // https://github.com/rjsf-team/react-jsonschema-form/issues/3832
483
- const retrievedSchema =
484
- experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema
485
- ? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf)
486
- : schema;
491
+ // Retrieve the schema:
492
+ // - If schema contains `allOf` AND `experimental_defaultFormStateBehavior.allOf` is set to `populateDefaults`
493
+ // - OR if schema contains an 'if' AND `emptyObjectFields` is not set to `skipEmptyDefaults`
494
+ // This ensures we compute defaults correctly for schemas with these keywords.
495
+ const shouldRetrieveSchema =
496
+ (experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema) ||
497
+ (experimental_defaultFormStateBehavior?.emptyObjectFields !== 'skipEmptyDefaults' && IF_KEY in schema);
498
+ const retrievedSchema = shouldRetrieveSchema
499
+ ? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf)
500
+ : schema;
487
501
  const parentConst = retrievedSchema[CONST_KEY];
488
502
  const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
489
503
  (acc: GenericObjectType, key: string) => {
@@ -614,12 +628,14 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
614
628
  if (Array.isArray(defaults)) {
615
629
  defaults = defaults.map((item, idx) => {
616
630
  const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx);
631
+ const itemFormData = Array.isArray(rawFormData) ? rawFormData[idx] : undefined;
617
632
  return computeDefaults<T, S, F>(validator, schemaItem, {
618
633
  rootSchema,
619
634
  _recurseList,
620
635
  experimental_defaultFormStateBehavior,
621
636
  experimental_customMergeAllOf,
622
637
  parentDefaults: item,
638
+ rawFormData: itemFormData,
623
639
  required,
624
640
  shouldMergeDefaultsIntoFormData,
625
641
  initialDefaultsGenerated,
@@ -8,6 +8,7 @@ import getFromSchema from './getFromSchema';
8
8
  import isFilesArray from './isFilesArray';
9
9
  import isMultiSelect from './isMultiSelect';
10
10
  import isSelect from './isSelect';
11
+ import omitExtraData, { getUsedFormData, getFieldNames } from './omitExtraData';
11
12
  import retrieveSchema from './retrieveSchema';
12
13
  import sanitizeDataForNewSchema from './sanitizeDataForNewSchema';
13
14
  import toPathSchema from './toPathSchema';
@@ -17,12 +18,15 @@ export {
17
18
  findSelectedOptionInXxxOf,
18
19
  getDefaultFormState,
19
20
  getDisplayLabel,
21
+ getFieldNames, // Exported only to prevent breaking change in core
20
22
  getClosestMatchingOption,
21
23
  getFirstMatchingOption,
22
24
  getFromSchema,
25
+ getUsedFormData, // Exported only to prevent breaking change in core
23
26
  isFilesArray,
24
27
  isMultiSelect,
25
28
  isSelect,
29
+ omitExtraData,
26
30
  retrieveSchema,
27
31
  sanitizeDataForNewSchema,
28
32
  toPathSchema,
@@ -0,0 +1,93 @@
1
+ import pick from 'lodash/pick';
2
+ import isEmpty from 'lodash/isEmpty';
3
+ import get from 'lodash/get';
4
+
5
+ import { NAME_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG } from '../constants';
6
+ import { GenericObjectType, PathSchema, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
7
+ import retrieveSchema from './retrieveSchema';
8
+ import toPathSchema from './toPathSchema';
9
+
10
+ /** Returns the `formData` with only the elements specified in the `fields` list
11
+ *
12
+ * @param formData - The data for the `Form`
13
+ * @param fields - The fields to keep while filtering
14
+ * @deprecated - To be removed as an exported `@rjsf/utils` function in a future release
15
+ */
16
+ export function getUsedFormData<T = any>(formData: T | undefined, fields: string[]): T | undefined {
17
+ // For the case of a single input form
18
+ if (fields.length === 0 && typeof formData !== 'object') {
19
+ return formData;
20
+ }
21
+
22
+ const data: GenericObjectType = pick(formData, fields);
23
+ if (Array.isArray(formData)) {
24
+ return Object.keys(data).map((key: string) => data[key]) as unknown as T;
25
+ }
26
+
27
+ return data as T;
28
+ }
29
+
30
+ /** Returns the list of field names from inspecting the `pathSchema` as well as using the `formData`
31
+ *
32
+ * @param pathSchema - The `PathSchema` object for the form
33
+ * @param [formData] - The form data to use while checking for empty objects/arrays
34
+ * @deprecated - To be removed as an exported `@rjsf/utils` function in a future release
35
+ */
36
+ export function getFieldNames<T = any>(pathSchema: PathSchema<T>, formData?: T): string[][] {
37
+ const formValueHasData = (value: T, isLeaf: boolean) =>
38
+ typeof value !== 'object' || isEmpty(value) || (isLeaf && !isEmpty(value));
39
+ const getAllPaths = (_obj: GenericObjectType, acc: string[][] = [], paths: string[][] = [[]]) => {
40
+ const objKeys = Object.keys(_obj);
41
+ objKeys.forEach((key: string) => {
42
+ const data = _obj[key];
43
+ if (typeof data === 'object') {
44
+ const newPaths = paths.map((path) => [...path, key]);
45
+ // If an object is marked with additionalProperties, all its keys are valid
46
+ if (data[RJSF_ADDITIONAL_PROPERTIES_FLAG] && data[NAME_KEY] !== '') {
47
+ acc.push(data[NAME_KEY]);
48
+ } else {
49
+ getAllPaths(data, acc, newPaths);
50
+ }
51
+ } else if (key === NAME_KEY && data !== '') {
52
+ paths.forEach((path) => {
53
+ const formValue = get(formData, path);
54
+ const isLeaf = objKeys.length === 1;
55
+ // adds path to fieldNames if it points to a value or an empty object/array which is not a leaf
56
+ if (
57
+ formValueHasData(formValue, isLeaf) ||
58
+ (Array.isArray(formValue) && formValue.every((val) => formValueHasData(val, isLeaf)))
59
+ ) {
60
+ acc.push(path);
61
+ }
62
+ });
63
+ }
64
+ });
65
+ return acc;
66
+ };
67
+
68
+ return getAllPaths(pathSchema);
69
+ }
70
+
71
+ /** Takes a `schema` and `formData` and returns a copy of the formData with any fields not defined in the schema removed.
72
+ * This is useful for ensuring that only data that is relevant to the schema is preserved. Objects with
73
+ * `additionalProperties` keyword set to `true` will not have their extra fields removed.
74
+ *
75
+ * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
76
+ * @param schema - The schema to use for filtering the formData
77
+ * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
78
+ * @param [formData] - The data for the `Form`
79
+ * @returns The `formData` after omitting extra data
80
+ */
81
+ export default function omitExtraData<
82
+ T = any,
83
+ S extends StrictRJSFSchema = RJSFSchema,
84
+ F extends FormContextType = any,
85
+ >(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S = {} as S, formData?: T): T | undefined {
86
+ const retrievedSchema = retrieveSchema(validator, schema, rootSchema, formData);
87
+ const pathSchema = toPathSchema(validator, retrievedSchema, '', rootSchema, formData);
88
+ const fieldNames = getFieldNames(pathSchema, formData);
89
+ const lodashFieldNames = fieldNames.map((fieldPaths: string[]) =>
90
+ Array.isArray(fieldPaths) ? fieldPaths.join('.') : fieldPaths,
91
+ );
92
+ return getUsedFormData(formData, lodashFieldNames);
93
+ }
@@ -6,6 +6,7 @@ import {
6
6
  ALL_OF_KEY,
7
7
  ANY_OF_KEY,
8
8
  DEPENDENCIES_KEY,
9
+ IF_KEY,
9
10
  ITEMS_KEY,
10
11
  NAME_KEY,
11
12
  ONE_OF_KEY,
@@ -48,7 +49,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
48
49
  _recurseList: S[] = [],
49
50
  experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
50
51
  ): PathSchema<T> {
51
- if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
52
+ if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema || IF_KEY in schema) {
52
53
  const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf);
53
54
  const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
54
55
  if (sameSchemaIndex === -1) {
package/src/types.ts CHANGED
@@ -380,6 +380,8 @@ export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
380
380
  MoveUpButton: ComponentType<IconButtonProps<T, S, F>>;
381
381
  /** The template to use for the Remove button used for AdditionalProperties and Array items */
382
382
  RemoveButton: ComponentType<IconButtonProps<T, S, F>>;
383
+ /** The template to use for the Clear button used for input fields */
384
+ ClearButton: ComponentType<IconButtonProps<T, S, F>>;
383
385
  };
384
386
  } & {
385
387
  /** Allow this to support any named `ComponentType` or an object of named `ComponentType`s */
@@ -401,6 +403,9 @@ export type GlobalUISchemaOptions = GenericObjectType & {
401
403
  removable?: boolean;
402
404
  /** Field labels are rendered by default. Labels may be omitted by setting the `label` option to `false` */
403
405
  label?: boolean;
406
+ /** Flag, if set to `true`, will allow the text input fields to be cleared
407
+ */
408
+ allowClearTextInputs?: boolean;
404
409
  /** When using `additionalProperties`, key collision is prevented by appending a unique integer to the duplicate key.
405
410
  * This option allows you to change the separator between the original key name and the integer. Default is "-"
406
411
  */
@@ -524,6 +529,8 @@ export type FieldTemplateProps<
524
529
  S extends StrictRJSFSchema = RJSFSchema,
525
530
  F extends FormContextType = any,
526
531
  > = RJSFBaseProps<T, S, F> & {
532
+ /** The FieldPathId containing the id and path for this field */
533
+ fieldPathId: FieldPathId;
527
534
  /** The id of the field in the hierarchy. You can use it to render a label targeting the wrapped widget */
528
535
  id: string;
529
536
  /** A string containing the base CSS classes, merged with any custom ones defined in your uiSchema */
@@ -1143,13 +1150,14 @@ export type UiSchema<
1143
1150
  | ((itemData: ArrayElement<T>, index: number, formContext?: F) => UiSchema<ArrayElement<T>, S, F>);
1144
1151
  };
1145
1152
 
1146
- /** A `CustomValidator` function takes in a `formData`, `errors` and `uiSchema` objects and returns the given `errors`
1153
+ /** A `CustomValidator` function takes in a `formData`, `errors`, `uiSchema` and `errorSchema` objects and returns the given `errors`
1147
1154
  * object back, while potentially adding additional messages to the `errors`
1148
1155
  */
1149
1156
  export type CustomValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = (
1150
1157
  formData: T | undefined,
1151
1158
  errors: FormValidation<T>,
1152
1159
  uiSchema?: UiSchema<T, S, F>,
1160
+ errorSchema?: ErrorSchema<T>,
1153
1161
  ) => FormValidation<T>;
1154
1162
 
1155
1163
  /** An `ErrorTransformer` function will take in a list of `errors` & a `uiSchema` and potentially return a
@@ -1360,6 +1368,16 @@ export interface SchemaUtilsType<T = any, S extends StrictRJSFSchema = RJSFSchem
1360
1368
  * @returns - True if schema contains a select, otherwise false
1361
1369
  */
1362
1370
  isSelect(schema: S): boolean;
1371
+ /**
1372
+ * The function takes a `schema` and `formData` and returns a copy of the formData with any fields not defined in the schema removed.
1373
+ * This is useful for ensuring that only data that is relevant to the schema is preserved. Objects with `additionalProperties`
1374
+ * keyword set to `true` will not have their extra fields removed.
1375
+ *
1376
+ * @param schema - The schema to use for filtering the `formData`
1377
+ * @param [formData] - The formData to filter
1378
+ * @returns The new form data, with any fields not defined in the schema removed
1379
+ */
1380
+ omitExtraData(schema: S, formData?: T): T | undefined;
1363
1381
  /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and
1364
1382
  * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially
1365
1383
  * recursive resolution.