Skip to content

Commit

Permalink
fix(overlay): overlay directives not emitting when detached externally
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
crisbeto committed Oct 26, 2017
1 parent 76a6e7b commit b5672e7
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 15 deletions.
61 changes: 48 additions & 13 deletions src/cdk/overlay/overlay-directives.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@ import {ComponentFixture, TestBed, async, inject} from '@angular/core/testing';
import {Directionality} from '@angular/cdk/bidi';
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
import {ESCAPE} from '@angular/cdk/keycodes';
import {ConnectedOverlayDirective, OverlayModule, OverlayOrigin} from './index';
import {OverlayContainer} from './overlay-container';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {ConnectedOverlayPositionChange} from './position/connected-position';
import {
ConnectedOverlayDirective,
OverlayModule,
OverlayOrigin,
Overlay,
ScrollStrategy,
ScrollDispatcher,
OverlayContainer,
ConnectedPositionStrategy,
ConnectedOverlayPositionChange,
} from './index';
import {Subject} from 'rxjs/Subject';


describe('Overlay directives', () => {
let overlayContainer: OverlayContainer;
let overlayContainerElement: HTMLElement;
let fixture: ComponentFixture<ConnectedOverlayDirectiveTest>;
let dir: {value: string};
let scrolledSubject = new Subject();

beforeEach(() => {
TestBed.configureTestingModule({
Expand All @@ -23,7 +32,10 @@ describe('Overlay directives', () => {
providers: [
{provide: Directionality, useFactory: () => {
return dir = {value: 'ltr'};
}}
}},
{provide: ScrollDispatcher, useFactory: () => ({
scrolled: () => scrolledSubject.asObservable()
})}
],
});
});
Expand Down Expand Up @@ -253,7 +265,7 @@ describe('Overlay directives', () => {
});

describe('outputs', () => {
it('should emit backdropClick appropriately', () => {
it('should emit when the backdrop was clicked', () => {
fixture.componentInstance.hasBackdrop = true;
fixture.componentInstance.isOpen = true;
fixture.detectChanges();
Expand All @@ -266,7 +278,7 @@ describe('Overlay directives', () => {
expect(fixture.componentInstance.backdropClicked).toBe(true);
});

it('should emit positionChange appropriately', () => {
it('should emit when the position has changed', () => {
expect(fixture.componentInstance.positionChangeHandler).not.toHaveBeenCalled();
fixture.componentInstance.isOpen = true;
fixture.detectChanges();
Expand All @@ -279,32 +291,54 @@ describe('Overlay directives', () => {
.toBe(true, `Expected directive to emit an instance of ConnectedOverlayPositionChange.`);
});

it('should emit attach and detach appropriately', () => {
it('should emit when attached', () => {
expect(fixture.componentInstance.attachHandler).not.toHaveBeenCalled();
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
fixture.componentInstance.isOpen = true;
fixture.detectChanges();

expect(fixture.componentInstance.attachHandler).toHaveBeenCalled();
expect(fixture.componentInstance.attachResult instanceof HTMLElement)
.toBe(true, `Expected pane to be populated with HTML elements when attach was called.`);

fixture.componentInstance.isOpen = false;
fixture.detectChanges();
});

it('should emit when detached', () => {
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
fixture.componentInstance.isOpen = true;
fixture.detectChanges();

expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();

fixture.componentInstance.isOpen = false;
fixture.detectChanges();
expect(fixture.componentInstance.detachHandler).toHaveBeenCalled();
});

it('should emit when detached externally', inject([Overlay], (overlay: Overlay) => {
expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();
fixture.componentInstance.scrollStrategy = overlay.scrollStrategies.close();
fixture.componentInstance.isOpen = true;
fixture.detectChanges();

expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled();

scrolledSubject.next();
fixture.detectChanges();

expect(fixture.componentInstance.detachHandler).toHaveBeenCalled();
}));

});

});


@Component({
template: `
<button cdk-overlay-origin #trigger="cdkOverlayOrigin">Toggle menu</button>
<ng-template cdk-connected-overlay [open]="isOpen" [width]="width" [height]="height"
[origin]="trigger"
[origin]="trigger" [scrollStrategy]="scrollStrategy"
[hasBackdrop]="hasBackdrop" backdropClass="mat-test-class"
(backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY"
(positionChange)="positionChangeHandler($event)" (attach)="attachHandler()"
Expand All @@ -322,10 +356,11 @@ class ConnectedOverlayDirectiveTest {
offsetY: number = 0;
hasBackdrop: boolean;
backdropClicked = false;
scrollStrategy: ScrollStrategy;
positionChangeHandler = jasmine.createSpy('positionChangeHandler');
attachHandler = jasmine.createSpy('attachHandler').and.callFake(() => {
this.attachResult =
this.connectedOverlayDirective.overlayRef.overlayElement.querySelector('p') as HTMLElement;
const overlayElement = this.connectedOverlayDirective.overlayRef.overlayElement;
this.attachResult = overlayElement.querySelector('p') as HTMLElement;
});
detachHandler = jasmine.createSpy('detachHandler');
attachResult: HTMLElement;
Expand Down
4 changes: 2 additions & 2 deletions src/cdk/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
}

this._overlayRef = this._overlay.create(this._buildConfig());
this._overlayRef.attachments().subscribe(() => this.attach.emit());
this._overlayRef.detachments().subscribe(() => this.detach.emit());
}

/** Builds the overlay config based on the directive's inputs */
Expand Down Expand Up @@ -342,7 +344,6 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {

if (!this._overlayRef.hasAttached()) {
this._overlayRef.attach(this._templatePortal);
this.attach.emit();
}

if (this.hasBackdrop) {
Expand All @@ -356,7 +357,6 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
private _detachOverlay() {
if (this._overlayRef) {
this._overlayRef.detach();
this.detach.emit();
}

this._backdropSubscription.unsubscribe();
Expand Down

0 comments on commit b5672e7

Please sign in to comment.