Skip to content

Commit

Permalink
fix: Prevent duplicate extraErrors when not live validating (#3288)
Browse files Browse the repository at this point in the history
* fix: Prevent duplicated extraErrors when not live validating
Fixed #3169 by merging `extraErrors` in while preventing duplicates
- Updated `@rjsf/utils` to make the `mergeObjects()` function support a `preventDuplicates` mode when concatenating arrays
  - Updated the tests to verify this new feature
- Updated `@rjsf/core` to use the `preventDuplicates` mode when merging `extraErrors` when not live validating
- Updated `validation.md` to remove extraneous leading space to fix #3282
- Updated `utility-functions.md` to document the new mode
- Updated `index.md` to provide the proper `core.cjs.production.min.js` package name for `unpkg.com` fixing #3262
- Updated the `CHANGELOG.md` accordingly

* - Responded to reviewer feedback

* - Fixed url for unpkg directory
  • Loading branch information
heath-freenome authored Dec 9, 2022
1 parent dc19858 commit fc6a5ae
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 11 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@ should change the heading of the (upcoming) version to include a major version b

## @rjsf/core
- Added `ref` definition to `ThemeProps` fixing [#2135](https://github.com/rjsf-team/react-jsonschema-form/issues/2135)
- Updated the `onChange` handler in `Form` to use the new `preventDuplicates` mode of `mergeObjects()` when merging `extraErrors` when live validation is off, fixing [#3169](https://github.com/rjsf-team/react-jsonschema-form/issues/3169)

## @rjsf/utils
- Updated `computedDefaults` (used by `getDefaultFormState`) to skip saving the computed default if it's an empty object unless `includeUndefinedValues` is truthy, fixing [#2150](https://github.com/rjsf-team/react-jsonschema-form/issues/2150) and [#2708](https://github.com/rjsf-team/react-jsonschema-form/issues/2708)
- Expanded the `getDefaultFormState` util's `includeUndefinedValues` prop to accept a boolean or `"excludeObjectChildren"` if it does not want to include undefined values in nested objects
- Updated `mergeObjects` to add new `preventDuplicates` mode when concatenating arrays so that only unique values from the source object array are copied to the destination object array

## Dev / docs / playground
- Removed extraneous leading space on the examples in the validation documentation, fixing [#3282](https://github.com/rjsf-team/react-jsonschema-form/issues/3282)
- Updated the documentation for `mergeObjects()` for the new `preventDuplicates` mode of concatenating arrays
- Updated the documentation for unpkg releases to the correct name fixing the confusion found in [#3262](https://github.com/rjsf-team/react-jsonschema-form/issues/3262)

# 5.0.0-beta.13

Expand Down
4 changes: 2 additions & 2 deletions docs/api-reference/utility-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,14 @@ Recursively merge deeply nested objects.
#### Parameters
- obj1: GenericObjectType - The first object to merge
- obj2: GenericObjectType - The second object to merge
- [concatArrays=false]: boolean - Optional flag that, when true, will cause arrays to be concatenated
- [concatArrays=false]: boolean | "preventDuplicates" - Optional flag that, when true, will cause arrays to be concatenated. Use "preventDuplicates" to merge arrays in a manner that prevents any duplicate entries from being merged.

#### Returns
@returns - A new object that is the merge of the two given objects

### mergeSchemas()
Recursively merge deeply nested schemas.
The difference between mergeSchemas and mergeObjects is that mergeSchemas only concats arrays for values under the 'required' keyword, and when it does, it doesn't include duplicate values.
The difference between mergeSchemas and mergeObjects is that mergeSchemas only concats arrays for values under the 'required' keyword, and when it does, it doesn't include duplicate values. NOTE: Uses shallow comparison for the duplicate checking.

#### Parameters
- obj1: GenericObjectType - The first object to merge
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ Our latest version requires React 16+. You can also install `react-jsonschema-fo
### As a script served from a CDN

```html
<script src="https://unpkg.com/@rjsf/core/dist/react-jsonschema-form.js"></script>
<script src="https://unpkg.com/@rjsf/core/dist/core.cjs.production.min.js"></script>
```

Source maps are available at [this url](https://unpkg.com/@rjsf/core/dist/react-jsonschema-form.js.map).
Source maps are available at [this url](https://unpkg.com/@rjsf/core/dist/core.cjs.production.min.js.map).

> Note: The CDN version **does not** embed `react` or `react-dom`.
> Note: The CDN version **does not** embed `react` or `react-dom`. If you want other distributions (i.e. umd, esm), look [here](https://unpkg.com/@rjsf/core/dist/) for all releases

You'll also need to alias the default export property to use the Form component:
Expand Down
4 changes: 2 additions & 2 deletions docs/usage/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ NOTE: The `ajv-i18n` validators implement the `Localizer` interface.

Using a specific locale while including all of `ajv-i18n`:

```tsx
```tsx
import { RJSFSchema } from "@rjsf/utils";
import { customizeValidator } from '@rjsf/validator-ajv8';
import localizer from "ajv-i18n";
Expand All @@ -514,7 +514,7 @@ render((

Using a specific locale minimizing the bundle size

```tsx
```tsx
import { RJSFSchema } from "@rjsf/utils";
import { customizeValidator } from '@rjsf/validator-ajv8';
import spanishLocalizer from "ajv-i18n/localize/es";
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,11 @@ export default class Form<
};
} else if (!noValidate && newErrorSchema) {
const errorSchema = extraErrors
? (mergeObjects(newErrorSchema, extraErrors, true) as ErrorSchema<T>)
? (mergeObjects(
newErrorSchema,
extraErrors,
"preventDuplicates"
) as ErrorSchema<T>)
: newErrorSchema;
state = {
formData: newFormData,
Expand Down
17 changes: 14 additions & 3 deletions packages/utils/src/mergeObjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,32 @@ import { GenericObjectType } from "./types";
*
* @param obj1 - The first object to merge
* @param obj2 - The second object to merge
* @param [concatArrays=false] - Optional flag that, when true, will cause arrays to be concatenated
* @param [concatArrays=false] - Optional flag that, when true, will cause arrays to be concatenated. Use
* "preventDuplicates" to merge arrays in a manner that prevents any duplicate entries from being merged.
* NOTE: Uses shallow comparison for the duplicate checking.
* @returns - A new object that is the merge of the two given objects
*/
export default function mergeObjects(
obj1: GenericObjectType,
obj2: GenericObjectType,
concatArrays = false
concatArrays: boolean | "preventDuplicates" = false
) {
return Object.keys(obj2).reduce((acc, key) => {
const left = obj1 ? obj1[key] : {},
right = obj2[key];
if (obj1 && key in obj1 && isObject(right)) {
acc[key] = mergeObjects(left, right, concatArrays);
} else if (concatArrays && Array.isArray(left) && Array.isArray(right)) {
acc[key] = left.concat(right);
let toMerge = right;
if (concatArrays === "preventDuplicates") {
toMerge = right.reduce((result, value) => {
if (!left.includes(value)) {
result.push(value);
}
return result;
}, []);
}
acc[key] = left.concat(toMerge);
} else {
acc[key] = right;
}
Expand Down
18 changes: 18 additions & 0 deletions packages/utils/test/mergeObjects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,23 @@ describe("mergeObjects()", () => {
a: { b: [1, 2] },
});
});

it("should not concat duplicate values in arrays when concatArrays is 'preventDuplicates'", () => {
const obj1 = { a: [1] };
const obj2 = { a: [1, 2] };

expect(mergeObjects(obj1, obj2, "preventDuplicates")).toEqual({
a: [1, 2],
});
});

it("should not concat duplicate values in nested arrays when concatArrays is 'preventDuplicates'", () => {
const obj1 = { a: { b: [1] } };
const obj2 = { a: { b: [1, 2] } };

expect(mergeObjects(obj1, obj2, "preventDuplicates")).toEqual({
a: { b: [1, 2] },
});
});
});
});

0 comments on commit fc6a5ae

Please sign in to comment.