fix(radio-button-group): no longer focus first radio button on label click and adds setFocus method.#7050
Conversation
| } | ||
| }; | ||
|
|
||
| private getFocusableRadioButton(): HTMLCalciteRadioButtonElement | null { |
There was a problem hiding this comment.
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.
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.
| } | ||
| }; | ||
|
|
||
| private getFocusableRadioButton(): HTMLCalciteRadioButtonElement | null { |
There was a problem hiding this comment.
Can you use array.find instead of the while loop?
return this.radioButtons.find((radioButton) => !radioButton.disabled);There was a problem hiding this comment.
The other option would to be to use the tabbable dependency to just get the first tabbable element and focus it.
|
|
||
| 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.
@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.
my bad, i checked in the wrong commit. .find( ) is the optimal choice here.
| return undefined; | ||
| } | ||
| return this.checked || this.isDefaultSelectable() ? 0 : -1; | ||
| return this.checked || this.isFocusable() ? 0 : -1; |
There was a problem hiding this comment.
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.
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.
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.
Probably something like...
type RootNode = Document | ShadowRoot;
type ActiveRadioButtonLookup = WeakMap<RootNode, Map<string, HTMLCalciteRadioButtonElement>>;There was a problem hiding this comment.
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.
🤖 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-buttonin group on label clickAdditional enhancements:
setFocus( )method incalcite-radio-button-groupNote:
With this PR,
delegateFocusis removed fromcalcite-radio-button-groupwhich 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 makesdelegateFocusto 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,delegateFocusis removed and addedsetFocus( )method.