-
Notifications
You must be signed in to change notification settings - Fork 10
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
Add instantValidation
prop for TextArea
#2355
Conversation
🦋 Changeset detectedLatest commit: bee2c97 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Size Change: +86 B (+0.09%) Total Size: 101 kB
ℹ️ View Unchanged
|
A new build was pushed to Chromatic! 🚀https://5e1bf4b385e3fb0020b7073c-crdgebdozd.chromatic.com/ Chromatic results:
|
render: (args) => { | ||
return ( | ||
<View style={{gap: spacing.small_12}}> | ||
<LabelSmall htmlFor="instant-validation-true-not-required"> |
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.
Will replace with LabeledField after. I'll be merging/releasing the form field updates separately from LabeledField (LabeledField changes are in feature/labeled-field
right now, and I'll update it with these form field changes after!)
GeraldRequired Reviewers
Don't want to be involved in this pull request? Comment |
npm Snapshot: Published🎉 Good news!! We've packaged up the latest commit from this PR (50f71bb) and published all packages with changesets to npm. You can install the packages in webapp by running: ./services/static/dev/tools/deploy_wonder_blocks.js --tag="PR2355" Packages can also be installed manually by running: yarn add @khanacademy/wonder-blocks-<package-name>@PR2355 |
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 looks great, thanks a lot for putting so much detail in the tests and documentation
}); | ||
|
||
describe("instantValidation=true", () => { | ||
it("should call validate each time the value changes if the instantValidation prop is true", async () => { |
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.
nit: The last part of the test statement is redundant so you could probably remove it if you'd like (no change required). For context, the test should display in the CLI as:
instantValidation=true > should call validate each time the value changes if the instantValidation prop is true
it("should call validate each time the value changes if the instantValidation prop is true", async () => { | |
it("should call validate each time the value changes", async () => { |
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 call! I removed the redundant parts for the test names 😄
expect(validate.mock.calls).toStrictEqual([ | ||
["t"], | ||
["te"], | ||
["tes"], | ||
["test"], | ||
]); |
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.
praise: This is a neat way to test multiple things while using the AAA testing pattern 🎉
|
||
// Assert | ||
expect(field).toHaveAttribute("aria-invalid", "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.
question: I wonder if we need to test what happens in the exact case where the user just focuses on the field and sets instantValidation=true
. In theory, the field should be in its default state (aria-invalid=false
), so I'm not sure if it is worth testing that case to prevent regressions in the future. wdyt?
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.
Clarification from our discussion offline: This refers to the case where the field is empty, the user focuses on it, and verify that validate
is not called when instantValidation=true
As discussed, this case is unrelated to the instantValidation
prop since validating on mount depends on if there is a value set in the component! It is covered in these tests:
wonder-blocks/packages/wonder-blocks-form/src/components/__tests__/text-area.test.tsx
Lines 980 to 1012 in d2aeaf8
it("should call the validate function when it is first rendered", async () => { | |
// Arrange | |
const validate = jest.fn(); | |
render( | |
<TextArea | |
value="text" | |
onChange={() => {}} | |
validate={validate} | |
/>, | |
defaultOptions, | |
); | |
// Act | |
// Assert | |
expect(validate).toHaveBeenCalledExactlyOnceWith("text"); | |
}); | |
it("should not call the validate function when it is first rendered if the value is empty", async () => { | |
// Arrange | |
const validate = jest.fn(); | |
render( | |
<TextArea | |
value="" | |
onChange={() => {}} | |
validate={validate} | |
/>, | |
defaultOptions, | |
); | |
// Act | |
// Assert | |
expect(validate).not.toHaveBeenCalled(); | |
}); |
Let me know if this addresses what you had in mind! :)
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.
thanks for the link, the second test covers the case I was thinking about.
await userEvent.type(field, "test"); | ||
// Blur will trigger error to be shown | ||
await userEvent.tab(); |
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.
question: I'm not sure if I'm missing some context here.... how do we signal in the test that this case throws a validation error? Do we need to modify the handleValidate
fn to check against test
?
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 test was checking only the last call on the handleValidate
mock since there were other tests making sure it called it with the error (I was trying to avoid multiple assertions!). I updated the assertion though so it checks for all calls so it includes the first call with the error message. Hope that makes it clearer!
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 looks great, thanks!
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.
praise: this covers a lot of scenarios, awesome (as usual) 👏
// If instantValidation is false and there is an error | ||
// message, error needs to be cleared when the user updates | ||
// the value |
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.
praise: this context is super helpful!
const handleBlur = (event: React.FocusEvent<HTMLTextAreaElement>) => { | ||
// Only handle validation on blur if instantValidation is false | ||
if (!instantValidation) { | ||
// Handle validation on blur if: | ||
// 1. There is a value in the field so a user tabbing through | ||
// fields doesn't trigger the error state when the field is left | ||
// empty. Or, | ||
// 2. The field is required. Tabbing through an empty field that | ||
// is required will trigger the error state | ||
if (event.target.value || required) { | ||
handleValidation(event.target.value); | ||
} | ||
} | ||
|
||
if (onBlur) { | ||
onBlur(event); | ||
} | ||
}; |
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.
thought: I know that we discussed this at some point, but maybe related to another prop. I feel that this logic for validation could be abstracted out into a custom hook so other field components could reuse this functionality. Do you think this would be feasible? or do you envision some limitations between form fields? (no changes required for this PR, just food for thought)
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.
Yes good question! I'm going to refactor TextField next so it's a function component, then I'll see how we can share the validation logic between TextArea and TextField. I wanted to work on implementing the validation logic in one of the components first so we can get the functionality working with tests! :)
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.
Sounds good!
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 a great PR! So many good tests! I left one suggestion for a test name and a reusable regex utility. This is looking great though, awesome job Bea!
export const InstantValidation: StoryComponentType = { | ||
args: { | ||
validate(value: string) { | ||
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/; |
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 this make sense for a test utility so it could be reused?
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.
Yes! I'll have another PR up soon related to shared validation logic for TextField and TextArea so I will update the stories to use a test utility there!
parameters: { | ||
chromatic: { | ||
// Disabling because this doesn't test anything visual. | ||
disableSnapshot: 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.
Ooh, I didn't know you could do that! Nice.
packages/wonder-blocks-form/src/components/__tests__/text-area.test.tsx
Outdated
Show resolved
Hide resolved
… If instantValidation is false, the error is cleared on change
…ncludes that info already
… checking the last one
Co-authored-by: Marcy Sutton-Todd <[email protected]>
3f056a0
to
bee2c97
Compare
## Summary: Refactor TextField to be a function component. This will make it easier to share validation logic from the TextArea instantValidation PR #2355 so that TextField will also have the `instantValidation` prop functionality (I'll be working on this next)! Issue: WB-1781 ## Test plan: Tests pass and TextField looks and functions the same way (`/?path=/docs/packages-form-textfield--docs`) Author: beaesguerra Reviewers: beaesguerra, jandrade, marcysutton, kevinb-khan Required Reviewers: Approved By: jandrade, marcysutton Checks: ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Test / Test (ubuntu-latest, 20.x, 2/2), ✅ Test / Test (ubuntu-latest, 20.x, 1/2), ✅ Lint / Lint (ubuntu-latest, 20.x), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ⏭️ Chromatic - Skip on Release PR (changesets), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald, ⏭️ dependabot Pull Request URL: #2356
Summary:
Issue: WB-1781
Next: I'll work on refactoring TextField to a function component first and then will add the same
instantValidation
prop to TextFieldTest plan:
/?path=/docs/packages-form-textarea--docs#instant%20validation
)/?path=/story/packages-form-textarea--instant-validation
)