-
Notifications
You must be signed in to change notification settings - Fork 0
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
Disc 2758 enhance text input2 #746
Conversation
03f66f9
to
38b6da3
Compare
src/TextInput2/TextInput2.tsx
Outdated
@@ -83,7 +89,16 @@ const TextInput2: React.FC<Props> = ({ | |||
|
|||
useEffect(() => { | |||
// don't show error if nothing has happened yet | |||
if (!isFocused && errorMessage === null) { | |||
if (!showErrorOnBlurOnly && !isFocused && errorMessage === null) { |
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.
This change is backwards compatible, since existing implementations of TextInput2
won't provide the showErrorOnBlurOnly
prop- hence, !showErrorOnBlurOnly
will evaluate to true
for those component implementations.
781b75b
to
5cf67a7
Compare
src/TextInput2/TextInput2.tsx
Outdated
setErrorMessage(newErrorMessage); | ||
// if showErrorOnBlurOnly, only display error after user has | ||
// modified initial input value and removed focus from input | ||
if (!showErrorOnBlurOnly || (!isFocused && isInputDirty)) { |
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.
Same backwards compatibility comment as above^
5cf67a7
to
983317d
Compare
282fa42
to
f8313c9
Compare
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.
Nice PR Antonio! Looks like you put alot of thought into the problems with this component and your changes to solve them. I wouldn't worry about backwards compatibility too much here, as I can't imagine anyone would rely on the live error message functionality for anything. Let me know if you see it differently.
I didn't put too much thought into the initial implementation of the error messaging, and this PR has made me do some reading. I found some nice articles and looked at some component libraries:
https://baymard.com/blog/inline-form-validation
https://cxl.com/blog/form-validation/
https://stripe.dev/elements-examples/
What I understand here is that we probably want some of these principles:
- Errors should show only on blur, they clear instantly if they are no longer in error
- If the input has no text entered, it should never show an error
- The
REQUIRED
error should probably come via a prop and not be internal to the component- We can use
initialIsInError
to handle REQUIRED errors that come from an event. e.g. clicking submit triggering an error state. It doesn't look like this is being used anywhere right now, so potential to rename torequirementError
or something
- We can use
onBlur
andonFocus
prop callbacks are good to have in general. However, if we wanted to force the user into input formatting, then rather than using onBlur we could probably add two function props (naming is hard)formatValue
: before onChange, call this function then pass the resulting string into onChange. e.g. no trailing/leading spaces, only numbers, auto-case, limit number of characters, etc.formatDisplay
: if we wanted other text in the input, we can change how the text is formatted. e.g. phone numbers (XXX) XXX-XXXX, MM/YYYY dates, etc.
Again, I don't think preserving backwards compatibility is too critical. Let me know your thoughts. Thanks!
src/TextInput2/TextInput2.tsx
Outdated
onBlur={() => setIsFocused(false)} | ||
onChange={(e) => { | ||
// set isInputDirty to true after value is changed for the first time | ||
if (!isInputDirty) setIsInputDirty(true); |
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.
Two personal opinions about dirty states:
- I think in general the dirty state is something I think we can avoid, because I don't think dirty states are intuitive to the user. e.g. "I've typed something on the keyboard even if nothing is filled in the input" vs "input is filled, and I'm done with the input. tell me if it's valid"
- If we must have a dirty state, I'd rather have the dirty state be calculated by comparing the initial value with the current state, e.g. https://github.com/Clever/sd2/blob/dff23a6adece60718a228043d5e510290e5f4f1e/ui/pages/IdentityManagement/LoginCredentials/LoginCredentials.tsx#L116-L121
But overall, I think if we are able to sucessfully rewrite the entire validation hook then we should be able to do away with this
@@ -83,7 +91,7 @@ const TextInput2: React.FC<Props> = ({ | |||
|
|||
useEffect(() => { |
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.
After reading about some best practices for error messaging, we can probably rewrite this entire thing. Something like this maybe:
useEffect(() => {
// if input is empty and requirement error is active, then do nothing and return
// if input is empty, then clear error and return
// check error validation function
// if no error validation, clear error and return
// if errormessage exists and is not focused, then set error and return
});
f8313c9
to
a994678
Compare
a994678
to
071f781
Compare
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.
Nice tests! I think if we add the errorOnEmpty
prop to the useEffect
dependency list, we can simplify a little bit
src/TextInput2/TextInput2.tsx
Outdated
} | ||
|
||
setErrorMessage(null); | ||
}, [value, isFocused]); |
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.
Since this uses shouldDisplayEmptyError
, it needs to have errorOnEmpty
here as well. I think once that happens we can remove the other useEffect
, and let a single useEffect
handle everything. e.g.
const [errorMessage, setErrorMessage] = useState(null);
...
// useEffect
if (!value) {
setErrorMessage(errorOnEmpty && !isFocused ? "" : null);
return;
}
...
if (!newErrorMessage) {
setErrorMessage(null);
return;
}
Correct me if i'm wrong here
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.
Good catch! 2733131
Changes initialIsInError prop to errorOnEmpty, to more accurately describe the prop's use case. If errorOnEmpty, empty error state is displayed if the input has no value and is out of focus (the error state is removed on focus). If not errorOnEmpty, error state is only displayed on blur, and is removed if the value is empty or if the error is resolved.
071f781
to
8467631
Compare
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.
🙌 Let me know how the implementation goes! Thanks for all your help here and humoring my comments
The implementation seems to be working how we want it! I updated the GIFs in the PR description to show the new implementation. Thanks again for the thorough reviews. |
Jira: DISC-2758
Overview:
Modifies
TextInput2
error validation behavior:initialIsInError
prop (unused) toerrorOnEmpty
errorOnEmpty
, empty error state is displayed while input is out of focus and value is empty, but is removed when input is in focus and value is emptyScreenshots/GIFs:
When
errorOnEmpty
is false:When
errorOnEmpty
is true:Testing:
Note: Added tests use Enzyme's
mount
function instead ofshallow
, becauseuseEffect
calls aren't made during shallow renders.Roll Out:
package.json
npm version minor
npm version major
npm version minor
ComponentsView.jsx
. To do so:docs/assets/img
with the format<COMPONENT URL LINK>.png
make deploy-docs
)