Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: #3239 by providing an ErrorSchemaBuilder class in @rjsf/utils #3307

Merged
merged 1 commit into from Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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