Skip to content

Commit aa472a0

Browse files
authored
feat(overlay-directives): support fallback positions (#1865)
1 parent 6aa7e22 commit aa472a0

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

src/lib/core/overlay/overlay-directives.spec.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {By} from '@angular/platform-browser';
44
import {ConnectedOverlayDirective, OverlayModule} from './overlay-directives';
55
import {OverlayContainer} from './overlay-container';
66
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
7+
import {ConnectedOverlayPositionChange} from './position/connected-position';
78

89

910
describe('Overlay directives', () => {
@@ -110,18 +111,6 @@ describe('Overlay directives', () => {
110111
expect(backdrop.classList).toContain('md-test-class');
111112
});
112113

113-
it('should emit backdropClick appropriately', () => {
114-
fixture.componentInstance.hasBackdrop = true;
115-
fixture.componentInstance.isOpen = true;
116-
fixture.detectChanges();
117-
118-
const backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement;
119-
backdrop.click();
120-
fixture.detectChanges();
121-
122-
expect(fixture.componentInstance.backdropClicked).toBe(true);
123-
});
124-
125114
it('should set the offsetX', () => {
126115
const trigger = fixture.debugElement.query(By.css('button')).nativeElement;
127116
const startX = trigger.getBoundingClientRect().left;
@@ -154,15 +143,42 @@ describe('Overlay directives', () => {
154143

155144
});
156145

146+
describe('outputs', () => {
147+
it('should emit backdropClick appropriately', () => {
148+
fixture.componentInstance.hasBackdrop = true;
149+
fixture.componentInstance.isOpen = true;
150+
fixture.detectChanges();
151+
152+
const backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement;
153+
backdrop.click();
154+
fixture.detectChanges();
155+
156+
expect(fixture.componentInstance.backdropClicked).toBe(true);
157+
});
158+
159+
it('should emit positionChange appropriately', () => {
160+
expect(fixture.componentInstance.positionChangeHandler).not.toHaveBeenCalled();
161+
fixture.componentInstance.isOpen = true;
162+
fixture.detectChanges();
163+
164+
expect(fixture.componentInstance.positionChangeHandler).toHaveBeenCalled();
165+
expect(fixture.componentInstance.positionChangeHandler.calls.mostRecent().args[0])
166+
.toEqual(jasmine.any(ConnectedOverlayPositionChange),
167+
`Expected directive to emit an instance of ConnectedOverlayPositionChange.`);
168+
});
169+
170+
});
171+
157172
});
158173

159174

160175
@Component({
161176
template: `
162177
<button overlay-origin #trigger="overlayOrigin">Toggle menu</button>
163178
<template connected-overlay [origin]="trigger" [open]="isOpen" [width]="width" [height]="height"
164-
[hasBackdrop]="hasBackdrop" backdropClass="md-test-class"
165-
(backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY">
179+
[hasBackdrop]="hasBackdrop" backdropClass="md-test-class"
180+
(backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY"
181+
(positionChange)="positionChangeHandler($event)">
166182
<p>Menu content</p>
167183
</template>`,
168184
})
@@ -174,6 +190,7 @@ class ConnectedOverlayDirectiveTest {
174190
offsetY: number = 0;
175191
hasBackdrop: boolean;
176192
backdropClicked = false;
193+
positionChangeHandler = jasmine.createSpy('positionChangeHandler');
177194

178195
@ViewChild(ConnectedOverlayDirective) connectedOverlayDirective: ConnectedOverlayDirective;
179196
}

src/lib/core/overlay/overlay-directives.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import {Overlay, OVERLAY_PROVIDERS} from './overlay';
1515
import {OverlayRef} from './overlay-ref';
1616
import {TemplatePortal} from '../portal/portal';
1717
import {OverlayState} from './overlay-state';
18-
import {ConnectionPositionPair} from './position/connected-position';
18+
import {
19+
ConnectionPositionPair,
20+
ConnectedOverlayPositionChange
21+
} from './position/connected-position';
1922
import {PortalModule} from '../portal/portal-directives';
2023
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
2124
import {Subscription} from 'rxjs/Subscription';
@@ -63,6 +66,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
6366
private _open = false;
6467
private _hasBackdrop = false;
6568
private _backdropSubscription: Subscription;
69+
private _positionSubscription: Subscription;
6670

6771
@Input() origin: OverlayOrigin;
6872
@Input() positions: ConnectionPositionPair[];
@@ -105,6 +109,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
105109

106110
/** Event emitted when the backdrop is clicked. */
107111
@Output() backdropClick = new EventEmitter<void>();
112+
@Output() positionChange = new EventEmitter<ConnectedOverlayPositionChange>();
108113

109114
// TODO(jelbourn): inputs for size, scroll behavior, animation, etc.
110115

@@ -169,11 +174,27 @@ export class ConnectedOverlayDirective implements OnDestroy {
169174
const originPoint = {originX: pos.originX, originY: pos.originY};
170175
const overlayPoint = {overlayX: pos.overlayX, overlayY: pos.overlayY};
171176

172-
return this._overlay.position()
177+
const strategy = this._overlay.position()
173178
.connectedTo(this.origin.elementRef, originPoint, overlayPoint)
174179
.withDirection(this.dir)
175180
.withOffsetX(this.offsetX)
176181
.withOffsetY(this.offsetY);
182+
183+
this._handlePositionChanges(strategy);
184+
185+
return strategy;
186+
}
187+
188+
private _handlePositionChanges(strategy: ConnectedPositionStrategy): void {
189+
for (let i = 1; i < this.positions.length; i++) {
190+
strategy.withFallbackPosition(
191+
{originX: this.positions[i].originX, originY: this.positions[i].originY},
192+
{overlayX: this.positions[i].overlayX, overlayY: this.positions[i].overlayY}
193+
);
194+
}
195+
196+
this._positionSubscription =
197+
strategy.onPositionChange.subscribe(pos => this.positionChange.emit(pos));
177198
}
178199

179200
/** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */
@@ -214,6 +235,9 @@ export class ConnectedOverlayDirective implements OnDestroy {
214235
if (this._backdropSubscription) {
215236
this._backdropSubscription.unsubscribe();
216237
}
238+
if (this._positionSubscription) {
239+
this._positionSubscription.unsubscribe();
240+
}
217241
}
218242
}
219243

0 commit comments

Comments
 (0)