Skip to content
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

Merged
merged 17 commits into from
Dec 9, 2024

Conversation

7emansell
Copy link
Contributor

Ticket:

This PR does the following:

  • Addresses comments in My Account 2.0 VQA and accessibility review
    • Mainly spacing (VQA) and focus behavior (a11y)

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

  • Adds focus management to all form fields

Checklist:

  • I updated the CHANGELOG with the appropriate information and JIRA ticket number (if applicable).
  • I have added relevant accessibility documentation for this pull request.
  • All new and existing tests passed.

Copy link

vercel bot commented Dec 5, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
research-catalog ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 9, 2024 7:51pm

buttonId="edit-password-button"
onClick={() => {
setIsEditing(true)
setEditingField("password")
setTimeout(() => {
Copy link
Contributor Author

@7emansell 7emansell Dec 5, 2024

Choose a reason for hiding this comment

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

@charmingduchess:

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@charmingduchess:

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.

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Contributor

@charmingduchess charmingduchess Dec 9, 2024

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)}
Copy link
Contributor Author

@7emansell 7emansell Dec 5, 2024

Choose a reason for hiding this comment

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

@charmingduchess:

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.

Copy link
Contributor Author

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

@7emansell
Copy link
Contributor Author

Got a new ticket number so closed the old PR and copied Vera's comments over

Copy link
Member

@EdwinGuzman EdwinGuzman left a 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 () => {
Copy link
Member

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
Copy link
Member

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:

Screenshot 2024-12-09 at 9 49 43 AM

buttonId="edit-password-button"
onClick={() => {
setIsEditing(true)
setEditingField("password")
setTimeout(() => {
Copy link
Member

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(() => {
Copy link
Member

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>}
Copy link
Member

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(() => {
Copy link
Member

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>}
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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?

Copy link
Contributor Author

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()
Copy link
Member

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?

Copy link
Contributor

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".

Copy link
Contributor

@charmingduchess charmingduchess left a comment

Choose a reason for hiding this comment

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

Looks good!

Copy link
Member

@EdwinGuzman EdwinGuzman left a 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.

@7emansell 7emansell merged commit 9f1e092 into main Dec 9, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants