From 5555123b72c040f47eb2d9299de9bd3603888248 Mon Sep 17 00:00:00 2001 From: Heath C <51679588+heath-freenome@users.noreply.github.com> Date: Tue, 24 Jan 2023 07:34:38 -0800 Subject: [PATCH] fix: Allow `formData` to be optional everywhere (#3395) Fixes #3305 by ensuring that `formData` can be optional everywhere There were some types which were requiring `formData` to be required even though it really can be optional everywhere - In `@rjsf/utils`, updated the `formData` prop in all types where it was required to make it optional - Also updated all function arguments where it was required to make `formData` optional - In `@rjsf/validator-ajv6` and `@rjsf/validator-ajv8`, updated places where `formData` was required as a function parameter to make it optional instead - In `@rjsf/core`, updated places where `formData` was required as a function parameter to make it optional instead - In `utility-functions.md`, updated the documentation of optional parameters without default values to ensure they included `| undefined` in the type of the parameter - In `5.x upgrade guide.md`, updated the note about improved typescript typing in `@rjsf/utils` to indicate that it may require updating typing in the user's implementations - Updated the `CHANGELOG.md` accordingly --- CHANGELOG.md | 10 ++++ docs/5.x upgrade guide.md | 3 +- docs/api-reference/utility-functions.md | 56 +++++++++---------- packages/core/src/components/Form.tsx | 19 +++++-- .../components/fields/MultiSchemaField.tsx | 6 +- .../src/components/fields/ObjectField.tsx | 16 ++++-- .../src/components/fields/SchemaField.tsx | 2 +- packages/utils/src/createSchemaUtils.ts | 4 +- .../utils/src/mergeDefaultsWithFormData.ts | 10 ++-- packages/utils/src/schema/retrieveSchema.ts | 4 +- packages/utils/src/types.ts | 20 ++++--- .../test/mergeDefaultsWithFormData.test.ts | 2 +- packages/validator-ajv6/src/validator.ts | 2 +- .../test/utilsTests/getTestValidator.ts | 6 +- packages/validator-ajv8/src/validator.ts | 2 +- .../test/utilsTests/getTestValidator.ts | 8 ++- 16 files changed, 104 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33caa31ed5..7c0ef9724b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ should change the heading of the (upcoming) version to include a major version b - [#1661](https://github.com/rjsf-team/react-jsonschema-form/issues/1661) - And probably others - Updated `ObjectField` to deal with `additionalProperties` with `oneOf`/`anyOf`, fixing [#2538](https://github.com/rjsf-team/react-jsonschema-form/issues/2538) +- Updated `Form`, `MultiSchemaField`, `ObjectField` and `SchemaField` to properly support making `formData` optional, fixing [#3305](https://github.com/rjsf-team/react-jsonschema-form/issues/3305) ## @rjsf/material-ui - Fix shrinking of `SelectWidget` label only if value is not empty, fixing [#3369](https://github.com/rjsf-team/react-jsonschema-form/issues/3369) @@ -41,11 +42,20 @@ should change the heading of the (upcoming) version to include a major version b - Deprecated `getMatchingOption()` and updated all calls to it in other utility functions to use `getFirstMatchingOption()` - Updated `stubExistingAdditionalProperties()` to deal with `additionalProperties` with `oneOf`/`anyOf`, fixing [#2538](https://github.com/rjsf-team/react-jsonschema-form/issues/2538) - Updated `getSchemaType()` to grab the type of the first element of a `oneOf`/`anyOf`, fixing [#1654](https://github.com/rjsf-team/react-jsonschema-form/issues/1654) +- Updated all props or function parameters of the generic type `T` to allow for them to be optionally provided, fixing [#3305](https://github.com/rjsf-team/react-jsonschema-form/issues/3305) + - This was done in both the types file and the actual implementation code + +## @rjsf/validator-ajv6 +- Updated places where `formData` was required as a function argument to make it optional, fixing [#3305](https://github.com/rjsf-team/react-jsonschema-form/issues/3305) + +## @rjsf/validator-ajv8 +- Updated places where `formData` was required as a function argument to make it optional, fixing [#3305](https://github.com/rjsf-team/react-jsonschema-form/issues/3305) ## Dev / docs / playground - Updated the playground to `onFormDataEdited()` to only change the formData in the state if the `JSON.stringify()` of the old and new values are different, partially fixing [#3236](https://github.com/rjsf-team/react-jsonschema-form/issues/3236) - Updated the playground `npm start` command to always use the `--force` option to avoid issues where changes made to other packages weren't getting picked up due to `vite` caching - Updated the documentation for `utility-functions` and the `5.x upgrade guide` to add the new utility functions and to document the deprecation of `getMatchingOption()` + - Also updated `utility-functions`, making all optional parameters without a default (as denoted by the syntax `[]: `) to add ` | undefined` onto the type to make it clear it supports passing in undefined as a value. # 5.0.0-beta-17 diff --git a/docs/5.x upgrade guide.md b/docs/5.x upgrade guide.md index 244326d75e..115123ac92 100644 --- a/docs/5.x upgrade guide.md +++ b/docs/5.x upgrade guide.md @@ -45,7 +45,8 @@ In version 4, RJSF exported all its types directly from `@rjsf/core`. In version 5, only the types for the `Form` component and the `withTheme()` HOC are exported directly from `@rjsf/core`. All the rest of the types for RJSF are now exported from the new `@rjsf/utils` package. -NOTE: The types in `@rjsf/utils` have been improved significantly from those in version 4. +NOTE: The types in `@rjsf/utils` have been improved significantly from those in version 4 and as a result may require you to fix your Typescript typings and add some casts. + Some of the most notable changes are: - `RJSFSchema` has replaced the use of `JSON7Schema` for future compatibility reasons. diff --git a/docs/api-reference/utility-functions.md b/docs/api-reference/utility-functions.md index 1ed58d387f..3891844ecb 100644 --- a/docs/api-reference/utility-functions.md +++ b/docs/api-reference/utility-functions.md @@ -61,7 +61,7 @@ The UI for the field can expand if it has additional properties, is not forced a #### Parameters - schema: S - The schema for the field that is being checked - [uiSchema={}]: UiSchema - The uiSchema for the field -- [formData]: T - The formData for the field +- [formData]: T | undefined - The formData for the field #### Returns - boolean: True if the schema element has additionalProperties, is expandable, and not at the maxProperties limit @@ -154,7 +154,7 @@ Using the `schema`, `defaultType` and `options`, extract out the props for the ` #### Parameters - schema: S - The schema for the field provided by the widget -- [defaultType]: string - The default type, if any, for the field provided by the widget +- [defaultType]: string | undefined - The default type, if any, for the field provided by the widget - [options={}]: UIOptionsType - The UI Options for the field provided by the widget - [autoDefaultStepAny=true]: boolean - Determines whether to auto-default step=any when the type is number and no step #### Returns @@ -311,11 +311,11 @@ When merging defaults and form data, we want to merge in this specific way: - scalars are overwritten/set by form data #### Parameters -- defaults: T - The defaults to merge -- formData: T - The form data into which the defaults will be merged +- [defaults]: T | undefined - The defaults to merge +- [formData]: T | undefined - The form data into which the defaults will be merged #### Returns -- T: The resulting merged form data with defaults +- T | undefined: The resulting merged form data with defaults ### mergeObjects() Recursively merge deeply nested objects. @@ -409,7 +409,7 @@ If the value is an empty string, then the `emptyValue` from the `options` is ret #### Parameters - schema: S - The schema to used to determine the value's true type - [value]: any - The value to convert -- [options]: UIOptionsType - The UIOptionsType from which to potentially extract the `emptyValue` +- [options]: UIOptionsType | undefined - The UIOptionsType from which to potentially extract the `emptyValue` #### Returns - string | boolean | number | string[] | boolean[] | number[] | undefined: The `value` converted to the proper type @@ -498,8 +498,8 @@ Returns the superset of `formData` that includes the given set updated to includ #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - theSchema: S - The schema for which the default state is desired -- [formData]: T - The current formData, if any, onto which to provide any missing defaults -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s +- [formData]: T | undefined - The current formData, if any, onto which to provide any missing defaults +- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s - [includeUndefinedValues=false]: boolean | "excludeObjectChildren" - Optional flag, if true, cause undefined values to be added as defaults. If "excludeObjectChildren", pass `includeUndefinedValues` as false when computing defaults for any nested object properties. #### Returns @@ -512,7 +512,7 @@ Determines whether the combination of `schema` and `uiSchema` properties indicat - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - schema: S - The schema for which the display label flag is desired - [uiSchema={}]: UiSchema - The UI schema from which to derive potentially displayable information -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s +- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s #### Returns - boolean: True if the label should be displayed or false if it should not @@ -526,7 +526,7 @@ The closest match is determined using the number of matching properties, and mor #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - rootSchema: S - The root schema, used to primarily to look up `$ref`s -- formData: T | undefined - The current formData, if any, used to figure out a match +- [formData]: T | undefined - The current formData, if any, used to figure out a match - options: S[] - The list of options to find a matching options from - [selectedOption=-1]: number - The index of the currently selected option, defaulted to -1 if not specified @@ -539,7 +539,7 @@ Always returns the first option if there is nothing that matches. #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary -- formData: T | undefined - The current formData, if any, used to figure out a match +- [formData]: T | undefined - The current formData, if any, used to figure out a match - options: S[] - The list of options to find a matching options from - rootSchema: S - The root schema, used to primarily to look up `$ref`s @@ -552,7 +552,7 @@ Deprecated, use `getFirstMatchingOption()` instead. #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary -- formData: T | undefined - The current formData, if any, used to figure out a match +- [formData]: T | undefined - The current formData, if any, used to figure out a match - options: S[] - The list of options to find a matching options from - rootSchema: S - The root schema, used to primarily to look up `$ref`s @@ -566,7 +566,7 @@ Checks to see if the `schema` and `uiSchema` combination represents an array of - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - schema: S - The schema for which check for array of files flag is desired - [uiSchema={}]: UiSchema - The UI schema from which to check the widget -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s +- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s #### Returns - boolean: True if schema/uiSchema contains an array of files, otherwise false @@ -577,7 +577,7 @@ Checks to see if the `schema` combination represents a multi-select #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - schema: S - The schema for which check for a multi-select flag is desired -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s +- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s #### Returns - boolean: True if schema contains a multi-select, otherwise false @@ -588,7 +588,7 @@ Checks to see if the `schema` combination represents a select #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - theSchema: S - The schema for which check for a select flag is desired -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s +- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s #### Returns - boolean: True if schema contains a select, otherwise false @@ -600,7 +600,7 @@ If no `additionalErrorSchema` is passed, then `validationData` is returned. #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used to convert an ErrorSchema to a list of errors - validationData: ValidationData - The current `ValidationData` into which to merge the additional errors -- [additionalErrorSchema]: ErrorSchema - The additional set of errors in an `ErrorSchema` +- [additionalErrorSchema]: ErrorSchema | undefined - The additional set of errors in an `ErrorSchema` #### Returns - ValidationData: The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided. @@ -614,7 +614,7 @@ potentially recursive resolution. - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs - schema: S - The schema for which retrieving a schema is desired - [rootSchema={}]: S - The root schema that will be forwarded to all the APIs -- [rawFormData]: T - The current formData, if any, to assist retrieving a schema +- [rawFormData]: T | undefined - The current formData, if any, to assist retrieving a schema #### Returns - RJSFSchema: The schema having its conditions, additional properties, references and dependencies resolved @@ -628,8 +628,8 @@ Also, any properties in the old schema that are non-existent in the new schema a #### Parameters - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - rootSchema: S - The root JSON schema of the entire form -- [newSchema]: S - The new schema for which the data is being sanitized -- [oldSchema]: S - The old schema from which the data originated +- [newSchema]: S | undefined - The new schema for which the data is being sanitized +- [oldSchema]: S | undefined - The old schema from which the data originated - [data={}]: any - The form data associated with the schema, defaulting to an empty object when undefined #### Returns @@ -642,8 +642,8 @@ Generates an `IdSchema` object for the `schema`, recursively - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - schema: S - The schema for which the `IdSchema` is desired - [id]: string | null - The base id for the schema -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s -- [formData]: T - The current formData, if any, to assist retrieving a schema +- [rootSchema]: S | undefined- The root schema, used to primarily to look up `$ref`s +- [formData]: T | undefined - The current formData, if any, to assist retrieving a schema - [idPrefix='root']: string - The prefix to use for the id - [idSeparator='_']: string - The separator to use for the path segments in the id @@ -657,8 +657,8 @@ Generates an `PathSchema` object for the `schema`, recursively - validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary - schema: S - The schema for which the `PathSchema` is desired - [name='']: string - The base name for the schema -- [rootSchema]: S - The root schema, used to primarily to look up `$ref`s -- [formData]: T - The current formData, if any, to assist retrieving a schema +- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s +- [formData]: T | undefined - The current formData, if any, to assist retrieving a schema #### Returns - PathSchema - The `PathSchema` object for the `schema` @@ -684,7 +684,7 @@ Use this class to add, replace or clear errors in an error schema by using eithe Once you are done building the `ErrorSchema`, you can get the result and/or reset all the errors back to an initial set and start again. #### Parameters -- [initialSchema]: ErrorSchema - The optional set of initial errors, that will be cloned into the class +- [initialSchema]: ErrorSchema | undefined - The optional set of initial errors, that will be cloned into the class #### Returns - ErrorSchemaBuilder - The instance of the `ErrorSchemaBuilder` class @@ -709,7 +709,7 @@ const errorSchema: ErrorSchema = builder.ErrorSchema; Resets all errors in the `ErrorSchemaBuilder` back to the `initialSchema` if provided, otherwise an empty set. #### Parameters -- [initialSchema]: ErrorSchema - The optional set of initial errors, that will be cloned into the class +- [initialSchema]: ErrorSchema | undefined - The optional set of initial errors, that will be cloned into the class #### Returns - ErrorSchemaBuilder - The instance of the `ErrorSchemaBuilder` class @@ -720,7 +720,7 @@ For more information about how to specify the path see the [eslint lodash plugin #### Parameters - errorOrList: string | string[] - The error or list of errors to add into the `ErrorSchema` -- [pathOfError]: string | string[] - The optional path into the `ErrorSchema` at which to add the error(s) +- [pathOfError]: string | string[] | undefined - The optional path into the `ErrorSchema` at which to add the error(s) #### Returns - ErrorSchemaBuilder - The instance of the `ErrorSchemaBuilder` class @@ -731,7 +731,7 @@ For more information about how to specify the path see the [eslint lodash plugin #### Parameters - errorOrList: string | string[] - The error or list of errors to add into the `ErrorSchema` -- [pathOfError]: string | string[] - The optional path into the `ErrorSchema` at which to add the error(s) +- [pathOfError]: string | string[] | undefined - The optional path into the `ErrorSchema` at which to add the error(s) #### Returns - ErrorSchemaBuilder - The instance of the `ErrorSchemaBuilder` class @@ -741,7 +741,7 @@ Clears the error(s) in the `ErrorSchema` at either the root level or the locatio For more information about how to specify the path see the [eslint lodash plugin docs](https://github.com/wix/eslint-plugin-lodash/blob/master/docs/rules/path-style.md). #### Parameters -- [pathOfError]: string | string[] - The optional path into the `ErrorSchema` at which to add the error(s) +- [pathOfError]: string | string[] | undefined - The optional path into the `ErrorSchema` at which to add the error(s) #### Returns - ErrorSchemaBuilder - The instance of the `ErrorSchemaBuilder` class diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index a2b6965c33..9a8dcf6e3b 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -206,7 +206,7 @@ export interface FormState< /** The schemaUtils implementation used by the `Form`, created from the `validator` and the `schema` */ schemaUtils: SchemaUtilsType; /** The current data for the form, computed from the `formData` prop and the changes made by the user */ - formData: T; + formData?: T; /** Flag indicating whether the form is in edit mode, true when `formData` is passed to the form, otherwise false */ edit: boolean; /** The current list of errors for the form, includes `extraErrors` */ @@ -405,7 +405,7 @@ export default class Form< * @param altSchemaUtils - The alternate schemaUtils to use for validation */ validate( - formData: T, + formData: T | undefined, schema = this.props.schema, altSchemaUtils?: SchemaUtilsType ): ValidationData { @@ -455,7 +455,10 @@ export default class Form< * @param formData - The data for the `Form` * @param fields - The fields to keep while filtering */ - getUsedFormData = (formData: T, fields: string[][]): T => { + getUsedFormData = ( + formData: T | undefined, + fields: string[][] + ): T | undefined => { // For the case of a single input form if (fields.length === 0 && typeof formData !== "object") { return formData; @@ -476,9 +479,9 @@ export default class Form< /** Returns the list of field names from inspecting the `pathSchema` as well as using the `formData` * * @param pathSchema - The `PathSchema` object for the form - * @param formData - The form data to use while checking for empty objects/arrays + * @param [formData] - The form data to use while checking for empty objects/arrays */ - getFieldNames = (pathSchema: PathSchema, formData: T): string[][] => { + getFieldNames = (pathSchema: PathSchema, formData?: T): string[][] => { const getAllPaths = ( _obj: GenericObjectType, acc: string[][] = [], @@ -524,7 +527,11 @@ export default class Form< * @param newErrorSchema - The new `ErrorSchema` based on the field change * @param id - The id of the field that caused the change */ - onChange = (formData: T, newErrorSchema?: ErrorSchema, id?: string) => { + onChange = ( + formData: T | undefined, + newErrorSchema?: ErrorSchema, + id?: string + ) => { const { extraErrors, omitExtraData, diff --git a/packages/core/src/components/fields/MultiSchemaField.tsx b/packages/core/src/components/fields/MultiSchemaField.tsx index d1e9ea9330..526d6f71a0 100644 --- a/packages/core/src/components/fields/MultiSchemaField.tsx +++ b/packages/core/src/components/fields/MultiSchemaField.tsx @@ -81,7 +81,11 @@ class AnyOfField< * @param options - The list of options to choose from * @return - The index of the `option` that best matches the `formData` */ - getMatchingOption(selectedOption: number, formData: T, options: S[]) { + getMatchingOption( + selectedOption: number, + formData: T | undefined, + options: S[] + ) { const { schemaUtils } = this.props.registry; const option = schemaUtils.getClosestMatchingOption( diff --git a/packages/core/src/components/fields/ObjectField.tsx b/packages/core/src/components/fields/ObjectField.tsx index 3885a96096..1192428b88 100644 --- a/packages/core/src/components/fields/ObjectField.tsx +++ b/packages/core/src/components/fields/ObjectField.tsx @@ -67,7 +67,11 @@ class ObjectField< * @returns - The onPropertyChange callback for the `name` property */ onPropertyChange = (name: string, addedByAdditionalProperties = false) => { - return (value: T, newErrorSchema?: ErrorSchema, id?: string) => { + return ( + value: T | undefined, + newErrorSchema?: ErrorSchema, + id?: string + ) => { const { formData, onChange, errorSchema } = this.props; if (value === undefined && addedByAdditionalProperties) { // Don't set value = undefined for fields added by @@ -79,7 +83,7 @@ class ObjectField< // set empty values to the empty string. value = "" as unknown as T; } - const newFormData = { ...formData, [name]: value }; + const newFormData = { ...formData, [name]: value } as unknown as T; onChange( newFormData, errorSchema && @@ -102,7 +106,7 @@ class ObjectField< return (event: DragEvent) => { event.preventDefault(); const { onChange, formData } = this.props; - const copiedFormData = { ...formData }; + const copiedFormData = { ...formData } as T; unset(copiedFormData, key); onChange(copiedFormData); }; @@ -112,10 +116,10 @@ class ObjectField< * that is already not assigned is found. * * @param preferredKey - The preferred name of a new key - * @param formData - The form data in which to check if the desired key already exists + * @param [formData] - The form data in which to check if the desired key already exists * @returns - The name of the next available key from `preferredKey` */ - getAvailableKey = (preferredKey: string, formData: T) => { + getAvailableKey = (preferredKey: string, formData?: T) => { const { uiSchema } = this.props; const { duplicateKeySuffixSeparator = "-" } = getUiOptions( uiSchema @@ -200,7 +204,7 @@ class ObjectField< return; } const { formData, onChange, registry } = this.props; - const newFormData = { ...formData }; + const newFormData = { ...formData } as T; let type: RJSFSchema["type"] = undefined; if (isObject(schema.additionalProperties)) { diff --git a/packages/core/src/components/fields/SchemaField.tsx b/packages/core/src/components/fields/SchemaField.tsx index a08dd11def..01a864e7b5 100644 --- a/packages/core/src/components/fields/SchemaField.tsx +++ b/packages/core/src/components/fields/SchemaField.tsx @@ -156,7 +156,7 @@ function SchemaFieldRender< * `onChange` chain if it is not already being provided from a deeper level in the hierarchy */ const handleFieldComponentChange = React.useCallback( - (formData: T, newErrorSchema?: ErrorSchema, id?: string) => { + (formData: T | undefined, newErrorSchema?: ErrorSchema, id?: string) => { const theId = id || fieldId; return onChange(formData, newErrorSchema, theId); }, diff --git a/packages/utils/src/createSchemaUtils.ts b/packages/utils/src/createSchemaUtils.ts index 5fb8a1c635..5c4cc974f5 100644 --- a/packages/utils/src/createSchemaUtils.ts +++ b/packages/utils/src/createSchemaUtils.ts @@ -168,7 +168,7 @@ class SchemaUtils< * @returns - The index of the matched option or 0 if none is available * @deprecated */ - getMatchingOption(formData: T, options: S[]) { + getMatchingOption(formData: T | undefined, options: S[]) { return getMatchingOption( this.validator, formData, @@ -238,7 +238,7 @@ class SchemaUtils< * @param [rawFormData] - The current formData, if any, to assist retrieving a schema * @returns - The schema having its conditions, additional properties, references and dependencies resolved */ - retrieveSchema(schema: S, rawFormData: T) { + retrieveSchema(schema: S, rawFormData?: T) { return retrieveSchema( this.validator, schema, diff --git a/packages/utils/src/mergeDefaultsWithFormData.ts b/packages/utils/src/mergeDefaultsWithFormData.ts index 98c90c17eb..07d03fe928 100644 --- a/packages/utils/src/mergeDefaultsWithFormData.ts +++ b/packages/utils/src/mergeDefaultsWithFormData.ts @@ -13,14 +13,14 @@ import { GenericObjectType } from "../src"; * - when the array is not set in form data, the default is copied over * - scalars are overwritten/set by form data * - * @param defaults - The defaults to merge - * @param formData - The form data into which the defaults will be merged + * @param [defaults] - The defaults to merge + * @param [formData] - The form data into which the defaults will be merged * @returns - The resulting merged form data with defaults */ export default function mergeDefaultsWithFormData( - defaults: T, - formData: T -): T { + defaults?: T, + formData?: T +): T | undefined { if (Array.isArray(formData)) { const defaultsArray = Array.isArray(defaults) ? defaults : []; const mapped = formData.map((value, idx) => { diff --git a/packages/utils/src/schema/retrieveSchema.ts b/packages/utils/src/schema/retrieveSchema.ts index 2868636603..097c8b26e4 100644 --- a/packages/utils/src/schema/retrieveSchema.ts +++ b/packages/utils/src/schema/retrieveSchema.ts @@ -32,14 +32,14 @@ import getFirstMatchingOption from "./getFirstMatchingOption"; * @param validator - An implementation of the `ValidatorType` interface that is used to detect valid schema conditions * @param schema - The schema for which resolving a condition is desired * @param rootSchema - The root schema that will be forwarded to all the APIs - * @param formData - The current formData to assist retrieving a schema + * @param [formData] - The current formData to assist retrieving a schema * @returns - A schema with the appropriate condition resolved */ export function resolveCondition< T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any ->(validator: ValidatorType, schema: S, rootSchema: S, formData: T) { +>(validator: ValidatorType, schema: S, rootSchema: S, formData?: T) { const { if: expression, then, diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 260254e97b..35413ca373 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -314,11 +314,15 @@ export interface FieldProps< /** The tree of unique ids for every child field */ idSchema: IdSchema; /** The data for this field */ - formData: T; + formData?: T; /** The tree of errors for this field and its children */ errorSchema?: ErrorSchema; /** The field change event handler; called with the updated form data and an optional `ErrorSchema` */ - onChange: (newFormData: T, es?: ErrorSchema, id?: string) => any; + onChange: ( + newFormData: T | undefined, + es?: ErrorSchema, + id?: string + ) => any; /** The input blur event handler; call it with the field id and value */ onBlur: (id: string, value: any) => void; /** The input focus event handler; call it with the field id and value */ @@ -401,7 +405,7 @@ export type FieldTemplateProps< /** The `formContext` object that was passed to `Form` */ formContext?: F; /** The formData for this field */ - formData: T; + formData?: T; /** The value change event handler; Can be called with a new value to change the value for this field */ onChange: FieldProps["onChange"]; /** The key change event handler; Called when the key associated with a field is changed for an additionalProperty */ @@ -567,7 +571,7 @@ export type ArrayFieldTemplateProps< /** The `formContext` object that was passed to Form */ formContext?: F; /** The formData for this array */ - formData: T; + formData?: T; /** An array of strings listing all generated error messages from encountered errors for this widget */ rawErrors?: string[]; /** The `registry` object */ @@ -617,7 +621,7 @@ export type ObjectFieldTemplateProps< /** An object containing the id for this object & ids for its properties */ idSchema: IdSchema; /** The form data for the object */ - formData: T; + formData?: T; /** The `formContext` object that was passed to Form */ formContext?: F; /** The `registry` object */ @@ -869,7 +873,7 @@ export type CustomValidator< S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any > = ( - formData: T, + formData: T | undefined, errors: FormValidation, uiSchema?: UiSchema ) => FormValidation; @@ -937,7 +941,7 @@ export interface ValidatorType< * @param formData - The form data to validate * @param rootSchema - The root schema used to provide $ref resolutions */ - isValid(schema: S, formData: T, rootSchema: S): boolean; + isValid(schema: S, formData: T | undefined, rootSchema: S): boolean; /** Runs the pure validation of the `schema` and `formData` without any of the RJSF functionality. Provided for use * by the playground. Returns the `errors` from the validation * @@ -1032,7 +1036,7 @@ export interface SchemaUtilsType< * @returns - The index of the matched option or 0 if none is available * @deprecated */ - getMatchingOption(formData: T, options: S[]): number; + getMatchingOption(formData: T | undefined, options: S[]): number; /** Checks to see if the `schema` and `uiSchema` combination represents an array of files * * @param schema - The schema for which check for array of files flag is desired diff --git a/packages/utils/test/mergeDefaultsWithFormData.test.ts b/packages/utils/test/mergeDefaultsWithFormData.test.ts index 258e9f0d7c..9dd3d74bce 100644 --- a/packages/utils/test/mergeDefaultsWithFormData.test.ts +++ b/packages/utils/test/mergeDefaultsWithFormData.test.ts @@ -87,6 +87,6 @@ describe("mergeDefaultsWithFormData()", () => { const obj2 = { a: file, }; - expect(mergeDefaultsWithFormData(obj1, obj2).a).toBeInstanceOf(File); + expect(mergeDefaultsWithFormData(obj1, obj2)?.a).toBeInstanceOf(File); }); }); diff --git a/packages/validator-ajv6/src/validator.ts b/packages/validator-ajv6/src/validator.ts index 962ac97cd9..8e9b42fe75 100644 --- a/packages/validator-ajv6/src/validator.ts +++ b/packages/validator-ajv6/src/validator.ts @@ -350,7 +350,7 @@ export default class AJV6Validator< * @param formData- - The form data to validate * @param rootSchema - The root schema used to provide $ref resolutions */ - isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema) { + isValid(schema: RJSFSchema, formData: T | undefined, rootSchema: RJSFSchema) { try { // add the rootSchema ROOT_SCHEMA_PREFIX as id. // then rewrite the schema ref's to point to the rootSchema diff --git a/packages/validator-ajv6/test/utilsTests/getTestValidator.ts b/packages/validator-ajv6/test/utilsTests/getTestValidator.ts index e6c9b28522..b600d01010 100644 --- a/packages/validator-ajv6/test/utilsTests/getTestValidator.ts +++ b/packages/validator-ajv6/test/utilsTests/getTestValidator.ts @@ -34,7 +34,11 @@ export default function getTestValidator( ): RJSFValidationError[] { return validator.toErrorList(errorSchema, fieldPath); }, - isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema): boolean { + isValid( + schema: RJSFSchema, + formData: T | undefined, + rootSchema: RJSFSchema + ): boolean { return validator.isValid(schema, formData, rootSchema); }, rawValidation( diff --git a/packages/validator-ajv8/src/validator.ts b/packages/validator-ajv8/src/validator.ts index 873dad9834..ab635614b2 100644 --- a/packages/validator-ajv8/src/validator.ts +++ b/packages/validator-ajv8/src/validator.ts @@ -424,7 +424,7 @@ export default class AJV8Validator< * @param formData - The form data to validate * @param rootSchema - The root schema used to provide $ref resolutions */ - isValid(schema: S, formData: T, rootSchema: S) { + isValid(schema: S, formData: T | undefined, rootSchema: S) { const rootSchemaId = rootSchema["$id"] ?? ROOT_SCHEMA_PREFIX; try { // add the rootSchema ROOT_SCHEMA_PREFIX as id. diff --git a/packages/validator-ajv8/test/utilsTests/getTestValidator.ts b/packages/validator-ajv8/test/utilsTests/getTestValidator.ts index e6c9b28522..191bbb3a5e 100644 --- a/packages/validator-ajv8/test/utilsTests/getTestValidator.ts +++ b/packages/validator-ajv8/test/utilsTests/getTestValidator.ts @@ -16,7 +16,7 @@ export default function getTestValidator( const validator = customizeValidator(options); return { validateFormData( - formData: T, + formData: T | undefined, schema: RJSFSchema, customValidate?: CustomValidator, transformErrors?: ErrorTransformer @@ -34,7 +34,11 @@ export default function getTestValidator( ): RJSFValidationError[] { return validator.toErrorList(errorSchema, fieldPath); }, - isValid(schema: RJSFSchema, formData: T, rootSchema: RJSFSchema): boolean { + isValid( + schema: RJSFSchema, + formData: T | undefined, + rootSchema: RJSFSchema + ): boolean { return validator.isValid(schema, formData, rootSchema); }, rawValidation(