Skip to content

Commit af12c9c

Browse files
committed
fix(overlay): overlay directives not emitting when detached externally
Currently the `ConnectedOverlayDirective` only emits the `detach` event when it _thinks_ that the overlay is detached (escape press, backdrop click etc.), but this won't necessarily be correct (e.g. when it was closed by a scroll strategy). These changes refactor the outputs to always be one-to-one with the `OverlayRef` detachments.
1 parent 24f0471 commit af12c9c

File tree

2 files changed

+42
-11
lines changed

2 files changed

+42
-11
lines changed

src/cdk/overlay/overlay-directives.spec.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@ import {CdkConnectedOverlay, OverlayModule, CdkOverlayOrigin} from './index';
88
import {OverlayContainer} from './overlay-container';
99
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
1010
import {ConnectedOverlayPositionChange} from './position/connected-position';
11+
import {ScrollDispatcher} from '@angular/cdk/scrolling';
12+
import {Overlay} from './overlay';
13+
import {ScrollStrategy} from './scroll/index';
14+
import {Subject} from 'rxjs/Subject';
1115

1216

1317
describe('Overlay directives', () => {
1418
let overlayContainer: OverlayContainer;
1519
let overlayContainerElement: HTMLElement;
1620
let fixture: ComponentFixture<ConnectedOverlayDirectiveTest>;
1721
let dir: {value: string};
22+
let scrolledSubject = new Subject();
1823

1924
beforeEach(() => {
2025
TestBed.configureTestingModule({
@@ -23,7 +28,10 @@ describe('Overlay directives', () => {
2328
providers: [
2429
{provide: Directionality, useFactory: () => {
2530
return dir = {value: 'ltr'};
26-
}}
31+
}},
32+
{provide: ScrollDispatcher, useFactory: () => ({
33+
scrolled: () => scrolledSubject.asObservable()
34+
})}
2735
],
2836
});
2937
});
@@ -253,7 +261,7 @@ describe('Overlay directives', () => {
253261
});
254262

255263
describe('outputs', () => {
256-
it('should emit backdropClick appropriately', () => {
264+
it('should emit when the backdrop was clicked', () => {
257265
fixture.componentInstance.hasBackdrop = true;
258266
fixture.componentInstance.isOpen = true;
259267
fixture.detectChanges();
@@ -266,7 +274,7 @@ describe('Overlay directives', () => {
266274
expect(fixture.componentInstance.backdropClicked).toBe(true);
267275
});
268276

269-
it('should emit positionChange appropriately', () => {
277+
it('should emit when the position has changed', () => {
270278
expect(fixture.componentInstance.positionChangeHandler).not.toHaveBeenCalled();
271279
fixture.componentInstance.isOpen = true;
272280
fixture.detectChanges();
@@ -279,32 +287,54 @@ describe('Overlay directives', () => {
279287
.toBe(true, `Expected directive to emit an instance of ConnectedOverlayPositionChange.`);
280288
});
281289

282-
it('should emit attach and detach appropriately', () => {
290+
it('should emit when attached', () => {
283291
expect(fixture.componentInstance.attachHandler).not.toHaveBeenCalled();
284-
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
285292
fixture.componentInstance.isOpen = true;
286293
fixture.detectChanges();
287294

288295
expect(fixture.componentInstance.attachHandler).toHaveBeenCalled();
289296
expect(fixture.componentInstance.attachResult instanceof HTMLElement)
290297
.toBe(true, `Expected pane to be populated with HTML elements when attach was called.`);
298+
299+
fixture.componentInstance.isOpen = false;
300+
fixture.detectChanges();
301+
});
302+
303+
it('should emit when detached', () => {
304+
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
305+
fixture.componentInstance.isOpen = true;
306+
fixture.detectChanges();
307+
291308
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
292309

293310
fixture.componentInstance.isOpen = false;
294311
fixture.detectChanges();
295312
expect(fixture.componentInstance.detachHandler).toHaveBeenCalled();
296313
});
297314

315+
it('should emit when detached externally', inject([Overlay], (overlay: Overlay) => {
316+
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
317+
fixture.componentInstance.scrollStrategy = overlay.scrollStrategies.close();
318+
fixture.componentInstance.isOpen = true;
319+
fixture.detectChanges();
320+
321+
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
322+
323+
scrolledSubject.next();
324+
fixture.detectChanges();
325+
326+
expect(fixture.componentInstance.detachHandler).toHaveBeenCalled();
327+
}));
328+
298329
});
299330

300331
});
301332

302-
303333
@Component({
304334
template: `
305335
<button cdk-overlay-origin #trigger="cdkOverlayOrigin">Toggle menu</button>
306336
<ng-template cdk-connected-overlay [open]="isOpen" [width]="width" [height]="height"
307-
[origin]="trigger"
337+
[origin]="trigger" [scrollStrategy]="scrollStrategy"
308338
[hasBackdrop]="hasBackdrop" backdropClass="mat-test-class"
309339
(backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY"
310340
(positionChange)="positionChangeHandler($event)" (attach)="attachHandler()"
@@ -322,10 +352,11 @@ class ConnectedOverlayDirectiveTest {
322352
offsetY: number = 0;
323353
hasBackdrop: boolean;
324354
backdropClicked = false;
355+
scrollStrategy: ScrollStrategy;
325356
positionChangeHandler = jasmine.createSpy('positionChangeHandler');
326357
attachHandler = jasmine.createSpy('attachHandler').and.callFake(() => {
327-
this.attachResult =
328-
this.connectedOverlayDirective.overlayRef.overlayElement.querySelector('p') as HTMLElement;
358+
const overlayElement = this.connectedOverlayDirective.overlayRef.overlayElement;
359+
this.attachResult = overlayElement.querySelector('p') as HTMLElement;
329360
});
330361
detachHandler = jasmine.createSpy('detachHandler');
331362
attachResult: HTMLElement;

src/cdk/overlay/overlay-directives.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
268268
}
269269

270270
this._overlayRef = this._overlay.create(this._buildConfig());
271+
this._overlayRef.attachments().subscribe(() => this.attach.emit());
272+
this._overlayRef.detachments().subscribe(() => this.detach.emit());
271273
}
272274

273275
/** Builds the overlay config based on the directive's inputs */
@@ -342,7 +344,6 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
342344

343345
if (!this._overlayRef.hasAttached()) {
344346
this._overlayRef.attach(this._templatePortal);
345-
this.attach.emit();
346347
}
347348

348349
if (this.hasBackdrop) {
@@ -356,7 +357,6 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
356357
private _detachOverlay() {
357358
if (this._overlayRef) {
358359
this._overlayRef.detach();
359-
this.detach.emit();
360360
}
361361

362362
this._backdropSubscription.unsubscribe();

0 commit comments

Comments
 (0)