Skip to content

Commit cf44ede

Browse files
committed
fix(focusVisibleElement): set focus on custom appRootSelector
1 parent fc552ad commit cf44ede

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

core/src/components/app/app.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ComponentInterface } from '@stencil/core';
22
import { Component, Element, Host, Method, h } from '@stencil/core';
3-
import { getOrInitFocusVisibleUtility } from '@utils/focus-visible';
3+
import { focusElements } from '@utils/focus-visible';
44

55
import { config } from '../../global/config';
66
import { getIonTheme } from '../../global/ionic-global';
@@ -27,8 +27,7 @@ export class App implements ComponentInterface {
2727
*/
2828
@Method()
2929
async setFocus(elements: HTMLElement[]) {
30-
const focusVisible = getOrInitFocusVisibleUtility();
31-
focusVisible.setFocus(elements);
30+
focusElements(elements);
3231
}
3332

3433
render() {

core/src/utils/focus-visible.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ export const getOrInitFocusVisibleUtility = () => {
3030
return focusVisibleUtility;
3131
};
3232

33+
/**
34+
* Used to set focus on an element that uses `ion-focusable`.
35+
* Do not use this if focusing the element as a result of a keyboard
36+
* event as the focus utility should handle this for us. This method
37+
* should be used when we want to programmatically focus an element as
38+
* a result of another user action. (Ex: We focus the first element
39+
* inside of a popover when the user presents it, but the popover is not always
40+
* presented as a result of keyboard action.)
41+
*/
42+
export const focusElements = (elements: Element[]) => {
43+
const focusVisible = getOrInitFocusVisibleUtility();
44+
focusVisible.setFocus(elements);
45+
};
46+
3347
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
3448
let currentFocus: Element[] = [];
3549
let keyboardMode = true;

core/src/utils/helpers.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { EventEmitter } from '@stencil/core';
2+
import { focusElements } from '@utils/focus-visible';
23

34
import type { Side } from '../components/menu/menu-interface';
45
import { config } from '../global/config';
@@ -267,10 +268,29 @@ export const focusVisibleElement = (el: HTMLElement) => {
267268
* which will let us explicitly set the elements to focus.
268269
*/
269270
if (el.classList.contains('ion-focusable')) {
270-
const appRootSelector = config.get('appRootSelector', 'ion-app');
271+
const appRootSelector: string = config.get('appRootSelector', 'ion-app');
271272
const app = el.closest(appRootSelector) as HTMLIonAppElement | null;
272273
if (app) {
273-
app.setFocus([el]);
274+
if (appRootSelector === 'ion-app') {
275+
/**
276+
* If the app root is the default, then it will be
277+
* in charge of setting focus. This is because the
278+
* focus-visible utility is attached to the app root
279+
* and will handle setting focus on the correct element.
280+
*/
281+
app.setFocus([el]);
282+
} else {
283+
/**
284+
* If the user has provided a custom app root selector,
285+
* then we need to manually set focus on the element
286+
* since the focus-visible utility will not be available
287+
* on the custom app root.
288+
*
289+
* The focus-visible utility is used to set focus on an
290+
* element that uses `ion-focusable`.
291+
*/
292+
focusElements([el]);
293+
}
274294
}
275295
}
276296
};

0 commit comments

Comments
 (0)