Skip to content

Commit 3e30dfe

Browse files
author
Kara Erickson
committed
feat(overlay): add event on position change
1 parent adfc4d1 commit 3e30dfe

File tree

3 files changed

+47
-2
lines changed

3 files changed

+47
-2
lines changed

src/lib/core/overlay/position/connected-position-strategy.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {ElementRef} from '@angular/core';
22
import {ConnectedPositionStrategy} from './connected-position-strategy';
33
import {ViewportRuler} from './viewport-ruler';
44
import {OverlayPositionBuilder} from './overlay-position-builder';
5+
import {ConnectedOverlayPositionChange} from './connected-position';
56

67

78
// Default width and height of the overlay and origin panels throughout these tests.
@@ -259,6 +260,32 @@ describe('ConnectedPositionStrategy', () => {
259260

260261
});
261262

263+
it('should emit onPositionChange event when position changes', () => {
264+
// force the overlay to open in a fallback position
265+
fakeViewportRuler.fakeRect = {
266+
top: 0, left: 0, width: 500, height: 500, right: 500, bottom: 500
267+
};
268+
positionBuilder = new OverlayPositionBuilder(fakeViewportRuler);
269+
originElement.style.top = '200px';
270+
originElement.style.left = '475px';
271+
272+
strategy = positionBuilder.connectedTo(
273+
fakeElementRef,
274+
{originX: 'end', originY: 'center'},
275+
{overlayX: 'start', overlayY: 'center'})
276+
.withFallbackPosition(
277+
{originX: 'start', originY: 'bottom'},
278+
{overlayX: 'end', overlayY: 'top'});
279+
280+
const positionChangeHandler = jasmine.createSpy('positionChangeHandler');
281+
strategy.onPositionChange.first().subscribe(positionChangeHandler);
282+
283+
strategy.apply(overlayElement);
284+
expect(positionChangeHandler.calls.mostRecent().args[0])
285+
.toEqual(jasmine.any(ConnectedOverlayPositionChange),
286+
`Expected strategy to emit an event when a fallback position was used.`);
287+
});
288+
262289

263290
/**
264291
* Run all tests for connecting the overlay to the origin such that first preferred

src/lib/core/overlay/position/connected-position-strategy.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import {applyCssTransform} from '../../style/apply-transform';
55
import {
66
ConnectionPositionPair,
77
OriginConnectionPosition,
8-
OverlayConnectionPosition
8+
OverlayConnectionPosition,
9+
ConnectedOverlayPositionChange
910
} from './connected-position';
10-
11+
import {Subject} from 'rxjs/Subject';
12+
import {Observable} from 'rxjs/Observable';
1113

1214
/**
1315
* A strategy for positioning overlays. Using this strategy, an overlay is given an
@@ -36,6 +38,12 @@ export class ConnectedPositionStrategy implements PositionStrategy {
3638
/** The origin element against which the overlay will be positioned. */
3739
private _origin: HTMLElement;
3840

41+
private _onPositionChange: Subject<ConnectedOverlayPositionChange> = new Subject();
42+
43+
/** Emits an event when a fallback position is used. */
44+
get onPositionChange(): Observable<ConnectedOverlayPositionChange> {
45+
return this._onPositionChange.asObservable();
46+
}
3947

4048
constructor(
4149
private _connectedTo: ElementRef,
@@ -64,6 +72,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
6472
// We use the viewport rect to determine whether a position would go off-screen.
6573
const viewportRect = this._viewportRuler.getViewportRect();
6674
let firstOverlayPoint: Point = null;
75+
let isFirstPosition = true;
6776

6877
// We want to place the overlay in the first of the preferred positions such that the
6978
// overlay fits on-screen.
@@ -77,8 +86,12 @@ export class ConnectedPositionStrategy implements PositionStrategy {
7786
// If the overlay in the calculated position fits on-screen, put it there and we're done.
7887
if (this._willOverlayFitWithinViewport(overlayPoint, overlayRect, viewportRect)) {
7988
this._setElementPosition(element, overlayPoint);
89+
if (!isFirstPosition) {
90+
this._onPositionChange.next(new ConnectedOverlayPositionChange(pos));
91+
}
8092
return Promise.resolve(null);
8193
}
94+
isFirstPosition = false;
8295
}
8396

8497
// TODO(jelbourn): fallback behavior for when none of the preferred positions fit on-screen.

src/lib/core/overlay/position/connected-position.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@ export class ConnectionPositionPair {
3131
this.overlayY = overlay.overlayY;
3232
}
3333
}
34+
35+
/** The change event emitted by the strategy when a fallback position is used. */
36+
export class ConnectedOverlayPositionChange {
37+
constructor(public connectionPair: ConnectionPositionPair) {}
38+
}

0 commit comments

Comments
 (0)