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

Adds FormControl's "Auto-Wiring" to SelectPanel v2 Component #4389

Merged

Conversation

JeffreyKozik
Copy link
Contributor

@JeffreyKozik JeffreyKozik commented Mar 13, 2024

Followup to this conversation in Slack and #4372. Related to https://github.com/github/primer/issues/2396.

Changelog

New

Changed

Made use of the the new useFormControlForwardedProps hook (introduced by #3632) to automatically wire the SelectPanel v2 component to the accessibility and validation provided by the FormControl component it's nested within. If SelectPanel v2 isn't nested within FormControl the hook has no effect.

Within a form the accessible name is now the text within the corresponding FormControl.Label tag and the button itself
Screenshot of computed properties
Screenshot as storybook UI

Outside of a form the accessible name is still the text within the SelectPanel.Button itself
Screenshot of computed properties
Screenshot of storybook UI

Removed

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

@JeffreyKozik JeffreyKozik requested a review from a team as a code owner March 13, 2024 18:28
Copy link

changeset-bot bot commented Mar 13, 2024

🦋 Changeset detected

Latest commit: eacfa7c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Minor

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

@@ -315,7 +328,8 @@ const Panel: React.FC<SelectPanelProps> = ({
}

const SelectPanelButton = React.forwardRef<HTMLButtonElement, ButtonProps>((props, anchorRef) => {
return <Button ref={anchorRef} {...props} />
const inputProps = useFormControlForwardedProps(props)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Octicon,
Link,
Checkbox,
useFormControlForwardedProps,
Copy link
Contributor Author

@JeffreyKozik JeffreyKozik Mar 13, 2024

Choose a reason for hiding this comment

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

Using the hook recently created by @iansan5653 in this PR: #3632. This hook is also being used in the InlineAutocomplete draft component:

const inputProps = useFormControlForwardedProps(externalInputProps)

Copy link
Contributor

github-actions bot commented Mar 13, 2024

size-limit report 📦

Path Size
packages/react/dist/browser.esm.js 88 KB (0%)
packages/react/dist/browser.umd.js 88.25 KB (0%)

@primer primer deleted a comment from github-actions bot Mar 13, 2024
@primer primer deleted a comment from github-actions bot Mar 13, 2024
@JeffreyKozik JeffreyKozik marked this pull request as draft March 13, 2024 18:33
@github-actions github-actions bot temporarily deployed to storybook-preview-4389 March 13, 2024 18:34 Inactive
@JeffreyKozik JeffreyKozik marked this pull request as ready for review March 13, 2024 18:43
Copy link
Member

@siddharthkp siddharthkp 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!

Merging to main is blocked today for another release. I can merge this tomorrow.

@JeffreyKozik
Copy link
Contributor Author

Looks good!

Merging to main is blocked today for another release. I can merge this tomorrow.

Sounds great, thank you!

Copy link
Contributor

@adrianababakanian adrianababakanian left a comment

Choose a reason for hiding this comment

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

🎉

@iansan5653
Copy link
Contributor

iansan5653 commented Mar 14, 2024

🤔 Overall the approach looks great, but I have concerns about the accessibility implications. If a button's label is set using aria-label or aria-labelledby, the text inside the button is totally overridden and becomes invisible to screen readers.

This is problematic for several reasons:

  • There may be additional context / instructions in the panel's placeholder value that's shown when no selection is active, and designers may rely on users being able to access this content
  • In setups where the current selection is used as the anchor text, when a selection is made, screen reader users will be unable to determine what it is (or even that anything is selected)
  • Voice control users will see a button and think that its content is its label, but actually the label is something different (this may be more of an inherent problem with the way we style SelectPanel anchors as buttons instead of inputs)

A potential alternative: What if instead of setting aria-labelledby, we set the button's aria-label to something like "Label Text: Button Text"? This would be a bit of a hack; we'd have to do something roughly like `${document.getElementById(labelledBy).textContent}: ${buttonText}`. This would result in the following label states in your Storybook example:

  • Without selection: "SelectPanel within FormControl: Choose a tag"
  • With selection: "SelectPanel within FormControl: v35.28.0"

However there are risks with this approach too: will users be confused by the accessible label of the element changing when the selected value changes? I don't know how this would affect VoiceOver users.

It's also possible this could create confusing labels, like if the consumer sets the visible label and the button text to the same value: "Choose a tag: Choose a tag" would be annoying. IMHO, the potential for annoyance is probably outweighed by the potential for totally excluding important context.


A third option might be to explore a different role altogether for the anchor of a SelectPanel. I'm not totally sure that button is actually ideal here when the current selection is used as the anchor text.

On one hand yes, it's an activatable button that opens a panel; but on the other hand it's also an input with a visible current value. Is there a role we can use that would allow us to set both a label and a current value?

I think it's worth digging in to this a bit further - maybe in accessibility office hours?

@JeffreyKozik
Copy link
Contributor Author

@iansan5653 thank you for the detailed and thoughtful review.

As we use the FormControl.Label to label other components within forms such as radio groups, text inputs, and select menus, why is the labelling able to work in an accessible way for those components, but not for this one? Does it ultimately come down to those being role=input and this being role=button?

If that's the case would it either a) be appropriate to change to role=input for the entire SelectMenu or b) be appropriate to have a prop that allows developers to set to role=input for situations such as this one?

Alternatively, the 2nd option you propose seems acceptable as well.

I'm happy to go to a11y office hours to discuss this further if needed as well. Let me know the next one you're available for.

@iansan5653
Copy link
Contributor

iansan5653 commented Mar 14, 2024

Does it ultimately come down to those being role=input and this being role=button?

Yep, pretty much. While both roles are widgets with accessible names and text content, screen readers intentionally ignore the text content of a button in favor of its accessible name. 99% of the time these two things are identical (because it's not really standard to have a button with an external label), so if a screen reader were to read out both it would be annoying and unnecessary the vast majority of the time .

If that's the case would it either a) be appropriate to change to role=input for the entire SelectMenu or b) be appropriate to have a prop that allows developers to set to role=input for situations such as this one?

Unfortunately not. role=input implies much more than just 'this button acts similarly to an input'. Screen readers will expect an input role to act exactly like a text input - that is, they will expect it to accept text content and not provide a list of options.

Native browser <select> elements get the role combobox, which is much a closer candidate. I think the only thing SelectPanel is missing from the combobox pattern is handling alphanumeric keys when focus is on the anchor when the [panel is closed (this should probably open the panel and put those characters in the filter). Here's an example of an accessible select-like custom combobox.

It's honestly possible that we should consider always giving the SelectPanel anchor the role of combobox, or maybe automatically in certain situations. I really don't know for sure about that and I'm by no means an expert, so I'd definitely recommend talking to the accessibility team about that. I'd be happy to show up to any of their office hours - just let me know when you'll be there!

@JeffreyKozik
Copy link
Contributor Author

@iansan5653 that's interesting context. It seems like giving SelectPanel the combobox role makes a lot of sense here, although I'm not sure the implications of that for the other use cases of SelectPanel such as when it's outside of a form. @siddharthkp what do you think about giving the SelectPanel anchor the role of combobox?

@TylerJDev
Copy link
Contributor

I believe it was mentioned that SelectPanel couldn't be a combobox, even though it's a similar pattern, as it handles certain aspects differently than a true combobox would.

We do have something else in the works that could act as the middle ground between SelectPanel and ActionMenu, that's specifically meant to be a true "combobox". Since this is still in a proof of concept state, I think it'd be best to try a different route for now, and iterate on it later.

I'm wondering if we could keep the usage of aria-labelledby, but also reference the button itself. For example:

<label id="label1">SelectPanel within FormControl</label>
<button id="label2" aria-labelledby="label1 label2">Choose a tag</button>

This means that the button references both the label and the contents within itself (i.e. "Choose a tag"), making the accessible name: "SelectPanel within FormControl, Choose a tag". This is using multiple aria-labelledby id(s) to concatenate a label.

We could also change the order so that the current value is announced first (e.g. aria-labelledby="label2 label1" = "Choose a tag, SelectPanel within FormControl"). If we went this route, this would have to be unique to SelectPanel, as other the other components that support FormControl linking now do not require additional ARIA.

I'd still recommend running this by the direct Accessibility team, as I may be unaware of any other implications, such as how someone using voice might interact with this control, and Ian's comments around potential confusion with the label. I don't mind following up with them too!

@siddharthkp
Copy link
Member

siddharthkp commented Mar 15, 2024

This is great @iansan5653, thanks for bringing this up! I totally missed it.

Without selection: "SelectPanel within FormControl: Choose a tag"
With selection: "SelectPanel within FormControl: v35.28.0"

This looks perfect! There is a really old discussion that might be helpful to read through: https://github.com/github/primer/discussions/696

I'm wondering if we could keep the usage of aria-labelledby, but also reference the button itself. For example:

SelectPanel within FormControl
Choose a tag

This would work nicely!

@TylerJDev, Just checking, both of these information should be considered as labels right (not description)?

It's honestly possible that we should consider always giving the SelectPanel anchor the role of combobox

As Tyler said, it's something still being discussed with our accessibility consultants, we may or may not end up there. It's tricky because SelectPanel also has other elements like filter buttons (that look like tabs).

Let's park that for a couple weeks till we can conclude if we're going the combobox route or not.

@JeffreyKozik how do you feel about making this change? If you've got other things on your plate, I can help out here!

@JeffreyKozik
Copy link
Contributor Author

@TylerJDev @siddharthkp thanks for providing more details on this issue! I'll take a swing at this change later today, but if I get pulled into other issues and need you to help out here on Monday I'll let you know, thanks @siddharthkp!

@TylerJDev
Copy link
Contributor

Just checking, both of these information should be considered as labels right (not description)?

Yup, exactly! If we used a description for one or the other, there's the possibility that it would be ignored by a screen reader (or other AT).

@github-actions github-actions bot temporarily deployed to storybook-preview-4389 March 26, 2024 18:33 Inactive
@JeffreyKozik
Copy link
Contributor Author

@siddharthkp if this approach looks suitable to you, I think it's ready to merge 👍

@siddharthkp siddharthkp added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Apr 9, 2024
@JeffreyKozik
Copy link
Contributor Author

JeffreyKozik commented Apr 22, 2024

Trying to create the integration tests and run them again with my updated code, however the workflow keeps failing: https://github.com/github/github/actions/workflows/primer-react-pr-test.yml so I'm not even able to run the integration tests. Not sure if this is related to what's ongoing with npm, or a separate issue

It's working as expected now

@JeffreyKozik
Copy link
Contributor Author

Integration tests passed 🎉 https://github.com/github/github/pull/322332

Only tests failing seem related to github-licenses which I believe is expected because it's an integration test PR

@siddharthkp siddharthkp added this pull request to the merge queue Apr 24, 2024
Merged via the queue into main with commit 02035fe Apr 24, 2024
30 checks passed
@siddharthkp siddharthkp deleted the jeffreykozik/autolinking_selectpanelv2_to_formcontrol branch April 24, 2024 14:11
@primer primer bot mentioned this pull request Apr 24, 2024
@siddharthkp siddharthkp added integration-tests: passing Changes in this PR do NOT cause breaking changes in gh/gh and removed integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm labels May 6, 2024
@JeffreyKozik JeffreyKozik self-assigned this Jul 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration-tests: passing Changes in this PR do NOT cause breaking changes in gh/gh
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants