Skip to content

Commit 65358a9

Browse files
authored
fix(cdk/overlay): not emitting for auxiliar button clicks (#22616)
Similar to #21397. Fixes that the `OverlayOutsideClickDispatcher` doesn't emit when the user presses outside using the middle or right mouse button.
1 parent 706fc48 commit 65358a9

File tree

2 files changed

+56
-32
lines changed

2 files changed

+56
-32
lines changed

src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.spec.ts

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {TestBed, inject, fakeAsync} from '@angular/core/testing';
22
import {Component, NgModule} from '@angular/core';
3-
import {dispatchMouseEvent} from '@angular/cdk/testing/private';
3+
import {dispatchFakeEvent, dispatchMouseEvent} from '@angular/cdk/testing/private';
44
import {OverlayModule, OverlayContainer, Overlay} from '../index';
55
import {OverlayOutsideClickDispatcher} from './overlay-outside-click-dispatcher';
66
import {ComponentPortal} from '@angular/cdk/portal';
@@ -51,9 +51,7 @@ describe('OverlayOutsideClickDispatcher', () => {
5151
overlayTwo.dispose();
5252
});
5353

54-
it(
55-
'should dispatch mouse click events to the attached overlays',
56-
() => {
54+
it('should dispatch mouse click events to the attached overlays', () => {
5755
const overlayOne = overlay.create();
5856
overlayOne.attach(new ComponentPortal(TestComponent));
5957
const overlayTwo = overlay.create();
@@ -80,27 +78,53 @@ describe('OverlayOutsideClickDispatcher', () => {
8078
overlayTwo.dispose();
8179
});
8280

83-
it(
84-
'should dispatch mouse click events to the attached overlays even when propagation is stopped',
85-
() => {
86-
const overlayRef = overlay.create();
87-
overlayRef.attach(new ComponentPortal(TestComponent));
88-
const spy = jasmine.createSpy('overlay mouse click event spy');
89-
overlayRef.outsidePointerEvents().subscribe(spy);
81+
it('should dispatch auxiliary button click events to the attached overlays', () => {
82+
const overlayOne = overlay.create();
83+
overlayOne.attach(new ComponentPortal(TestComponent));
84+
const overlayTwo = overlay.create();
85+
overlayTwo.attach(new ComponentPortal(TestComponent));
9086

91-
outsideClickDispatcher.add(overlayRef);
87+
const overlayOneSpy = jasmine.createSpy('overlayOne auxiliary click event spy');
88+
const overlayTwoSpy = jasmine.createSpy('overlayTwo auxiliary click event spy');
89+
90+
overlayOne.outsidePointerEvents().subscribe(overlayOneSpy);
91+
overlayTwo.outsidePointerEvents().subscribe(overlayTwoSpy);
92+
93+
outsideClickDispatcher.add(overlayOne);
94+
outsideClickDispatcher.add(overlayTwo);
9295

9396
const button = document.createElement('button');
9497
document.body.appendChild(button);
95-
button.addEventListener('click', event => event.stopPropagation());
96-
button.click();
98+
dispatchFakeEvent(button, 'auxclick');
9799

98-
expect(spy).toHaveBeenCalled();
100+
expect(overlayOneSpy).toHaveBeenCalled();
101+
expect(overlayTwoSpy).toHaveBeenCalled();
99102

100103
button.parentNode!.removeChild(button);
101-
overlayRef.dispose();
104+
overlayOne.dispose();
105+
overlayTwo.dispose();
102106
});
103107

108+
it('should dispatch mouse click events to the attached overlays even when propagation is stopped',
109+
() => {
110+
const overlayRef = overlay.create();
111+
overlayRef.attach(new ComponentPortal(TestComponent));
112+
const spy = jasmine.createSpy('overlay mouse click event spy');
113+
overlayRef.outsidePointerEvents().subscribe(spy);
114+
115+
outsideClickDispatcher.add(overlayRef);
116+
117+
const button = document.createElement('button');
118+
document.body.appendChild(button);
119+
button.addEventListener('click', event => event.stopPropagation());
120+
button.click();
121+
122+
expect(spy).toHaveBeenCalled();
123+
124+
button.parentNode!.removeChild(button);
125+
overlayRef.dispose();
126+
});
127+
104128
it('should dispose of the global click event handler correctly', () => {
105129
const overlayRef = overlay.create();
106130
const body = document.body;
@@ -191,10 +215,8 @@ describe('OverlayOutsideClickDispatcher', () => {
191215
overlayRef.dispose();
192216
});
193217

194-
it(
195-
'should not throw an error when when closing out related components via the' +
196-
' outsidePointerEvents emitter on background click',
197-
fakeAsync(() => {
218+
it('should not throw an error when when closing out related components via the ' +
219+
'outsidePointerEvents emitter on background click', fakeAsync(() => {
198220
const firstOverlayRef = overlay.create();
199221
firstOverlayRef.attach(new ComponentPortal(TestComponent));
200222
const secondOverlayRef = overlay.create();

src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ export class OverlayOutsideClickDispatcher extends BaseOverlayDispatcher {
3030
add(overlayRef: OverlayReference): void {
3131
super.add(overlayRef);
3232

33-
// tslint:disable: max-line-length
3433
// Safari on iOS does not generate click events for non-interactive
3534
// elements. However, we want to receive a click for any element outside
3635
// the overlay. We can force a "clickable" state by setting
37-
// `cursor: pointer` on the document body.
38-
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile
39-
// and https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
40-
// tslint:enable: max-line-length
36+
// `cursor: pointer` on the document body. See:
37+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile
38+
// https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
4139
if (!this._isAttached) {
42-
this._document.body.addEventListener('click', this._clickListener, true);
43-
this._document.body.addEventListener('contextmenu', this._clickListener, true);
40+
const body = this._document.body;
41+
body.addEventListener('click', this._clickListener, true);
42+
body.addEventListener('auxclick', this._clickListener, true);
43+
body.addEventListener('contextmenu', this._clickListener, true);
4444

4545
// click event is not fired on iOS. To make element "clickable" we are
4646
// setting the cursor to pointer
4747
if (this._platform.IOS && !this._cursorStyleIsSet) {
48-
this._cursorOriginalValue = this._document.body.style.cursor;
49-
this._document.body.style.cursor = 'pointer';
48+
this._cursorOriginalValue = body.style.cursor;
49+
body.style.cursor = 'pointer';
5050
this._cursorStyleIsSet = true;
5151
}
5252

@@ -57,10 +57,12 @@ export class OverlayOutsideClickDispatcher extends BaseOverlayDispatcher {
5757
/** Detaches the global keyboard event listener. */
5858
protected detach() {
5959
if (this._isAttached) {
60-
this._document.body.removeEventListener('click', this._clickListener, true);
61-
this._document.body.removeEventListener('contextmenu', this._clickListener, true);
60+
const body = this._document.body;
61+
body.removeEventListener('click', this._clickListener, true);
62+
body.removeEventListener('auxclick', this._clickListener, true);
63+
body.removeEventListener('contextmenu', this._clickListener, true);
6264
if (this._platform.IOS && this._cursorStyleIsSet) {
63-
this._document.body.style.cursor = this._cursorOriginalValue;
65+
body.style.cursor = this._cursorOriginalValue;
6466
this._cursorStyleIsSet = false;
6567
}
6668
this._isAttached = false;

0 commit comments

Comments
 (0)