Skip to content

Commit

Permalink
fix(focusVisibleElement): set focus on custom appRootSelector
Browse files Browse the repository at this point in the history
  • Loading branch information
thetaPC committed Feb 26, 2025
1 parent fc552ad commit cf44ede
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
5 changes: 2 additions & 3 deletions core/src/components/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Method, h } from '@stencil/core';
import { getOrInitFocusVisibleUtility } from '@utils/focus-visible';
import { focusElements } from '@utils/focus-visible';

import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global';
Expand All @@ -27,8 +27,7 @@ export class App implements ComponentInterface {
*/
@Method()
async setFocus(elements: HTMLElement[]) {
const focusVisible = getOrInitFocusVisibleUtility();
focusVisible.setFocus(elements);
focusElements(elements);
}

render() {
Expand Down
14 changes: 14 additions & 0 deletions core/src/utils/focus-visible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ export const getOrInitFocusVisibleUtility = () => {
return focusVisibleUtility;
};

/**
* Used to set focus on an element that uses `ion-focusable`.
* Do not use this if focusing the element as a result of a keyboard
* event as the focus utility should handle this for us. This method
* should be used when we want to programmatically focus an element as
* a result of another user action. (Ex: We focus the first element
* inside of a popover when the user presents it, but the popover is not always
* presented as a result of keyboard action.)
*/
export const focusElements = (elements: Element[]) => {
const focusVisible = getOrInitFocusVisibleUtility();
focusVisible.setFocus(elements);
};

export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
let currentFocus: Element[] = [];
let keyboardMode = true;
Expand Down
24 changes: 22 additions & 2 deletions core/src/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { EventEmitter } from '@stencil/core';
import { focusElements } from '@utils/focus-visible';

import type { Side } from '../components/menu/menu-interface';
import { config } from '../global/config';
Expand Down Expand Up @@ -267,10 +268,29 @@ export const focusVisibleElement = (el: HTMLElement) => {
* which will let us explicitly set the elements to focus.
*/
if (el.classList.contains('ion-focusable')) {
const appRootSelector = config.get('appRootSelector', 'ion-app');
const appRootSelector: string = config.get('appRootSelector', 'ion-app');
const app = el.closest(appRootSelector) as HTMLIonAppElement | null;
if (app) {
app.setFocus([el]);
if (appRootSelector === 'ion-app') {
/**
* If the app root is the default, then it will be
* in charge of setting focus. This is because the
* focus-visible utility is attached to the app root
* and will handle setting focus on the correct element.
*/
app.setFocus([el]);
} else {
/**
* If the user has provided a custom app root selector,
* then we need to manually set focus on the element
* since the focus-visible utility will not be available
* on the custom app root.
*
* The focus-visible utility is used to set focus on an
* element that uses `ion-focusable`.
*/
focusElements([el]);
}
}
}
};
Expand Down

0 comments on commit cf44ede

Please sign in to comment.