Skip to content

fix: ui:emptyValue behavior #2457

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

Closed
wants to merge 7 commits into from

Conversation

ssbyoung
Copy link

@ssbyoung ssbyoung commented Jul 1, 2021

Please note that this PR relies on #2456 being merged and released

Reasons for making this change

Fixes #2449

By default, if a form has a field with an empty string and ui:emptyValue is filled out, the empty value will not appear until the user interacts with the form. I believe that this is incorrect. This change makes it so that ui:emptyValue will be populated on the first form render.

I also refactored everywhere this logic lived to use a hook inside of @rjsf/core. Because it is a hook it requires the user to be on at least react 16.8.0, hence the need for #2456 . I also updated all the peer dependencies to 3.0.0. If PR #2456 goes through as a major version. All of these files will need to be updated.

Important Changes

  • ui:emptyValue will be populated for empty form fields
  • If ui:emptyValue is not populated AND the user enters the empty string onChange will be called with the empty string instead of undefined.

Checklist

  • I'm adding or updating code
    • I've added and/or updated tests
    • I've updated docs if needed

@ssbyoung ssbyoung requested a review from agustin107 as a code owner July 1, 2021 19:16
@ssbyoung ssbyoung changed the title Ssbyoung/fix empty value fix: ui:emptyValue behavior Jul 1, 2021
@@ -286,6 +288,22 @@ declare module '@rjsf/core' {

export module utils {

type UseEmptyValueOnChangeProps = {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm definitely not super strong at typescript. If someone has a suggestion how to clean this up, it would be much appreciated.

@@ -14,7 +14,7 @@
"precommit": "lint-staged",
"publish-to-npm": "npm run build && npm publish",
"start": "concurrently \"npm:build:* -- --watch\"",
"tdd": "cross-env NODE_ENV=test mocha --require @babel/register --watch --require ./test/setup-jsdom.js test/**/*_test.js",
"tdd": "cross-env BABEL_ENV=test NODE_ENV=test mocha --require @babel/register --watch --require ./test/setup-jsdom.js test/**/*_test.js",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command does not work without the BABEL_ENV being set.

@@ -208,6 +208,21 @@ describe("StringField", () => {
});
});

it("should render ui:EmptyValue when formData is emptyString and ui:EmptyValue is defined", () => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the most important test in this PR. The act(() => {}) gives the chance for the hook to run.

@@ -221,7 +236,7 @@ describe("StringField", () => {
});

sinon.assert.calledWithMatch(onChange.lastCall, {
formData: undefined,
formData: "",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were previously undefined because options.emptyValue was undefined. If the user enters the empty string, I believe we should call onChange with the empty string.

@@ -7,6 +7,11 @@ import jsonpointer from "jsonpointer";
import fields from "./components/fields";
import widgets from "./components/widgets";
import validateFormData, { isValid } from "./validate";
import useEmptyValueOnChange from './hooks/useEmptyValueOnChange';

export const hooks = {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tacked the hooks onto the utils. If there is a better place, let me know.

return rawOnChange(newValue);
}, [rawOnChange]);

useEffect(() => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This useEffect takes care of the inital render state.


useEffect(() => {
if (value === mergeValues(value)){
return;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in this guard clause because I was seeing tests fail for onChange getting called multiple times if the value wasn't empty.

@epicfaace
Copy link
Member

By default, if a form has a field with an empty string and ui:emptyValue is filled out, the empty value will not appear until the user interacts with the form. I believe that this is incorrect. This change makes it so that ui:emptyValue will be populated on the first form render.

Trying to understand why we need to change this though -- isn't populating data on the first form render what default in the schema is for? Why couldn't someone just set something equal to ui:emptyValue and default if they want this behavior?

I think that when ui:emptyValue was originally added to this library, the intention was for it to only override the default behavior of setting a field to undefined after the user interacts with the form -- "When a text input is empty, the field in form data is set to undefined" (see https://github.com/rjsf-team/react-jsonschema-form/blob/3ec17f1c0ff40401b7a99c5e9891ac2834a1e73f/docs/usage/validation.md#the-case-of-empty-strings).

Maybe could you further explain your use case so we can see if this PR is the best solution or there is another alternative that may work better?

@ssbyoung
Copy link
Author

@epicfaace default value does not work properly. Here is a a small example demonstrating it:
rjsf-default-issue.zip

I think default is the behavior I want. It wasn't documented and I only found the syntax in the playground.

My use case is that when I initially render the form with empty data, I'd like to have certain fields have pre-populated values as defined in the schema.

@epicfaace
Copy link
Member

@ssbyoung can you share the issue with default using a playground example? https://rjsf-team.github.io/react-jsonschema-form/

@ssbyoung
Copy link
Author

@epicfaace Can you take a look at the example I sent? I think there might be an issue with the consumer of this library using react 17 and this library using react 16. I'm also unsure how to modify the playground with the issue I'm seeing.

@epicfaace
Copy link
Member

I think you could make a codepen here (https://codepen.io/epicfaace/pen/WNjvBrb) and put in your desired versions of React / rjsf

@ssbyoung
Copy link
Author

@epicfaace I'm having a hard time getting a codepen with [email protected]. Do you have an example with one that uses the latest version?

@ssbyoung
Copy link
Author

@epicfaace is the playground https://rjsf-team.github.io/react-jsonschema-form/ running on version 3.0.0?

@epicfaace
Copy link
Member

Oh, I think codepens with v3 an issue because of #2447. Could you try using a codepen with the latest version of v2 instead? I'm guessing the issue will also appear in that version.

@ssbyoung
Copy link
Author

@ssbyoung https://codepen.io/ssbyoung/pen/NWjdNjv?editors=1111 is a codepen showing this same issue.

@epicfaace
Copy link
Member

epicfaace commented Jul 13, 2021 via email

@ssbyoung
Copy link
Author

As a consumer I'd expect all falsey values to to get replaced. Does it explicitly not have to be set in the object for the default to appear?

@ssbyoung
Copy link
Author

@epicfaace after trying default with my code, the current implementation works as is. I will be closing this PR as well as the other one related to this change.

@ssbyoung ssbyoung closed this Jul 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ui:emptyValue does not work when initial data has an empty string
2 participants