-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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: fixed many issues in oneOf/anyOf functions #3392
fix: fixed many issues in oneOf/anyOf functions #3392
Conversation
docs/5.x upgrade guide.md
Outdated
@@ -231,6 +231,7 @@ render(( | |||
In version 5, all the utility functions that were previously accessed via `import { utils } from '@rjsf/core';` are now available via `import utils from '@rjsf/utils';`. | |||
Because of the decoupling of validation from `@rjsf/core` there is a breaking change for all the [validator-based utility functions](https://react-jsonschema-form.readthedocs.io/en/stable/api-reference/utiltity-functions#validator-based-utility-functions), since they now require an additional `ValidatorType` parameter. | |||
More over, one previously exported function `resolveSchema()` is no longer exposed in the `@rjsf/utils`, so use `retrieveSchema()` instead. | |||
Finally, one previously exported function `getMatchingOption()` has been deprecated in favor of `getFirstMatchingOption()`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for renaming it to getFirstMatchingOption
? I think getMatchingOption
is pretty clear ("option" not "options") and not sure if "first" is worth the rename? of course there's some ambiguity as to whether it would be the first, last, or an arbitrary one, but I think first could be a sane and intuitive default. If it really got the last matching option instead, for example, then that would be a stronger case for renaming the function to clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the getMatchingOption()
function logic loops over all of the options in a list and return the first option that is considered valid for a schema. This means that it stops looking for any other options. Sometimes there is actually an option that has a closer match to the formData that the first one. Which is why I added the getClosestMatchingOption()
function. The rename better suggests the behaviour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i have a big problem right now where i have anyOf
where two schemas have an intersecting key. if my form is empty except that common key, sometimes, the form just changes views, which is extremely disruptive.
that is:
{ type: "object", properties: { "foo": ..., ...A } }
{ type: "object", properties: { "foo": ..., ...B } }
...the form can toggle on edit, even if the anyOf
option <select />
was not touched. hopefully this changeset will make it clear that if a current subschema is selected, it should not change the active view
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cdaringe I plan on cutting beta-18 with this change within a day. Let me know if does not fix it, but from what you shared I believe it would work properly. What is this key's type? (i.e. what is A
and B
defined to be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @heath-freenome , thanks! i'll install the beta as soon as it's out and test if it's fixed. A & B are objects of other properties that are non-insersecting. in the above, i was poorly trying to communicate that that I have anyOf
with 2 schemas, which share the field foo
but share no other field names.
cf1ef2b
to
a5a2dc5
Compare
@@ -156,7 +156,7 @@ export function computeDefaults< | |||
) as T[]; | |||
} else if (ONE_OF_KEY in schema) { | |||
schema = schema.oneOf![ | |||
getMatchingOption<T, S, F>( | |||
getFirstMatchingOption<T, S, F>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nickgros I'm wondering if, because of value defaulting, we actually want to use getClosestMatchingOption()
here instead?? Is the slower performance for the best choice worth it? I'm on the fence but leaning towards best vs fast
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think that this is so useful that I think getClosestMatchingOption
should be the default behavior.
Couple of thoughts:
- If users are relying on the existing behavior, we'll need an opt-in for the old behavior
- If the main performance hit is from calculating scores of complex schemas, we'll probably need an opt-out.
- If the main performance hit is because we make multiple validation checks by repeatedly calling
getFirstMatchingOption
, then let's see if we can optimize that (e.g. use the Ajv cache)
We can wait and see what feedback we get from users if we're not sure if any of these will be real problems.
options: S[], | ||
rootSchema: S | ||
): number { | ||
return getMatchingOption<T, S, F>(validator, formData, options, rootSchema); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer this fn to contain the implementation and getMatchingOption
calls getFirstMatchingOption
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went back and forth on this and decided to keep the choice that would simply maintaining the test coverage and the overall diff. Switching it meant duplicating the tests for getMatchingOption()
. The way it is now, I was able to rename those tests to be getFirstMatchingOptionTests
.
* @returns - The new form data, with all the fields uniquely associated with the old schema set | ||
* to `undefined`. Will return `undefined` if the new schema is not an object containing properties. | ||
*/ | ||
export default function sanitizeDataForNewSchema< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something we should think about: what makes this different from omitExtraData
? If the behavior should be the same, maybe we can use this instead of whatever the current logic is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this is different than omitExtraData
in that when we change from one schema to another we are trying to remove anything that doesn't match the old schema AND keep anything that does. The old implementation just removed things, especially objects since the nesting of data wasn't checked. Plus omitExtraData
is only for when submitting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is different as omitExtraData
only happens on submit
// any non-matching value lower | ||
newScore += formValue === value.const ? 1 : -1; | ||
} | ||
// TODO eventually, deal with enums/arrays |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with handling this case later, too.
@@ -156,7 +156,7 @@ export function computeDefaults< | |||
) as T[]; | |||
} else if (ONE_OF_KEY in schema) { | |||
schema = schema.oneOf![ | |||
getMatchingOption<T, S, F>( | |||
getFirstMatchingOption<T, S, F>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think that this is so useful that I think getClosestMatchingOption
should be the default behavior.
Couple of thoughts:
- If users are relying on the existing behavior, we'll need an opt-in for the old behavior
- If the main performance hit is from calculating scores of complex schemas, we'll probably need an opt-out.
- If the main performance hit is because we make multiple validation checks by repeatedly calling
getFirstMatchingOption
, then let's see if we can optimize that (e.g. use the Ajv cache)
We can wait and see what feedback we get from users if we're not sure if any of these will be real problems.
(newOptionDefault && newOptionDefault !== formValue) || | ||
(newOptionConst && newOptionConst !== formValue) | ||
) { | ||
removeOldSchemaData[key] = newOptionDefault; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making this undefined actually didn't fix one or two of the issues. In this case two schemas had a single named property but each had a different default value. When you changed from the first to the second, clearing the value here kept the first selected because getDefaultFormState's use of getFirstMatchingOption
would choose the first option again. By switching from one default to another, it picked the second.
Fixes rjsf-team#2944, rjsf-team#3236, rjsf-team#2978 and possibly others - In `@rjsf/utils`, added new `getClosestMatchingOption()`, `getFirstMatchingOption()` and `sanitizeDataForNewSchema()` schema-based utility functions - Deprecated `getMatchingOption()` and updated all calls to it in other utility functions to use `getFirstMatchingOption()` - Added 100% unit tests for all new functions, renaming the old `getMatchingOptionsTest.ts` file to `getFirstMatchingOptionsTest.ts` - Updated `createSchemaUtils()` and it's associated type to add the three new functions - In `@rjsf/validator-ajv6` and `@rjsf/validator-ajv8`, updated the `schema.tests.ts` to add the new tests for the new schema-based utility functions - In `@rjsf/core`, updated the `MultiSchemaField` to use the new `getClosestMatchingOption()` and `sanitizeDataForNewSchema()` utility functions - Also updated the render to properly pass props to the widget and the schema field - In `@rjsf/playground`, updated `onFormDataEdited()` to only change the formData in the state if the `JSON.stringify()` of the old and new values are different - Also updated the `npm start` command to add the `--force` option to avoid issues where changes made to other packages weren't getting picked up due to `vite` caching - Updated the `utility-functions.md` file to document the new schema-based functions and to fix up incorrect strike-through caused by the unescaped `<S>` generic - Updated the `5.x upgrade guide.md` file to document the new utility functions and the deprecation of `getMatchingOption()`
…jsf-team#2375 - Also updated the `CHANGELOG.md` to include all of the fixed issues
…f/anyOf/oneOf
…a `oneOf`/`anyOf`
ff9865c
to
ad93f3c
Compare
## @rjsf/material-ui | ||
- Fix shrinking of `SelectWidget` label only if value is not empty, fixing [#3369](https://github.com/rjsf-team/react-jsonschema-form/issues/3369) | ||
|
||
## @rjsf/mui | ||
- Fix shrinking of `SelectWidget` label only if value is not empty, fixing [#3369](https://github.com/rjsf-team/react-jsonschema-form/issues/3369) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added these for #3388 so I could merge it without the CHANGELOG changes
…eans and have the same value
… handle readOnly default values like const - Updated some documentation in the types and createSchemaUtils
* fix: fixed several issue in oneOf/anyOf functions Fixes rjsf-team#2944, rjsf-team#3236, rjsf-team#2978 and possibly others - In `@rjsf/utils`, added new `getClosestMatchingOption()`, `getFirstMatchingOption()` and `sanitizeDataForNewSchema()` schema-based utility functions - Deprecated `getMatchingOption()` and updated all calls to it in other utility functions to use `getFirstMatchingOption()` - Added 100% unit tests for all new functions, renaming the old `getMatchingOptionsTest.ts` file to `getFirstMatchingOptionsTest.ts` - Updated `createSchemaUtils()` and it's associated type to add the three new functions - In `@rjsf/validator-ajv6` and `@rjsf/validator-ajv8`, updated the `schema.tests.ts` to add the new tests for the new schema-based utility functions - In `@rjsf/core`, updated the `MultiSchemaField` to use the new `getClosestMatchingOption()` and `sanitizeDataForNewSchema()` utility functions - Also updated the render to properly pass props to the widget and the schema field - In `@rjsf/playground`, updated `onFormDataEdited()` to only change the formData in the state if the `JSON.stringify()` of the old and new values are different - Also updated the `npm start` command to add the `--force` option to avoid issues where changes made to other packages weren't getting picked up due to `vite` caching - Updated the `utility-functions.md` file to document the new schema-based functions and to fix up incorrect strike-through caused by the unescaped `<S>` generic - Updated the `5.x upgrade guide.md` file to document the new utility functions and the deprecation of `getMatchingOption()` * - Fixed a few small issues exposed by trying to use the playground in rjsf-team#2375 - Also updated the `CHANGELOG.md` to include all of the fixed issues * - Fix rjsf-team#2538 by fixing additionalProperties to deal with allOf/anyOf/oneOf * - Updated `getSchemaType()` to grab the type of the first element of a `oneOf`/`anyOf` * - Allow `formData` in `getClosestMatchingOption()` to accept `undefined` * - Responded to reviewer feedback * - Deal with sanitizing data when both `array.items` elements are booleans and have the same value * - Fixed issue with const being assigned default value incorrectly and handle readOnly default values like const - Updated some documentation in the types and createSchemaUtils
* fix: fixed several issue in oneOf/anyOf functions Fixes rjsf-team#2944, rjsf-team#3236, rjsf-team#2978 and possibly others - In `@rjsf/utils`, added new `getClosestMatchingOption()`, `getFirstMatchingOption()` and `sanitizeDataForNewSchema()` schema-based utility functions - Deprecated `getMatchingOption()` and updated all calls to it in other utility functions to use `getFirstMatchingOption()` - Added 100% unit tests for all new functions, renaming the old `getMatchingOptionsTest.ts` file to `getFirstMatchingOptionsTest.ts` - Updated `createSchemaUtils()` and it's associated type to add the three new functions - In `@rjsf/validator-ajv6` and `@rjsf/validator-ajv8`, updated the `schema.tests.ts` to add the new tests for the new schema-based utility functions - In `@rjsf/core`, updated the `MultiSchemaField` to use the new `getClosestMatchingOption()` and `sanitizeDataForNewSchema()` utility functions - Also updated the render to properly pass props to the widget and the schema field - In `@rjsf/playground`, updated `onFormDataEdited()` to only change the formData in the state if the `JSON.stringify()` of the old and new values are different - Also updated the `npm start` command to add the `--force` option to avoid issues where changes made to other packages weren't getting picked up due to `vite` caching - Updated the `utility-functions.md` file to document the new schema-based functions and to fix up incorrect strike-through caused by the unescaped `<S>` generic - Updated the `5.x upgrade guide.md` file to document the new utility functions and the deprecation of `getMatchingOption()` * - Fixed a few small issues exposed by trying to use the playground in rjsf-team#2375 - Also updated the `CHANGELOG.md` to include all of the fixed issues * - Fix rjsf-team#2538 by fixing additionalProperties to deal with allOf/anyOf/oneOf * - Updated `getSchemaType()` to grab the type of the first element of a `oneOf`/`anyOf` * - Allow `formData` in `getClosestMatchingOption()` to accept `undefined` * - Responded to reviewer feedback * - Deal with sanitizing data when both `array.items` elements are booleans and have the same value * - Fixed issue with const being assigned default value incorrectly and handle readOnly default values like const - Updated some documentation in the types and createSchemaUtils
Reasons for making this change
Fixes #3236, #2978, #2944, #2202, #2183, #2086, #2069, #1661 and possibly others
Fixes #2538 by dealing with
additionalProperties
withanyOf
/oneOf
Fixes #1654 by inferring types from the
oneOf
/anyOf
@rjsf/utils
, added newgetClosestMatchingOption()
,getFirstMatchingOption()
andsanitizeDataForNewSchema()
schema-based utility functionsgetMatchingOption()
and updated all calls to it in other utility functions to usegetFirstMatchingOption()
getMatchingOptionsTest.ts
file togetFirstMatchingOptionsTest.ts
createSchemaUtils()
and it's associated type to add the three new functionsstubExistingAdditionalProperties
to deal withadditionalProperties
withanyOf
/oneOf
getSchemaType()
to grab the type of the first element of aoneOf
/anyOf
@rjsf/validator-ajv6
and@rjsf/validator-ajv8
, updated theschema.tests.ts
to add the new tests for the new schema-based utility functions@rjsf/core
, updated theMultiSchemaField
to use the newgetClosestMatchingOption()
andsanitizeDataForNewSchema()
utility functionsObjectField
to deal withadditionalProperties
withanyOf
/oneOf
@rjsf/playground
, updatedonFormDataEdited()
to only change the formData in the state if theJSON.stringify()
of the old and new values are differentnpm start
command to add the--force
option to avoid issues where changes made to other packages weren't getting picked up due tovite
cachingutility-functions.md
file to document the new schema-based functions and to fix up incorrect strike-through caused by the unescaped<S>
generic5.x upgrade guide.md
file to document the new utility functions and the deprecation ofgetMatchingOption()
Checklist
npm run test:update
to update snapshots, if needed.