diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 2e5163329d..1bdfaa8854 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2303,6 +2303,7 @@ export namespace Components { * The name of the control, which is submitted with the form data. */ "name": string; + "setFocus": () => Promise; /** * the value of the radio group. */ diff --git a/core/src/components/radio-group/radio-group.tsx b/core/src/components/radio-group/radio-group.tsx index 5d6036bc84..a8762b5f8a 100644 --- a/core/src/components/radio-group/radio-group.tsx +++ b/core/src/components/radio-group/radio-group.tsx @@ -1,5 +1,5 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; -import { Component, Element, Event, Host, Listen, Prop, Watch, h } from '@stencil/core'; +import { Component, Element, Event, Host, Listen, Method, Prop, Watch, h } from '@stencil/core'; import { renderHiddenInput } from '@utils/helpers'; import { getIonMode } from '../../global/ionic-global'; @@ -217,6 +217,13 @@ export class RadioGroup implements ComponentInterface { } } + /** @internal */ + @Method() + async setFocus() { + const radioToFocus = this.getRadios().find((r) => r.tabIndex !== -1); + radioToFocus?.setFocus(); + } + render() { const { label, labelId, el, name, value } = this; const mode = getIonMode(this); diff --git a/core/src/utils/focus-trap.ts b/core/src/utils/focus-trap.ts index 1ac3d351ff..918516c067 100644 --- a/core/src/utils/focus-trap.ts +++ b/core/src/utils/focus-trap.ts @@ -13,7 +13,7 @@ import { focusVisibleElement } from '@utils/helpers'; * valid usage for the disabled property on ion-button. */ export const focusableQueryString = - '[tabindex]:not([tabindex^="-"]):not([hidden]):not([disabled]), input:not([type=hidden]):not([tabindex^="-"]):not([hidden]):not([disabled]), textarea:not([tabindex^="-"]):not([hidden]):not([disabled]), button:not([tabindex^="-"]):not([hidden]):not([disabled]), select:not([tabindex^="-"]):not([hidden]):not([disabled]), .ion-focusable:not([tabindex^="-"]):not([hidden]):not([disabled]), .ion-focusable[disabled="false"]:not([tabindex^="-"]):not([hidden])'; + '[tabindex]:not([tabindex^="-"]):not([hidden]):not([disabled]), input:not([type=hidden]):not([tabindex^="-"]):not([hidden]):not([disabled]), textarea:not([tabindex^="-"]):not([hidden]):not([disabled]), button:not([tabindex^="-"]):not([hidden]):not([disabled]), select:not([tabindex^="-"]):not([hidden]):not([disabled]), ion-checkbox:not([tabindex^="-"]):not([hidden]):not([disabled]), ion-radio:not([tabindex^="-"]):not([hidden]):not([disabled]), .ion-focusable:not([tabindex^="-"]):not([hidden]):not([disabled]), .ion-focusable[disabled="false"]:not([tabindex^="-"]):not([hidden])'; /** * Focuses the first descendant in a context @@ -78,7 +78,13 @@ const focusElementInContext = ( } if (elementToFocus) { - focusVisibleElement(elementToFocus); + const radioGroup = elementToFocus.closest('ion-radio-group'); + + if (radioGroup) { + radioGroup.setFocus(); + } else { + focusVisibleElement(elementToFocus); + } } else { // Focus fallback element instead of letting focus escape fallbackElement.focus();