Skip to content

Commit

Permalink
Fixed #1596 by adapting the fix from #2002 into rjsf v5 (#3047)
Browse files Browse the repository at this point in the history
* Fixed #1596 by adapting the fix from #2002 into rjsf v5
- Added a new `mergeValidationData()` method in `@rjsf/utils` to handle the appending of errors onto the end of the validationData from an additional error schema
  - Added this to the `schema` directory `index.ts` along with exposing it on the `SchemaUtils` type and implementation
  - Also fixed the type of `toErrorList()` in `ValidatorType` to change from `fieldName: string` to `fieldPath: string[]`
  - Added reusable `mergeValidationDataTest.ts`, calling it in the utils
- Update the `@rjsf/validator-ajv6` to pick up the breaking change from #2002 around `AJV6Validator.toErrorList()`
  - Also modified the `validateFormData()` function to return the result of `mergeValidationData()` when the user has a custom validator
  - Updated tests for the new structure of the `toErrorList()` data
  - Also updated the `schema.tests` to add the new `mergeValidationDataTest()`
- Updated `Form` to use the `mergeValidationData()` function in the few places where `extraErrors` was being merged into the schema validation
  - Updated tests for the new structor of the `toErrorList()` data
- Updated the `CHANGELOG.md` to describe this fix
- Updated the `5.x upgrade guide.md` to describe all the new utility functions added and describe util.js and validator.js breaking changes
- Updated the `validation.md` documentation for the `ErrorListTemplate` change along with making the `RJSFValidationError` interface describe the optional properties

* - Responded to reviewer feedback... also, removed the `:` after the property in the `stack` to match AJV stack, adding `message` to also match AJV
- Added migration guide changes
  • Loading branch information
heath-freenome authored Aug 26, 2022
1 parent e3edba7 commit 5f1bd8a
Show file tree
Hide file tree
Showing 19 changed files with 367 additions and 82 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@ should change the heading of the (upcoming) version to include a major version b
## @rjsf/utils
- clear errors on formData change when liveOmit=true when "additionalProperties: false" [issue 1507](https://github.com/rjsf-team/react-jsonschema-form/issues/1507) (https://github.com/rjsf-team/react-jsonschema-form/pull/2631)

## @rjsf/validator-ajv6
- A **BREAKING CHANGE** to `toErrorList()` was made so that it takes `fieldPath: string[]` rather than `fieldName='root'` as part of the fix to (https://github.com/rjsf-team/react-jsonschema-form/issues/1596)
- The returned `errors` also now adds `property` from the `fieldPath` along with the proper path from the `property` to the `stack` message, making it consistent with the AJV errors.
- Previously the `stack` attribute would say `root: error message`; now it says `. error message`
- In addition, the extra information provided by AJV is no longer lost from the `errors` when merged with custom validation, fixing (https://github.com/rjsf-team/react-jsonschema-form/issues/1596).

## @rjsf/core
- **BREAKING CHANGE** Fix overriding core submit button className (https://github.com/rjsf-team/react-jsonschema-form/issues/2979)
- Fix `ui:field` with anyOf or oneOf no longer rendered twice (#2890)
- **BREAKING CHANGE** Fixed `anyOf` and `oneOf` getting incorrect, potentially duplicate ids when combined with array (https://github.com/rjsf-team/react-jsonschema-form/issues/2197)
- `formContext` is now passed properly to `SchemaField`, fixes (https://github.com/rjsf-team/react-jsonschema-form/issues/2394, https://github.com/rjsf-team/react-jsonschema-form/issues/2274)
- Added `ui:duplicateKeySuffixSeparator` to customize how duplicate object keys are renamed when using `additionalProperties`.
- The `extraErrors` are now consistently appended onto the end of the schema validation-based `errors` information that is returned via the `onErrors()` callback when submit fails.
- In addition, the extra information provided by AJV is no longer stripped from the `errors` during the merge process, fixing (https://github.com/rjsf-team/react-jsonschema-form/issues/1596).

## @rjsf/antd
- Fix esm build to use `@rollup/plugin-replace` to replace `antd/lib` and `rc-picker/lib` with `antd/es` and `rc-picker/es` respectively, fixing (https://github.com/rjsf-team/react-jsonschema-form/issues/2962)
Expand Down
94 changes: 91 additions & 3 deletions docs/5.x upgrade guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Unfortunately, there is required work pending to properly support React 18, so u
### New packages

There are three new packages added in RJSF version 5:
- `@rjsf/utils`: All of the [utility functions](https://react-jsonschema-form.readthedocs.io/en/stable/api-reference/utiltity-functions) previously imported from `@rjsf/core/utils` as well as the Typescript types for RJSV version 5.
- `@rjsf/utils`: All of the [utility functions](https://react-jsonschema-form.readthedocs.io/en/stable/api-reference/utiltity-functions) previously imported from `@rjsf/core/utils` as well as the Typescript types for RJSF version 5.
- The following new utility functions were added: `createSchemaUtils()`, `getInputProps()`, `mergeValidationData()` and `processSelectValue()`
- `@rjsf/validator-ajv6`: The [ajv](https://github.com/ajv-validator/ajv)-v6-based validator refactored out of `@rjsf/[email protected]`, that implements the `ValidatorType` interface defined in `@rjsf/utils`.
- `@rjsf/mui`: Previously `@rjsf/material-ui/v5`, now provided as its own theme.

Expand Down Expand Up @@ -86,10 +87,10 @@ render((
), document.getElementById("app"));
```

#### validate renamed
##### validate renamed
In version 5, the `validate` prop on `Form` was renamed to `customValidate` to avoid confusion with the new `validator` prop.

#### utils
#### utils.js
In version 5, all the utility functions that were previously accessed via `import { utils } from '@rjsf/core';` are now available via `import utils from '@rjsf/utils';`.
Because of the decoupling of validation from `@rjsf/core` there is a breaking change for all the [validator-based utility functions](https://react-jsonschema-form.readthedocs.io/en/stable/api-reference/utiltity-functions#validator-based-utility-functions), since they now require an additional `ValidatorType` parameter.

Expand Down Expand Up @@ -130,6 +131,93 @@ function YourWidget(props: WidgetProps) {
}
```

#### validator.js
Because of the decoupling of validation from `@rjsf/core` this file was refactored into its own `@rjsf/validator-ajv` package.
During that refactor a few **breaking changes** were made to how it works related to custom validation and `ErrorSchema` conversion.

##### toErrorList param changed
In previous versions, the `toErrorList()` function used to take a `fieldName` string defaulted to `root`, and use it to format the `stack` message.
In version 5, `fieldName` was changed to `fieldPath` string array defaulted to an empty array, and is used to recursively add the field name to the errors as the `property` object along with the raw `message`.
The result is that if you had an `ErrorSchema` that looks like:

```tsx
const errorSchema: ErrorSchema = {
__error: [ "error message 1"],
password: { __error: "passwords do not match" }
}
```

The returned result from calling `toErrorList(errorSchema)` has changed as follows:

```tsx
// version 4 result
[
{ stack: "root: error message 1" },
{ stack: "password: passwords do not match" }
]

// version 5 result
[
{ property: ".", message: "error message 1", stack: ". error message 1" },
{ property: ".password", message: "passwords do not match", stack: ".password passwords do not match" }
]
```

##### Custom validation and extraErrors
In previous versions, when using a custom validator on the `Form`, any errors that were generated were inconsistently inserted into the validations `errors` list.
In addition, there was an [issue](https://github.com/rjsf-team/react-jsonschema-form/issues/1596) with the additional AJV error information besides the `stack` being lost when custom validation generated errors, which has been fixed.
Also, when `extraErrors` were provided, they were being inconsistently inserted into the `errors` list and the additional AJV error information besides the `stack` was also lost.
In version 5, all of these errors will be consistently appended onto the end of the validation `errors` list, and the additional AJV error information is maintained.

In other words, if custom validation or `extraErrors` produced the following `ErrorSchema`:

```tsx
{
__error: [ "Please correct your password"],
password2: { __error: "passwords do not match" }
}
```

And the AJV validation produced the following `ErrorSchema`:

```tsx
{
__error: [{
message: "should NOT be shorter than 3 characters",
name: "minLength",
params: { limit: 3 },
property: ".password2",
schemaPath: "#/properties/password2/minLength",
stack: ".password2 should NOT be shorter than 3 characters",
}]
}
```

The resulting `errors` list has changed as follows:

```tsx
// version 4
[
{ stack: "root: Please correct your password" },
{ stack: "password2: passwords do not match" },
{ stack: "password2: should NOT be shorter than 3 characters" }
]

// version 5
[
{
message: "should NOT be shorter than 3 characters",
name: "minLength",
params: { limit: 3 },
property: ".password2",
schemaPath: "#/properties/password2/minLength",
stack: ".password2 should NOT be shorter than 3 characters",
},
{ property: ".", message: "Please correct your password", stack: ". Please correct your password" },
{ property: ".", message: "passwords do not match", stack: ".password2 passwords do not match" }
]
```

#### Generate correct ids when arrays are combined with `anyOf`/`oneOf`

In v4, with arrays inside `anyOf` or `oneOf`, the parent name was not used to generate ids.
Expand Down
32 changes: 22 additions & 10 deletions docs/api-reference/utility-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ Converts a UTC date string into a local Date format
Returns the superset of `formData` that includes the given set updated to include any missing fields that have computed to have defaults provided in the `schema`.

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- theSchema: RJSFSchema - The schema for which the default state is desired
- [formData]: T - The current formData, if any, onto which to provide any missing defaults
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s
Expand All @@ -398,7 +398,7 @@ Returns the superset of `formData` that includes the given set updated to includ
Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema` should be displayed in a UI.

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- schema: RJSFSchema - The schema for which the display label flag is desired
- [uiSchema={}]: UiSchema<T, F> - The UI schema from which to derive potentially displayable information
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s
Expand All @@ -410,7 +410,7 @@ Determines whether the combination of `schema` and `uiSchema` properties indicat
Given the `formData` and list of `options`, attempts to find the index of the option that best matches the data.

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary
- validator: ValidatorType<T> - 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
- options: RJSFSchema[] - The list of options to find a matching options from
- rootSchema: RJSFSchema - The root schema, used to primarily to look up `$ref`s
Expand All @@ -422,7 +422,7 @@ Given the `formData` and list of `options`, attempts to find the index of the op
Checks to see if the `schema` and `uiSchema` combination represents an array of files

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- schema: RJSFSchema - The schema for which check for array of files flag is desired
- [uiSchema={}]: UiSchema<T, F> - The UI schema from which to check the widget
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s
Expand All @@ -434,7 +434,7 @@ Checks to see if the `schema` and `uiSchema` combination represents an array of
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
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- schema: RJSFSchema - The schema for which check for a multi-select flag is desired
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s

Expand All @@ -445,20 +445,32 @@ Checks to see if the `schema` combination represents a multi-select
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
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- theSchema: RJSFSchema - The schema for which check for a select flag is desired
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s

#### Returns
- boolean: True if schema contains a select, otherwise false

### mergeValidationData<T=any>()
Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in the two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling `validator.toErrorList()` onto the `errors` in the `validationData`.
If no `additionalErrorSchema` is passed, then `validationData` is returned.

#### Parameters
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used to convert an ErrorSchema to a list of errors
- validationData: ValidationData<T> - The current `ValidationData` into which to merge the additional errors
- [additionalErrorSchema]: ErrorSchema<T> - The additional set of errors in an `ErrorSchema`

#### Returns
- ValidationData<T>: The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided.

### retrieveSchema<T = any>()
Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
potentially recursive resolution.

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be forwarded to all the APIs
- schema: RJSFSchema - The schema for which retrieving a schema is desired
- [rootSchema={}]: RJSFSchema - The root schema that will be forwarded to all the APIs
- [rawFormData]: T - The current formData, if any, to assist retrieving a schema
Expand All @@ -470,7 +482,7 @@ potentially recursive resolution.
Generates an `IdSchema` object for the `schema`, recursively

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- schema: RJSFSchema - The schema for which the `IdSchema` is desired
- [id]: string | null - The base id for the schema
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s
Expand All @@ -485,7 +497,7 @@ Generates an `IdSchema` object for the `schema`, recursively
Generates an `PathSchema` object for the `schema`, recursively

#### Parameters
- validator: ValidatorType - An implementation of the `ValidatorType` interface that will be used when necessary
- validator: ValidatorType<T> - An implementation of the `ValidatorType` interface that will be used when necessary
- schema: RJSFSchema - The schema for which the `PathSchema` is desired
- [name='']: string - The base name for the schema
- [rootSchema]: RJSFSchema - The root schema, used to primarily to look up `$ref`s
Expand All @@ -501,7 +513,7 @@ Creates a `SchemaUtilsType` interface that is based around the given `validator`
The resulting interface implementation will forward the `validator` and `rootSchema` to all the wrapped APIs.

#### Parameters
- validator: ValidatorType - an implementation of the `ValidatorType` interface that will be forwarded to all the APIs
- validator: ValidatorType<T> - an implementation of the `ValidatorType` interface that will be forwarded to all the APIs
- rootSchema: RJSFSchema - The root schema that will be forwarded to all the APIs

#### Returns
Expand Down
15 changes: 7 additions & 8 deletions docs/usage/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,19 @@ render((
Each element in the `errors` list passed to `transformErrors` is a `RJSFValidationError` interface (in `@rjsf/utils`) and has the following properties:

- `name`: name of the error, for example, "required" or "minLength"
- `message`: message, for example, "is a required property" or "should NOT be shorter than 3 characters"
- `params`: an object with the error params returned by ajv ([see doc](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#error-parameters) for more info).
- `property`: a string in Javascript property accessor notation to the data path of the field with the error. For example, `.name` or `['first-name']`.
- `schemaPath`: JSON pointer to the schema of the keyword that failed validation. For example, `#/fields/firstName/required`. (Note: this may sometimes be wrong due to a [bug in ajv](https://github.com/ajv-validator/ajv/issues/512)).
- `name`: optional name of the error, for example, "required" or "minLength"
- `message`: optional message, for example, "is a required property" or "should NOT be shorter than 3 characters"
- `params`: optional object with the error params returned by ajv ([see doc](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#error-parameters) for more info).
- `property`: optional string in Javascript property accessor notation to the data path of the field with the error. For example, `.name` or `.first-name`.
- `schemaPath`: optional JSON pointer to the schema of the keyword that failed validation. For example, `#/fields/firstName/required`. (Note: this may sometimes be wrong due to a [bug in ajv](https://github.com/ajv-validator/ajv/issues/512)).
- `stack`: full error name, for example ".name is a required property".

## Error List Display

To take control over how the form errors are displayed, you can define an *error list template* for your form.
This list is the form global error list that appears at the top of your forms.

An error list template is basically a React stateless component being passed errors as props so you can render them as you like:
An error list template is basically a React stateless component being passed errors as props, so you can render them as you like:

```tsx
import validator from "@rjsf/validator-ajv6";
Expand Down Expand Up @@ -167,7 +167,7 @@ render((
showErrorList={true}
formData={""}
liveValidate
ErrorList={ErrorListTemplate} />
templates: {{ ErrorListTemplate }} />
), document.getElementById("app"));
```

Expand All @@ -181,7 +181,6 @@ The following props are passed to `ErrorList` as defined by the `ErrorListProps`
- `uiSchema`: The uiSchema that was passed to `Form`.
- `formContext`: The `formContext` object that you passed to `Form`.


## The case of empty strings

When a text input is empty, the field in form data is set to `undefined`. String fields that use `enum` and a `select` widget will have an empty option at the top of the options list that when selected will result in the field being `undefined`.
Expand Down
Loading

0 comments on commit 5f1bd8a

Please sign in to comment.