Skip to content

Commit 0dbe23b

Browse files
authored
fix(menu): allow focus to be moved inside menuClosed event (#18756)
The `menuClosed` event is emitted before focus has been restored to the trigger which means that any change that the consumer has made will be overwritten. These changes move the menu's focus restorating slightly earlier in the chain.
1 parent 1a3c35a commit 0dbe23b

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

src/material-experimental/mdc-menu/menu.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,28 @@ describe('MDC-based MatMenu', () => {
188188
expect(document.activeElement).not.toBe(triggerEl);
189189
}));
190190

191+
it('should be able to move focus in the closed event', fakeAsync(() => {
192+
const fixture = createComponent(SimpleMenu, [], [FakeIcon]);
193+
const instance = fixture.componentInstance;
194+
fixture.detectChanges();
195+
const triggerEl = instance.triggerEl.nativeElement;
196+
const button = document.createElement('button');
197+
button.setAttribute('tabindex', '0');
198+
document.body.appendChild(button);
199+
200+
triggerEl.click();
201+
fixture.detectChanges();
202+
203+
const subscription = instance.trigger.menuClosed.subscribe(() => button.focus());
204+
instance.trigger.closeMenu();
205+
fixture.detectChanges();
206+
tick(500);
207+
208+
expect(document.activeElement).toBe(button);
209+
document.body.removeChild(button);
210+
subscription.unsubscribe();
211+
}));
212+
191213
it('should restore focus to the trigger immediately once the menu is closed', () => {
192214
const fixture = createComponent(SimpleMenu, [], [FakeIcon]);
193215
fixture.detectChanges();

src/material/menu/menu-trigger.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,9 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
277277
}
278278

279279
const menu = this.menu;
280-
281280
this._closingActionsSubscription.unsubscribe();
282281
this._overlayRef.detach();
282+
this._restoreFocus();
283283

284284
if (menu instanceof MatMenu) {
285285
menu._resetAnimation();
@@ -308,8 +308,6 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
308308
menu.lazyContent.detach();
309309
}
310310
}
311-
312-
this._restoreFocus();
313311
}
314312

315313
/**

src/material/menu/menu.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,28 @@ describe('MatMenu', () => {
190190
expect(document.activeElement).not.toBe(triggerEl);
191191
}));
192192

193+
it('should be able to move focus in the closed event', fakeAsync(() => {
194+
const fixture = createComponent(SimpleMenu, [], [FakeIcon]);
195+
const instance = fixture.componentInstance;
196+
fixture.detectChanges();
197+
const triggerEl = instance.triggerEl.nativeElement;
198+
const button = document.createElement('button');
199+
button.setAttribute('tabindex', '0');
200+
document.body.appendChild(button);
201+
202+
triggerEl.click();
203+
fixture.detectChanges();
204+
205+
const subscription = instance.trigger.menuClosed.subscribe(() => button.focus());
206+
instance.trigger.closeMenu();
207+
fixture.detectChanges();
208+
tick(500);
209+
210+
expect(document.activeElement).toBe(button);
211+
document.body.removeChild(button);
212+
subscription.unsubscribe();
213+
}));
214+
193215
it('should restore focus to the trigger immediately once the menu is closed', () => {
194216
const fixture = createComponent(SimpleMenu, [], [FakeIcon]);
195217
fixture.detectChanges();

0 commit comments

Comments
 (0)