Skip to content

Commit b6e3b41

Browse files
committed
fixup! fix(cdk-experimental/menu): refocus trigger after closeAll
1 parent 530fd07 commit b6e3b41

File tree

5 files changed

+43
-27
lines changed

5 files changed

+43
-27
lines changed

src/cdk-experimental/menu/menu-base.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,17 @@ export abstract class CdkMenuBase
111111

112112
/**
113113
* Close the open menu if the current active item opened the requested MenuStackItem.
114-
* @param item the MenuStackItem requested to be closed.
114+
* @param menu the MenuStackItem requested to be closed.
115+
* @param focusParentTrigger whether to focus the parent trigger after closing the menu.
115116
*/
116-
protected closeOpenMenu(menu: MenuStackItem | undefined, focusTrigger?: boolean) {
117+
protected closeOpenMenu(menu?: MenuStackItem, focusParentTrigger?: boolean) {
117118
const keyManager = this.keyManager;
118119
const trigger = this.openItem;
119120
if (menu === trigger?.getMenuTrigger()?.getMenu()) {
120121
trigger?.getMenuTrigger()?.close();
121122
// If the user has moused over a sibling item we want to focus the element under mouse focus
122123
// not the trigger which previously opened the now closed menu.
123-
if (focusTrigger) {
124+
if (focusParentTrigger) {
124125
if (trigger) {
125126
keyManager.setActiveItem(trigger);
126127
} else {
@@ -169,7 +170,7 @@ export abstract class CdkMenuBase
169170
private _subscribeToMenuStackClosed() {
170171
this.menuStack.closed
171172
.pipe(takeUntil(this.destroyed))
172-
.subscribe(({item, focusParentMenu}) => this.closeOpenMenu(item, focusParentMenu));
173+
.subscribe(({item, focusParentTrigger}) => this.closeOpenMenu(item, focusParentTrigger));
173174
}
174175

175176
private _subscribeToHasFocus() {

src/cdk-experimental/menu/menu-item-trigger.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,8 @@ export class CdkMenuItemTrigger extends MenuTrigger implements OnDestroy {
325325

326326
private _subscribeToMenuStackClosed() {
327327
if (!this._parentMenu) {
328-
this.menuStack.closed.subscribe(({focusParentMenu}) => {
329-
if (focusParentMenu && !this.menuStack.length()) {
328+
this.menuStack.closed.subscribe(({focusParentTrigger}) => {
329+
if (focusParentTrigger && !this.menuStack.length()) {
330330
this._elementRef.nativeElement.focus();
331331
}
332332
});

src/cdk-experimental/menu/menu-item.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
140140
trigger() {
141141
if (!this.disabled && !this.hasMenu()) {
142142
this.triggered.next();
143-
this._menuStack.closeAll(undefined, true);
143+
this._menuStack.closeAll({focusParentTrigger: true});
144144
}
145145
}
146146

@@ -212,14 +212,17 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
212212
const parentMenu = this._parentMenu!;
213213
if (this._menuStack.hasInlineMenu() || this._menuStack.length() > 1) {
214214
event.preventDefault();
215-
this._menuStack.close(parentMenu, FocusNext.previousItem, true);
215+
this._menuStack.close(parentMenu, {
216+
focusNextOnEmpty: FocusNext.previousItem,
217+
focusParentTrigger: true,
218+
});
216219
}
217220
}
218221

219222
private _forwardArrowPressed(event: KeyboardEvent) {
220223
if (!this.hasMenu() && this._menuStack.hasInlineMenu()) {
221224
event.preventDefault();
222-
this._menuStack.closeAll(FocusNext.nextItem, true);
225+
this._menuStack.closeAll({focusNextOnEmpty: FocusNext.nextItem, focusParentTrigger: true});
223226
}
224227
}
225228

src/cdk-experimental/menu/menu-stack.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ export const enum FocusNext {
1717
currentItem,
1818
}
1919

20-
/**
21-
* Interface for the elements tracked in the MenuStack.
22-
*/
20+
/** Interface for the elements tracked in the MenuStack. */
2321
export interface MenuStackItem {
2422
/** A reference to the previous Menus MenuStack instance. */
2523
menuStack?: MenuStack;
@@ -42,9 +40,20 @@ export const PARENT_OR_NEW_INLINE_MENU_STACK_PROVIDER = {
4240
useFactory: (parentMenuStack?: MenuStack) => parentMenuStack || MenuStack.inline(),
4341
};
4442

43+
/** Options that can be provided to the close or closeAll methods. */
44+
export interface CloseOptions {
45+
/** The element to focus next if the close operation causes the menu stack to become empty. */
46+
focusNextOnEmpty?: FocusNext;
47+
/** Whether to focus the parent trigger after closing the menu. */
48+
focusParentTrigger?: boolean;
49+
}
50+
51+
/** Event dispatched when a menu is closed. */
4552
export interface MenuStackCloseEvent {
53+
/** The menu being closed. */
4654
item: MenuStackItem | undefined;
47-
focusParentMenu?: boolean;
55+
/** Whether to focus the parent trigger after closing the menu. */
56+
focusParentTrigger?: boolean;
4857
}
4958

5059
/**
@@ -99,21 +108,19 @@ export class MenuStack {
99108
* Pop items off of the stack up to and including `lastItem` and emit each on the close
100109
* observable. If the stack is empty or `lastItem` is not on the stack it does nothing.
101110
* @param lastItem the last item to pop off the stack.
102-
* @param focusNext the event to emit on the `empty` observable if the method call resulted in an
103-
* empty stack. Does not emit if the stack was initially empty or if `lastItem` was not on the
104-
* @param focusParentMenu Whether to focus the parent menu after closing current one.
105-
* stack.
111+
* @param options Options that configure behavior on close.
106112
*/
107-
close(lastItem: MenuStackItem, focusNext?: FocusNext, focusParentMenu = false) {
113+
close(lastItem: MenuStackItem, options?: CloseOptions) {
114+
const {focusNextOnEmpty, focusParentTrigger} = {...options};
108115
if (this._elements.indexOf(lastItem) >= 0) {
109116
let poppedElement: MenuStackItem | undefined;
110117
do {
111118
poppedElement = this._elements.pop();
112-
this._close.next({item: poppedElement, focusParentMenu});
119+
this._close.next({item: poppedElement, focusParentTrigger});
113120
} while (poppedElement !== lastItem);
114121

115122
if (this.isEmpty()) {
116-
this._empty.next(focusNext);
123+
this._empty.next(focusNextOnEmpty);
117124
}
118125
}
119126
}
@@ -137,18 +144,18 @@ export class MenuStack {
137144

138145
/**
139146
* Pop off all MenuStackItems and emit each one on the `close` observable one by one.
140-
* @param focusNext the event to emit on the `empty` observable once the stack is emptied. Does
141-
* not emit if the stack was initially empty.
147+
* @param options Options that configure behavior on close.
142148
*/
143-
closeAll(focusNext?: FocusNext, focusParentMenu = false) {
149+
closeAll(options?: CloseOptions) {
150+
const {focusNextOnEmpty, focusParentTrigger} = {...options};
144151
if (!this.isEmpty()) {
145152
while (!this.isEmpty()) {
146153
const menuStackItem = this._elements.pop();
147154
if (menuStackItem) {
148-
this._close.next({item: menuStackItem, focusParentMenu});
155+
this._close.next({item: menuStackItem, focusParentTrigger});
149156
}
150157
}
151-
this._empty.next(focusNext);
158+
this._empty.next(focusNextOnEmpty);
152159
}
153160
}
154161

@@ -167,10 +174,12 @@ export class MenuStack {
167174
return this._elements[this._elements.length - 1];
168175
}
169176

177+
/** Whether the menu stack is associated with an inline menu. */
170178
hasInlineMenu() {
171179
return this._hasInlineMenu;
172180
}
173181

182+
/** Sets whether the menu stack contains the focused element. */
174183
setHasFocus(hasFocus: boolean) {
175184
this._hasFocus.next(hasFocus);
176185
}

src/cdk-experimental/menu/menu.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,15 @@ export class CdkMenu extends CdkMenuBase implements AfterContentInit, OnDestroy
131131
case ESCAPE:
132132
if (!hasModifierKey(event)) {
133133
event.preventDefault();
134-
this.menuStack.close(this, FocusNext.currentItem, true);
134+
this.menuStack.close(this, {
135+
focusNextOnEmpty: FocusNext.currentItem,
136+
focusParentTrigger: true,
137+
});
135138
}
136139
break;
137140

138141
case TAB:
139-
this.menuStack.closeAll(undefined, true);
142+
this.menuStack.closeAll({focusParentTrigger: true});
140143
break;
141144

142145
default:

0 commit comments

Comments
 (0)