Skip to content

Commit

Permalink
fix: #3239 by providing an ErrorSchemaBuilder class in @rjsf/utils (#…
Browse files Browse the repository at this point in the history
…3307)

fix: #3239 by providing a new `ErrorSchemaBuilder` class in `@rjsf/utils`
- In `@rjsf/utils` added `ErrorSchemaBuilder` to facilitate building `ErrorSchema` objects without the need for fancy casting
  - Exported the new class as part of the main `index.js`
  - Added 100% unit tests
- In `@rjsf/validator-ajv6` and `@rjsf/validator-ajv8` updated the `toErrorSchema()` function to use the `ErrorSchemaBuilder` to simplify the implementation
  - Also updated the tests to use the `ErrorSchemaBuilder` to replace the expected values that required doing `as ErrorSchema` casting
- Updated the `utility-functions.md` file to document `ErrorSchemaBuilder`
- Updated the `CHANGELOG.md` accordingly for this fix as well as PR #3297
  • Loading branch information
heath-freenome committed Dec 22, 2022
1 parent 8ebbb42 commit e35f61a
Show file tree
Hide file tree
Showing 9 changed files with 548 additions and 99 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Expand Up @@ -21,12 +21,26 @@ should change the heading of the (upcoming) version to include a major version b
- Pass the `schema` along to the `ArrayFieldItemTemplate` as part of the fix for [#3253](https://github.com/rjsf-team/react-jsonschema-form/issues/3253)
- Tweak Babel configuration to emit ES5-compatible output files, fixing [#3240](https://github.com/rjsf-team/react-jsonschema-form/issues/3240)

## @rjsf/material-ui
- Reverse the condition used in the `onChange` handler in the `RangeWidget`, fixing [#2161](https://github.com/rjsf-team/react-jsonschema-form/issues/2161)

## @rjsf/mui
- Reverse the condition used in the `onChange` handler in the `RangeWidget`, fixing [#2161](https://github.com/rjsf-team/react-jsonschema-form/issues/2161)

## @rjsf/utils
- Update the `ArrayFieldItemTemplate` to add `schema` as part of the fix for [#3253](https://github.com/rjsf-team/react-jsonschema-form/issues/3253)
- Fix improper merging of nested `allOf`s ([#3025](https://github.com/rjsf-team/react-jsonschema-form/pull/3025), [#3227](https://github.com/rjsf-team/react-jsonschema-form/pull/3227)), fixing [#2923](https://github.com/rjsf-team/react-jsonschema-form/pull/2929)
- Added a new `ErrorSchemaBuilder` class to enable building a proper `ErrorSchema` without crazy castings, fixing [#3239](https://github.com/rjsf-team/react-jsonschema-form/issues/3239)

## @rjsf/validator-ajv6
- Updated the validator to use the `ErrorSchemaBuilder` in the `toErrorSchema()` function to simplify the implementation

## @rjsf/validator-ajv8
- Updated the validator to use the `ErrorSchemaBuilder` in the `toErrorSchema()` function to simplify the implementation

## Dev / docs / playground
- Fixed the documentation for `ArrayFieldItemTemplate` as part of the fix for [#3253](https://github.com/rjsf-team/react-jsonschema-form/issues/3253)
- Added documentation for `ErrorSchemaBuilder` in the `utility-functions.md`, fixing [#3239](https://github.com/rjsf-team/react-jsonschema-form/issues/3239)

# 5.0.0-beta.14

Expand Down
70 changes: 70 additions & 0 deletions docs/api-reference/utility-functions.md
Expand Up @@ -544,3 +544,73 @@ The resulting interface implementation will forward the `validator` and `rootSch

#### Returns
- SchemaUtilsType<T, S, F> - An implementation of a `SchemaUtilsType` interface

## ErrorSchema builder class

### ErrorSchemaBuilder<T = any>(initialSchema?: ErrorSchema<T>) constructor
The `ErrorSchemaBuilder<T>` is used to build an `ErrorSchema<T>` since the definition of the `ErrorSchema` type is designed for reading information rather than writing it.
Use this class to add, replace or clear errors in an error schema by using either dotted path or an array of path names.
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<T> - The optional set of initial errors, that will be cloned into the class

#### Returns
- ErrorSchemaBuilder<T> - The instance of the `ErrorSchemaBuilder` class

### ErrorSchema getter function
Returns the `ErrorSchema` that has been updated by the methods of the `ErrorSchemaBuilder`

Usage:

```ts
import { ErrorSchemaBuilder, ErrorSchema } from "@rjsf/utils";

const builder = new ErrorSchemaBuilder();

// Do some work using the builder
...

const errorSchema: ErrorSchema = builder.ErrorSchema;
```

### resetAllErrors()
Resets all errors in the `ErrorSchemaBuilder` back to the `initialSchema` if provided, otherwise an empty set.

#### Parameters
- [initialSchema]: ErrorSchema<T> - The optional set of initial errors, that will be cloned into the class

#### Returns
- ErrorSchemaBuilder<T> - The instance of the `ErrorSchemaBuilder` class

### addErrors()
Adds the `errorOrList` to the list of errors in the `ErrorSchema` at either the root level or the location within the schema described by the `pathOfError`.
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
- 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)

#### Returns
- ErrorSchemaBuilder<T> - The instance of the `ErrorSchemaBuilder` class

### setErrors()
Sets/replaces the `errorOrList` as the error(s) in the `ErrorSchema` at either the root level or the location within the schema described by the `pathOfError`.
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
- 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)

#### Returns
- ErrorSchemaBuilder<T> - The instance of the `ErrorSchemaBuilder` class

### clearErrors()
Clears the error(s) in the `ErrorSchema` at either the root level or the location within the schema described by the `pathOfError`.
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)

#### Returns
- ErrorSchemaBuilder<T> - The instance of the `ErrorSchemaBuilder` class
118 changes: 118 additions & 0 deletions packages/utils/src/ErrorSchemaBuilder.ts
@@ -0,0 +1,118 @@
import cloneDeep from "lodash/cloneDeep";
import get from "lodash/get";
import set from "lodash/set";

import { ErrorSchema } from "./types";
import { ERRORS_KEY } from "./constants";

/** The `ErrorSchemaBuilder<T>` is used to build an `ErrorSchema<T>` since the definition of the `ErrorSchema` type is
* designed for reading information rather than writing it. Use this class to add, replace or clear errors in an error
* schema by using either dotted path or an array of path names. 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.
*/
export default class ErrorSchemaBuilder<T = any> {
/** The error schema being built
*
* @private
*/
private errorSchema: ErrorSchema<T> = {};

/** Construct an `ErrorSchemaBuilder` with an optional initial set of errors in an `ErrorSchema`.
*
* @param [initialSchema] - The optional set of initial errors, that will be cloned into the class
*/
constructor(initialSchema?: ErrorSchema<T>) {
this.resetAllErrors(initialSchema);
}

/** Returns the `ErrorSchema` that has been updated by the methods of the `ErrorSchemaBuilder`
*/
get ErrorSchema() {
return this.errorSchema;
}

/** Will get an existing `ErrorSchema` at the specified `pathOfError` or create and return one.
*
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to add the error(s)
* @returns - The error block for the given `pathOfError` or the root if not provided
* @private
*/
private getOrCreateErrorBlock(pathOfError?: string | string[]) {
const hasPath =
(Array.isArray(pathOfError) && pathOfError.length > 0) ||
typeof pathOfError === "string";
let errorBlock: ErrorSchema = hasPath
? get(this.errorSchema, pathOfError)
: this.errorSchema;
if (!errorBlock && pathOfError) {
errorBlock = {};
set(this.errorSchema, pathOfError, errorBlock);
}
return errorBlock;
}

/** Resets all errors in the `ErrorSchemaBuilder` back to the `initialSchema` if provided, otherwise an empty set.
*
* @param [initialSchema] - The optional set of initial errors, that will be cloned into the class
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
resetAllErrors(initialSchema?: ErrorSchema<T>) {
this.errorSchema = initialSchema ? cloneDeep(initialSchema) : {};
return this;
}

/** Adds the `errorOrList` to the list of errors in the `ErrorSchema` at either the root level or the location within
* the schema described by the `pathOfError`. 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).
*
* @param errorOrList - The error or list of errors to add into the `ErrorSchema`
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to add the error(s)
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
addErrors(errorOrList: string | string[], pathOfError?: string | string[]) {
const errorBlock: ErrorSchema = this.getOrCreateErrorBlock(pathOfError);
let errorsList = get(errorBlock, ERRORS_KEY);
if (!Array.isArray(errorsList)) {
errorsList = [];
errorBlock[ERRORS_KEY] = errorsList;
}

if (Array.isArray(errorOrList)) {
errorsList.push(...errorOrList);
} else {
errorsList.push(errorOrList);
}
return this;
}

/** Sets/replaces the `errorOrList` as the error(s) in the `ErrorSchema` at either the root level or the location
* within the schema described by the `pathOfError`. 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).
*
* @param errorOrList - The error or list of errors to set into the `ErrorSchema`
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to set the error(s)
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
setErrors(errorOrList: string | string[], pathOfError?: string | string[]) {
const errorBlock: ErrorSchema = this.getOrCreateErrorBlock(pathOfError);
// Effectively clone the array being given to prevent accidental outside manipulation of the given list
const listToAdd = Array.isArray(errorOrList)
? [...errorOrList]
: [errorOrList];
set(errorBlock, ERRORS_KEY, listToAdd);
return this;
}

/** Clears the error(s) in the `ErrorSchema` at either the root level or the location within the schema described by
* the `pathOfError`. 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).
*
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to clear the error(s)
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
clearErrors(pathOfError?: string | string[]) {
const errorBlock: ErrorSchema = this.getOrCreateErrorBlock(pathOfError);
set(errorBlock, ERRORS_KEY, []);
return this;
}
}
2 changes: 2 additions & 0 deletions packages/utils/src/index.ts
Expand Up @@ -4,6 +4,7 @@ import canExpand from "./canExpand";
import createSchemaUtils from "./createSchemaUtils";
import dataURItoBlob from "./dataURItoBlob";
import deepEquals from "./deepEquals";
import ErrorSchemaBuilder from "./ErrorSchemaBuilder";
import findSchemaDefinition from "./findSchemaDefinition";
import getInputProps from "./getInputProps";
import getSchemaType from "./getSchemaType";
Expand Down Expand Up @@ -45,6 +46,7 @@ export {
createSchemaUtils,
dataURItoBlob,
deepEquals,
ErrorSchemaBuilder,
findSchemaDefinition,
getInputProps,
getSchemaType,
Expand Down

0 comments on commit e35f61a

Please sign in to comment.