Skip to content

Commit 46b918e

Browse files
authored
fix(aria/menu): focus not moved into menu if items are delayed (#33743)
Fixes that if the items aren't available immediately on open (e.g. when they're in an overlay), the Aria menu fails to move focus into the first item. Fixes #33742.
1 parent bcb3e79 commit 46b918e

2 files changed

Lines changed: 12 additions & 6 deletions

File tree

‎goldens/aria/private/index.api.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,6 @@ export class MenuPattern<V> {
439439
_clearTimeouts(): void;
440440
close(): void;
441441
closeAll(): void;
442-
_closeTimeout: any;
443442
collapse(): void;
444443
readonly disabled: () => boolean;
445444
readonly dynamicSpaceKey: SignalLike<"" | " ">;
@@ -451,6 +450,7 @@ export class MenuPattern<V> {
451450
// (undocumented)
452451
readonly inputs: MenuInputs<V>;
453452
readonly isFocused: WritableSignalLike<boolean>;
453+
readonly items: () => MenuItemPattern<V>[];
454454
readonly keydownManager: SignalLike<KeyboardEventManager<KeyboardEvent>>;
455455
last(): void;
456456
readonly listBehavior: List<MenuItemPattern<V>, V | undefined>;
@@ -461,7 +461,6 @@ export class MenuPattern<V> {
461461
onKeydown(event: KeyboardEvent): void;
462462
onMouseOut(event: MouseEvent): void;
463463
onMouseOver(event: MouseEvent): void;
464-
_openTimeout: any;
465464
prev(): void;
466465
readonly role: () => string;
467466
readonly root: SignalLike<MenuTriggerPattern<V> | MenuBarPattern<V> | MenuPattern<V> | undefined>;

‎src/aria/private/menu/menu.ts‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,14 @@ export class MenuPattern<V> {
105105
/** Whether the menu trigger has been hovered. */
106106
readonly hasBeenHovered = signal(false);
107107

108+
/** Items currently inside the menu. */
109+
readonly items = () => this.inputs.items();
110+
108111
/** Timeout used to open sub-menus on hover. */
109-
_openTimeout: any;
112+
private _openTimeout: ReturnType<typeof setTimeout> | undefined;
110113

111114
/** Timeout used to close sub-menus on hover out. */
112-
_closeTimeout: any;
115+
private _closeTimeout: ReturnType<typeof setTimeout> | undefined;
113116

114117
/** The tab index of the menu. */
115118
readonly tabIndex = () => this.listBehavior.tabIndex();
@@ -246,7 +249,7 @@ export class MenuPattern<V> {
246249
}
247250

248251
const parent = this.inputs.parent();
249-
const activeItem = this?.inputs.activeItem();
252+
const activeItem = this.inputs.activeItem();
250253

251254
if (parent instanceof MenuItemPattern) {
252255
const grandparent = parent.inputs.parent();
@@ -689,7 +692,11 @@ export class MenuTriggerPattern<V> {
689692
pendingFocusEffect(): void {
690693
const menu = this.inputs.menu();
691694
const intent = this.pendingFocus();
692-
if (menu && intent) {
695+
const items = menu?.items();
696+
697+
// We check the items so that we don't try calling into
698+
// `first/last` until the items are actually available.
699+
if (menu && intent && items?.length) {
693700
if (intent === 'first') {
694701
menu.first();
695702
} else if (intent === 'last') {

0 commit comments

Comments
 (0)