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: Prevent duplicate extraErrors when not live validating #3288

Merged
merged 3 commits into from Dec 9, 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
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
4 changes: 2 additions & 2 deletions docs/api-reference/utility-functions.md
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
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
17 changes: 14 additions & 3 deletions packages/utils/src/mergeObjects.ts
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)) {
heath-freenome marked this conversation as resolved.
Show resolved Hide resolved
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] },
});
});
});
});