diff --git a/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.spec.ts b/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.spec.ts index 41518181c1fd..6040750afb3e 100644 --- a/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.spec.ts +++ b/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.spec.ts @@ -1,6 +1,6 @@ import {TestBed, inject, fakeAsync} from '@angular/core/testing'; import {Component, NgModule} from '@angular/core'; -import {dispatchMouseEvent} from '@angular/cdk/testing/private'; +import {dispatchFakeEvent, dispatchMouseEvent} from '@angular/cdk/testing/private'; import {OverlayModule, OverlayContainer, Overlay} from '../index'; import {OverlayOutsideClickDispatcher} from './overlay-outside-click-dispatcher'; import {ComponentPortal} from '@angular/cdk/portal'; @@ -51,9 +51,7 @@ describe('OverlayOutsideClickDispatcher', () => { overlayTwo.dispose(); }); - it( - 'should dispatch mouse click events to the attached overlays', - () => { + it('should dispatch mouse click events to the attached overlays', () => { const overlayOne = overlay.create(); overlayOne.attach(new ComponentPortal(TestComponent)); const overlayTwo = overlay.create(); @@ -80,27 +78,53 @@ describe('OverlayOutsideClickDispatcher', () => { overlayTwo.dispose(); }); - it( - 'should dispatch mouse click events to the attached overlays even when propagation is stopped', - () => { - const overlayRef = overlay.create(); - overlayRef.attach(new ComponentPortal(TestComponent)); - const spy = jasmine.createSpy('overlay mouse click event spy'); - overlayRef.outsidePointerEvents().subscribe(spy); + it('should dispatch auxiliary button click events to the attached overlays', () => { + const overlayOne = overlay.create(); + overlayOne.attach(new ComponentPortal(TestComponent)); + const overlayTwo = overlay.create(); + overlayTwo.attach(new ComponentPortal(TestComponent)); - outsideClickDispatcher.add(overlayRef); + const overlayOneSpy = jasmine.createSpy('overlayOne auxiliary click event spy'); + const overlayTwoSpy = jasmine.createSpy('overlayTwo auxiliary click event spy'); + + overlayOne.outsidePointerEvents().subscribe(overlayOneSpy); + overlayTwo.outsidePointerEvents().subscribe(overlayTwoSpy); + + outsideClickDispatcher.add(overlayOne); + outsideClickDispatcher.add(overlayTwo); const button = document.createElement('button'); document.body.appendChild(button); - button.addEventListener('click', event => event.stopPropagation()); - button.click(); + dispatchFakeEvent(button, 'auxclick'); - expect(spy).toHaveBeenCalled(); + expect(overlayOneSpy).toHaveBeenCalled(); + expect(overlayTwoSpy).toHaveBeenCalled(); button.parentNode!.removeChild(button); - overlayRef.dispose(); + overlayOne.dispose(); + overlayTwo.dispose(); }); + it('should dispatch mouse click events to the attached overlays even when propagation is stopped', + () => { + const overlayRef = overlay.create(); + overlayRef.attach(new ComponentPortal(TestComponent)); + const spy = jasmine.createSpy('overlay mouse click event spy'); + overlayRef.outsidePointerEvents().subscribe(spy); + + outsideClickDispatcher.add(overlayRef); + + const button = document.createElement('button'); + document.body.appendChild(button); + button.addEventListener('click', event => event.stopPropagation()); + button.click(); + + expect(spy).toHaveBeenCalled(); + + button.parentNode!.removeChild(button); + overlayRef.dispose(); + }); + it('should dispose of the global click event handler correctly', () => { const overlayRef = overlay.create(); const body = document.body; @@ -191,10 +215,8 @@ describe('OverlayOutsideClickDispatcher', () => { overlayRef.dispose(); }); - it( - 'should not throw an error when when closing out related components via the' + - ' outsidePointerEvents emitter on background click', - fakeAsync(() => { + it('should not throw an error when when closing out related components via the ' + + 'outsidePointerEvents emitter on background click', fakeAsync(() => { const firstOverlayRef = overlay.create(); firstOverlayRef.attach(new ComponentPortal(TestComponent)); const secondOverlayRef = overlay.create(); diff --git a/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.ts b/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.ts index a736beab8b26..6880f810c68b 100644 --- a/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.ts +++ b/src/cdk/overlay/dispatchers/overlay-outside-click-dispatcher.ts @@ -30,23 +30,23 @@ export class OverlayOutsideClickDispatcher extends BaseOverlayDispatcher { add(overlayRef: OverlayReference): void { super.add(overlayRef); - // tslint:disable: max-line-length // Safari on iOS does not generate click events for non-interactive // elements. However, we want to receive a click for any element outside // the overlay. We can force a "clickable" state by setting - // `cursor: pointer` on the document body. - // See https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile - // and https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html - // tslint:enable: max-line-length + // `cursor: pointer` on the document body. See: + // https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile + // https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html if (!this._isAttached) { - this._document.body.addEventListener('click', this._clickListener, true); - this._document.body.addEventListener('contextmenu', this._clickListener, true); + const body = this._document.body; + body.addEventListener('click', this._clickListener, true); + body.addEventListener('auxclick', this._clickListener, true); + body.addEventListener('contextmenu', this._clickListener, true); // click event is not fired on iOS. To make element "clickable" we are // setting the cursor to pointer if (this._platform.IOS && !this._cursorStyleIsSet) { - this._cursorOriginalValue = this._document.body.style.cursor; - this._document.body.style.cursor = 'pointer'; + this._cursorOriginalValue = body.style.cursor; + body.style.cursor = 'pointer'; this._cursorStyleIsSet = true; } @@ -57,10 +57,12 @@ export class OverlayOutsideClickDispatcher extends BaseOverlayDispatcher { /** Detaches the global keyboard event listener. */ protected detach() { if (this._isAttached) { - this._document.body.removeEventListener('click', this._clickListener, true); - this._document.body.removeEventListener('contextmenu', this._clickListener, true); + const body = this._document.body; + body.removeEventListener('click', this._clickListener, true); + body.removeEventListener('auxclick', this._clickListener, true); + body.removeEventListener('contextmenu', this._clickListener, true); if (this._platform.IOS && this._cursorStyleIsSet) { - this._document.body.style.cursor = this._cursorOriginalValue; + body.style.cursor = this._cursorOriginalValue; this._cursorStyleIsSet = false; } this._isAttached = false;