Skip to content

Commit b79c953

Browse files
authored
feat(overlay): emit position change event (#1832)
1 parent 009046f commit b79c953

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

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

Lines changed: 28 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,33 @@ 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).toHaveBeenCalled();
285+
expect(positionChangeHandler.calls.mostRecent().args[0])
286+
.toEqual(jasmine.any(ConnectedOverlayPositionChange),
287+
`Expected strategy to emit an instance of ConnectedOverlayPositionChange.`);
288+
});
289+
262290

263291
/**
264292
* 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: 16 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,13 @@ export class ConnectedPositionStrategy implements PositionStrategy {
3638
/** The origin element against which the overlay will be positioned. */
3739
private _origin: HTMLElement;
3840

41+
private _onPositionChange:
42+
Subject<ConnectedOverlayPositionChange> = new Subject<ConnectedOverlayPositionChange>();
43+
44+
/** Emits an event when the connection point changes. */
45+
get onPositionChange(): Observable<ConnectedOverlayPositionChange> {
46+
return this._onPositionChange.asObservable();
47+
}
3948

4049
constructor(
4150
private _connectedTo: ElementRef,
@@ -64,6 +73,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
6473
// We use the viewport rect to determine whether a position would go off-screen.
6574
const viewportRect = this._viewportRuler.getViewportRect();
6675
let firstOverlayPoint: Point = null;
76+
let isFirstPosition = true;
6777

6878
// We want to place the overlay in the first of the preferred positions such that the
6979
// overlay fits on-screen.
@@ -77,8 +87,12 @@ export class ConnectedPositionStrategy implements PositionStrategy {
7787
// If the overlay in the calculated position fits on-screen, put it there and we're done.
7888
if (this._willOverlayFitWithinViewport(overlayPoint, overlayRect, viewportRect)) {
7989
this._setElementPosition(element, overlayPoint);
90+
if (!isFirstPosition) {
91+
this._onPositionChange.next(new ConnectedOverlayPositionChange(pos));
92+
}
8093
return Promise.resolve(null);
8194
}
95+
isFirstPosition = false;
8296
}
8397

8498
// 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)