-
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
SCC-4384: My Account 2.0 VQA/a11y round 1 #412
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
buttonId="edit-password-button" | ||
onClick={() => { | ||
setIsEditing(true) | ||
setEditingField("password") | ||
setTimeout(() => { |
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.
Does this shift the focus to the text input? If so, there actually should be two different refs, just to make it explicit what is being focused on.
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.
@EdwinGuzman this is what we discussed– basically now I'm using 1 ref for everything (except in SettingsInputForm
, which uses an array of refs to match the array of inputs, and can't really be combined with a button ref). This makes it confusing to predict where the focus will go just from reading the code, but it does work, so I'm torn.
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.
For readability and debugging where focus goes, it seems like having two refs is the approach to follow. When reading useRef<HTMLButtonElement | TextInputRefType | null>
, I was thinking that ref is doing too many things. I think we discussed one ref where possible and updating the ref.current
value but that's not the case 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.
To be clear, what other problems are there?
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 think the issue is just conflicting directives. But I think me and Edwin agree this is a case where we can opt for readability.
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.
All refs now split up for readability again 👍
} | ||
} | ||
|
||
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.
With three text inputs open for the phone settings, this focus is triggering on every keystroke input. ie Typing a single character into input 2 jumps the focus to input 3. I think you want an empty array to only run on the first render. I think this will make typescript unhappy but that's ok.
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.
updated it to tempInputs.length
which I think is right because then it updates only when a new input is added/removed
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 linter is saying that focusLastInput
should also go in here. I'm not sure that's right but if it should go here, then the function should be wrapped in a useCallback.
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.
hmm yes I think the most idiomatic way of doing this is to make the focusLastInput
a useCallback
with a dependency of tempInputs.length
. I'm not too concerned about it. I think it is good to see that the useEffect
is going to run when that value updates, instead of having to check a useCallback
for a dependency array.
@@ -183,10 +211,15 @@ const SettingsInputForm = ({ | |||
sx={{ | |||
width: { base: "87%", md: "300px" }, | |||
}} | |||
ref={(el) => (inputRefs.current[index] = el)} |
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.
Not sure if you came across this ("How to manage a list of refs using a ref callback"), but your solution is react.dev approved! Looking at their solution, though, I think it might be important to remember to delete refs from the array once the input is removed from the dom.
I tried adding 5 inputs, then deleting one from the middle, and the count on the array of input refs actually went up to 6. It doesn't seem to cause any problems, but I think to avoid bugs down the line, the inputRefs array should match the inputs that are actually present on the dom.
Actually just encountered a bug! after opting to stay logged in from the timed logout modal, the focus is lost and the last input is null, which leads to a loss of focus. I think this can be avoided by removing refs as the inputs are removed.
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.
Updated to now remove ref (L104). Discussed offline but the issue with modal focus isn't going to be addressed here
Got a new ticket number so closed the old PR and copied Vera's comments over |
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.
As far as I can tell and reviewing the QA, this does fix all the issues. The 1vs2 ref issue could be addressed later if you want to move this back to vqa/a11y review.
After seeing the code, having two refs, similar to SettingsInputForm
, is a good tradeoff for readability.
@@ -60,12 +62,37 @@ describe("email form", () => { | |||
expect(screen.queryByText(/edit/)).not.toBeInTheDocument() | |||
}) | |||
|
|||
it("manages focus", 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.
👍
@@ -4,7 +4,7 @@ type SaveCancelButtonProps = { | |||
isDisabled?: boolean | |||
onCancel: () => void | |||
onSave: () => void | |||
inputType?: "phones" | "emails" | |||
inputType?: string |
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.
Not related to this change, but I was wondering why this was updated to be a generic string. This is used in multiple locations so it makes sense to update this. I think you should list down all possible values and not make it optional.
In some forms, PasswordForm
for example, not passing the inputType
makes it render undefined in the ids:
buttonId="edit-password-button" | ||
onClick={() => { | ||
setIsEditing(true) | ||
setEditingField("password") | ||
setTimeout(() => { |
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.
For readability and debugging where focus goes, it seems like having two refs is the approach to follow. When reading useRef<HTMLButtonElement | TextInputRefType | null>
, I was thinking that ref is doing too many things. I think we discussed one ref where possible and updating the ref.current
value but that's not the case here.
buttonId="edit-password-button" | ||
onClick={() => { | ||
setIsEditing(true) | ||
setEditingField("password") | ||
setTimeout(() => { |
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.
To be clear, what other problems are there?
{usernameInSierra} | ||
</Text> | ||
<EditButton | ||
ref={focusRef as React.Ref<HTMLButtonElement>} |
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.
From the comment in PasswordForm
regarding breaking up the ref into two refs, doing so will allow this as React.Ref...
to be removed.
} | ||
} | ||
|
||
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.
The linter is saying that focusLastInput
should also go in here. I'm not sure that's right but if it should go here, then the function should be wrapped in a useCallback.
@@ -195,6 +228,7 @@ const SettingsInputForm = ({ | |||
isClearable | |||
isClearableCallback={() => handleClearableCallback(index)} | |||
/> | |||
{index == 0 && <div style={{ width: "57px" }}> </div>} |
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 not a fan of these magic numbers (72px on line 252). I don't have any good suggestions right now but I'll take a closer look.
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– there are a lot of random fixed values in the designs which prevent using spacing tokens the way I'd like to
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.
Right, I said I'll take another pass but I don't want to hold up pushing this out for another review. Okay to add comments post merge?
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.
Sure– currently, need to fix the focus issue that occurs when you try to clear the textbox for any of the inputs, so I won't be merging for a bit
const focusLastInput = () => { | ||
const lastIndex = tempInputs.length - 1 | ||
if (inputRefs.current[lastIndex]) { | ||
inputRefs.current[lastIndex]?.focus() |
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 was going to ask if it was possible to use the Map
function like the code example in the React docs resource. It'll work but I imagine this part here would make it difficult to keep track of the previous element to focus?
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.
With an Array we get the order maintained for free, which is crucial here. FWIW, the docs do say "This lets you maintain your own array or a Map".
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.
Looks 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.
This addresses all the a11y/vqa issues. The clear X button is an issue that we'll debug on the DS so for now, it won't be included in this update.
Ticket:
This PR does the following:
Question/request for help:
On mobile when you click edit for Home library/ Notification preference/Password, the fields jump up slightly like they're losing padding/margin or height is just not the same between the two states. I would love a fresh pair of eyes on this since I'm having trouble tracking down why this is happening
How has this been tested?
Accessibility concerns or updates
Checklist: