-
Notifications
You must be signed in to change notification settings - Fork 77
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
fix(radio-button-group): no longer focus first radio button on label click and adds setFocus
method.
#7050
fix(radio-button-group): no longer focus first radio button on label click and adds setFocus
method.
#7050
Conversation
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 👍
@@ -126,6 +143,20 @@ export class RadioButtonGroup { | |||
} | |||
}; | |||
|
|||
private getFocusableRadioButton(): HTMLCalciteRadioButtonElement | 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.
Could you use the focusFirstTabbable
helper in the dom utils?
This is what the modal does...
@Method()
async setFocus(): Promise<void> {
await componentLoaded(this);
focusFirstTabbable(this.el);
}
Would we want only 1 radio button within a group tabbable at a time? Seems like that might make for a good enhancement in the future.
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.
tried implementing focusFirstTabbable
helper here, it works well if none if the radio-button's in the group are disabled. The reason for this is the way we assign tabIndex at the radio-button
level. The tabIndex=0
only if the radio-button is checked
or if its the first element in the group. I think this is a bug. Wonder if the assignment of tabIndex
should stay at the group level not the radio-button
level.
Would we want only 1 radio button within a group tabbable at a time? Seems like that might make for a good enhancement in the future.
The above is following w3 radio group pattern keyboard specs i believe. One button is tabbable and the user can use Arrow Keys to navigate between the items.
setFocus
method.
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.
comments! :)
@@ -126,6 +143,20 @@ export class RadioButtonGroup { | |||
} | |||
}; | |||
|
|||
private getFocusableRadioButton(): HTMLCalciteRadioButtonElement | 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.
Can you use array.find instead of the while loop?
return this.radioButtons.find((radioButton) => !radioButton.disabled);
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 other option would to be to use the tabbable
dependency to just get the first tabbable element and focus it.
packages/calcite-components/src/components/radio-button-group/radio-button-group.tsx
Show resolved
Hide resolved
packages/calcite-components/src/components/radio-button-group/radio-button-group.tsx
Outdated
Show resolved
Hide resolved
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.
👍
@@ -126,6 +143,11 @@ export class RadioButtonGroup { | |||
} | |||
}; | |||
|
|||
private getFocusableRadioButton(): HTMLCalciteRadioButtonElement | null { | |||
const index = this.radioButtons.findIndex((radiobutton) => !radiobutton.disabled); | |||
return index >= 0 ? this.radioButtons[index] : 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.
@anveshmekala if you're returning the button, why not just use .find()
instead of .findIndex
?
return this.radioButtons.find((radioButton) => !radiobutton.disabled) ?? 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.
my bad, i checked in the wrong commit. .find( )
is the optimal choice here.
@@ -275,7 +278,7 @@ export class RadioButton | |||
if (this.disabled) { | |||
return undefined; | |||
} | |||
return this.checked || this.isDefaultSelectable() ? 0 : -1; | |||
return this.checked || this.isFocusable() ? 0 : -1; |
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.
If a radio-button is added as the first radio-button after all the other radio buttons have already been added to the DOM my guess is that there will be two focusable radio buttons.
We may want to consider creating a registry of radio buttons that are keyed by the name property so that we can ensure only one is ever focusable. @anveshmekala what do you think?
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.
While testing the above edge case found out an issue with the focusable logic. tabIndex=0
is being assigned to all the tabbable radio-buttons if the first radio button is disabled due to async execution of isFocusable
method in each radio-button component.
Regarding the addition of new radio-button after the initial component load, once the above is fixed it wouldn't let two radio-buttons having tabIndex=0
but the first focusable radio-button wont be updated. Using registry may not help since it is specific to each radio-button and any updates (Ex: tabIndex) to the siblings wont trigger updates to the registry.
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 radio button's don't always belong to a group we can't rely on a mutation observer of a parent group so we likely need some kind of map/registry to maintain the active focusable radio button for a specific name
property. This logic would make sure only one radio button with the same name value can be focusable. It should be set/updated anytime a radio-button connectedCallback/disconnectedCallback occurs.
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.
Probably something like...
type RootNode = Document | ShadowRoot;
type ActiveRadioButtonLookup = WeakMap<RootNode, Map<string, HTMLCalciteRadioButtonElement>>;
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.
There is a method queryButtons
in radio-button component which queries all the radio-buttons with a similar name for a given radio-button. This will ensure any mutations to the tabIndex attribute of radio-button will be in the same group.
I tried addressing edge cases with the existing setup but it is breaking other features. Will probably split this PR tomorrow in to individual PR's since the issue we have is related to#7113 and prototype with WeakMap
.
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!
🤖 I have created a release *beep* *boop* --- <details><summary>@esri/calcite-components: 1.4.3</summary> ## [1.4.3](https://github.com/Esri/calcite-components/compare/@esri/calcite-components@1.4.2...@esri/calcite-components@1.4.3) (2023-06-26) ### Bug Fixes * **accordion-item:** support items working across shadowDOM ([#7035](#7035)) ([6378e35](6378e35)), closes [#6167](#6167) * **alert:** Sets autoCloseDuration to "medium" by default ([#7157](#7157)) ([1b9a8ed](1b9a8ed)) * **alert:** Update alert queue when an alert is removed from the DOM ([#7189](#7189)) ([edd59eb](edd59eb)) * **combobox, dropdown, input-date-picker, input-time-picker, popover, tooltip:** Prevent repositioning from affecting other floating components ([#7178](#7178)) ([1b02dae](1b02dae)) * Ensure mouse events are blocked for disabled components in Firefox ([#7107](#7107)) ([271d985](271d985)) * **input-date-picker:** Fix showing the placeholder when resetting the value ([#7156](#7156)) ([8d60ffd](8d60ffd)) * **input, input-number:** Allows numeric characters. ([#7213](#7213)) ([739f0af](739f0af)) * **input,input-number:** Allow typing decimal separator in firefox for arabic locale ([#7173](#7173)) ([595e6f2](595e6f2)) * **list:** No longer has incorrect border width ([a810943](a810943)) * **list:** Update selectedItems property on all item selection changes ([#7204](#7204)) ([da048f6](da048f6)) * **menu-item:** Ensure correct order of rendered icons ([#7098](#7098)) ([fd344e9](fd344e9)), closes [#7097](#7097) * **navigation:** Label is no longer a required property ([#7084](#7084)) ([ba2bd4d](ba2bd4d)) * **radio-button-group:** No longer focus first radio button on label click and adds `setFocus` method. ([#7050](#7050)) ([4267b8c](4267b8c)) * **radio-button, radio-button-group:** Prevent emitting events when selecting a checked radio button ([#7102](#7102)) ([77fcc81](77fcc81)) * **radio-button:** Focuses first focusable radio-button element in group. ([#7152](#7152)) ([dd7ec60](dd7ec60)) * **scrim:** Responsively set the scale of the loading spinner ([#7182](#7182)) ([72c5943](72c5943)) * **tooltip:** Improve component timing ([#7172](#7172)) ([106f5d2](106f5d2)) * **tree-item:** Ensure expanded tree-item is displayed when expanded and made visible ([#7216](#7216)) ([3c0fbf5](3c0fbf5)) <details><summary>@esri/calcite-components-react: 1.4.3</summary> ## [1.4.3](https://github.com/Esri/calcite-components/compare/@esri/calcite-components-react@1.4.2...@esri/calcite-components-react@1.4.3) (2023-06-26) ### Miscellaneous Chores * **@esri/calcite-components-react:** Synchronize undefined versions ### Dependencies * The following workspace dependencies were updated * dependencies * @esri/calcite-components bumped from 1.4.3-next.7 to 1.4.3 </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Ben Elan <no-reply@benelan.dev>
Related Issue: #6698
Summary
This PR will remove the focus flash from the first
calcite-radio-button
in group on label clickAdditional enhancements:
setFocus( )
method incalcite-radio-button-group
Note:
With this PR,
delegateFocus
is removed fromcalcite-radio-button-group
which is causing #6698. Because the tabIndex is set to 0 for the first focusable element on page load to enable keyboard navigation into the group it makesdelegateFocus
to apply focus styles on the first focusable element when the user clicks on the label (invokes .focus( )) resulting in the behavior mentioned in the issue 6698. To resolve this,delegateFocus
is removed and addedsetFocus( )
method.