Skip to content

Commit

Permalink
fix: Prevent duplicated extraErrors when not live validating
Browse files Browse the repository at this point in the history
Fixed rjsf-team#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 rjsf-team#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 rjsf-team#3262
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome committed Dec 8, 2022
1 parent dc19858 commit 0abca05
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
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
2 changes: 1 addition & 1 deletion docs/api-reference/utility-functions.md
Expand Up @@ -257,7 +257,7 @@ 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
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
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
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
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
16 changes: 13 additions & 3 deletions packages/utils/src/mergeObjects.ts
Expand Up @@ -5,21 +5,31 @@ 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.
* @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
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 0abca05

Please sign in to comment.