ipsos-components
Version:
Material Design components for Angular
786 lines (635 loc) • 30.9 kB
text/typescript
import {ElementRef} from '@angular/core';
import {TestBed, inject} from '@angular/core/testing';
import {OverlayPositionBuilder} from './overlay-position-builder';
import {CdkScrollable} from '@angular/cdk/scrolling';
import {Subscription} from 'rxjs/Subscription';
import {ScrollDispatchModule} from '@angular/cdk/scrolling';
import {
OverlayModule,
Overlay,
OverlayRef,
OverlayContainer,
ConnectedPositionStrategy,
ConnectedOverlayPositionChange,
} from '../index';
// Default width and height of the overlay and origin panels throughout these tests.
const DEFAULT_HEIGHT = 30;
const DEFAULT_WIDTH = 60;
// For all tests, we assume the browser window is 1024x786 (outerWidth x outerHeight).
// The karma config has been set to this for local tests, and it is the default size
// for tests on CI (both SauceLabs and Browserstack).
describe('ConnectedPositionStrategy', () => {
let positionBuilder: OverlayPositionBuilder;
let overlayContainer: OverlayContainer;
let overlayContainerElement: HTMLElement;
beforeEach(() => {
TestBed.configureTestingModule({imports: [ScrollDispatchModule, OverlayModule]});
inject([Overlay, OverlayContainer], (overlay: Overlay, oc: OverlayContainer) => {
positionBuilder = overlay.position();
overlayContainer = oc;
overlayContainerElement = oc.getContainerElement();
})();
});
afterEach(() => {
overlayContainer.ngOnDestroy();
});
describe('with origin on document body', () => {
const ORIGIN_HEIGHT = DEFAULT_HEIGHT;
const ORIGIN_WIDTH = DEFAULT_WIDTH;
const OVERLAY_HEIGHT = DEFAULT_HEIGHT;
const OVERLAY_WIDTH = DEFAULT_WIDTH;
let originElement: HTMLElement;
let overlayElement: HTMLElement;
let strategy: ConnectedPositionStrategy;
let fakeElementRef: ElementRef;
let originRect: ClientRect | null;
let originCenterX: number | null;
let originCenterY: number | null;
beforeEach(() => {
// The origin and overlay elements need to be in the document body in order to have geometry.
originElement = createPositionedBlockElement();
overlayElement = createPositionedBlockElement();
document.body.appendChild(originElement);
overlayContainerElement.appendChild(overlayElement);
fakeElementRef = new ElementRef(originElement);
});
afterEach(() => {
document.body.removeChild(originElement);
// Reset the origin geometry after each test so we don't accidently keep state between tests.
originRect = null;
originCenterX = null;
originCenterY = null;
});
describe('when not near viewport edge, not scrolled', () => {
// Place the original element close to the center of the window.
// (1024 / 2, 768 / 2). It's not exact, since outerWidth/Height includes browser
// chrome, but it doesn't really matter for these tests.
const ORIGIN_LEFT = 500;
const ORIGIN_TOP = 350;
beforeEach(() => {
originElement.style.left = `${ORIGIN_LEFT}px`;
originElement.style.top = `${ORIGIN_TOP}px`;
originRect = originElement.getBoundingClientRect();
originCenterX = originRect.left + (ORIGIN_WIDTH / 2);
originCenterY = originRect.top + (ORIGIN_HEIGHT / 2);
});
// Preconditions are set, now just run the full set of simple position tests.
runSimplePositionTests();
});
describe('when scrolled', () => {
// Place the original element decently far outside the unscrolled document (1024x768).
const ORIGIN_LEFT = 2500;
const ORIGIN_TOP = 2500;
// Create a very large element that will make the page scrollable.
let veryLargeElement: HTMLElement = document.createElement('div');
veryLargeElement.style.width = '4000px';
veryLargeElement.style.height = '4000px';
beforeEach(() => {
// Scroll the page such that the origin element is roughly in the
// center of the visible viewport (2500 - 1024/2, 2500 - 768/2).
document.body.appendChild(veryLargeElement);
document.body.scrollTop = 2100;
document.body.scrollLeft = 2100;
originElement.style.top = `${ORIGIN_TOP}px`;
originElement.style.left = `${ORIGIN_LEFT}px`;
originRect = originElement.getBoundingClientRect();
originCenterX = originRect.left + (ORIGIN_WIDTH / 2);
originCenterY = originRect.top + (ORIGIN_HEIGHT / 2);
});
afterEach(() => {
document.body.removeChild(veryLargeElement);
document.body.scrollTop = 0;
document.body.scrollLeft = 0;
});
// Preconditions are set, now just run the full set of simple position tests.
runSimplePositionTests();
});
describe('when near viewport edge', () => {
it('should reposition the overlay if it would go off the top of the screen', () => {
// We can use the real ViewportRuler in this test since we know that zero is
// always the top of the viewport.
originElement.style.top = '5px';
originElement.style.left = '200px';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'top'},
{overlayX: 'end', overlayY: 'bottom'})
.withFallbackPosition(
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.bottom));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
});
it('should reposition the overlay if it would go off the left of the screen', () => {
// We can use the real ViewportRuler in this test since we know that zero is
// always the left edge of the viewport.
originElement.style.top = '200px';
originElement.style.left = '5px';
originRect = originElement.getBoundingClientRect();
originCenterY = originRect.top + (ORIGIN_HEIGHT / 2);
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'end', overlayY: 'top'})
.withFallbackPosition(
{originX: 'end', originY: 'center'},
{overlayX: 'start', overlayY: 'center'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originCenterY - (OVERLAY_HEIGHT / 2)));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.right));
});
it('should reposition the overlay if it would go off the bottom of the screen', () => {
originElement.style.bottom = '25px';
originElement.style.left = '200px';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'})
.withFallbackPosition(
{originX: 'end', originY: 'top'},
{overlayX: 'end', overlayY: 'bottom'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top));
expect(Math.floor(overlayRect.right)).toBe(Math.floor(originRect.right));
});
it('should reposition the overlay if it would go off the right of the screen', () => {
originElement.style.top = '200px';
originElement.style.right = '25px';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'center'},
{overlayX: 'start', overlayY: 'center'})
.withFallbackPosition(
{originX: 'start', originY: 'bottom'},
{overlayX: 'end', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.bottom));
expect(Math.floor(overlayRect.right)).toBe(Math.floor(originRect.left));
});
it('should recalculate and set the last position with recalculateLastPosition()', () => {
// Push the trigger down so the overlay doesn't have room to open on the bottom.
originElement.style.bottom = '25px';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'})
.withFallbackPosition(
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'bottom'});
// This should apply the fallback position, as the original position won't fit.
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
// Now make the overlay small enough to fit in the first preferred position.
overlayElement.style.height = '15px';
// This should only re-align in the last position, even though the first would fit.
strategy.recalculateLastPosition();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top),
'Expected overlay to be re-aligned to the trigger in the previous position.');
});
it('should default to the initial position, if no positions fit in the viewport', () => {
// Make the origin element taller than the viewport.
originElement.style.height = '1000px';
originElement.style.top = '0';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'bottom'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
strategy.recalculateLastPosition();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top),
'Expected overlay to be re-aligned to the trigger in the initial position.');
});
it('should position a panel properly when rtl', () => {
// must make the overlay longer than the origin to properly test attachment
overlayElement.style.width = `500px`;
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'})
.withDirection('rtl');
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.bottom));
expect(Math.floor(overlayRect.right)).toBe(Math.floor(originRect.right));
});
it('should position a panel with the x offset provided', () => {
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'});
strategy.withOffsetX(10);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left + 10));
});
it('should position a panel with the y offset provided', () => {
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'});
strategy.withOffsetY(50);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top + 50));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
});
it('should allow for the fallback positions to specify their own offsets', () => {
originElement.style.bottom = '0';
originElement.style.left = '50%';
originElement.style.position = 'fixed';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder
.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'})
.withFallbackPosition(
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'bottom'},
-100, -100);
strategy.withOffsetY(50).withOffsetY(50);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top - 100));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left - 100));
});
});
it('should emit onPositionChange event when position changes', () => {
originElement.style.top = '200px';
originElement.style.right = '25px';
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'center'},
{overlayX: 'start', overlayY: 'center'})
.withFallbackPosition(
{originX: 'start', originY: 'bottom'},
{overlayX: 'end', overlayY: 'top'});
const positionChangeHandler = jasmine.createSpy('positionChangeHandler');
const subscription = strategy.onPositionChange.subscribe(positionChangeHandler);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
const latestCall = positionChangeHandler.calls.mostRecent();
expect(positionChangeHandler).toHaveBeenCalled();
expect(latestCall.args[0] instanceof ConnectedOverlayPositionChange)
.toBe(true, `Expected strategy to emit an instance of ConnectedOverlayPositionChange.`);
// If the strategy is re-applied and the initial position would now fit,
// the position change event should be emitted again.
originElement.style.top = '200px';
originElement.style.left = '200px';
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(positionChangeHandler).toHaveBeenCalledTimes(2);
subscription.unsubscribe();
});
it('should emit the onPositionChange event even if none of the positions fit', () => {
originElement.style.bottom = '25px';
originElement.style.right = '25px';
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'})
.withFallbackPosition(
{originX: 'start', originY: 'bottom'},
{overlayX: 'end', overlayY: 'top'});
const positionChangeHandler = jasmine.createSpy('positionChangeHandler');
const subscription = strategy.onPositionChange.subscribe(positionChangeHandler);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(positionChangeHandler).toHaveBeenCalled();
subscription.unsubscribe();
});
it('should pick the fallback position that shows the largest area of the element', () => {
originElement.style.top = '200px';
originElement.style.right = '25px';
originRect = originElement.getBoundingClientRect();
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'center'},
{overlayX: 'start', overlayY: 'center'})
.withFallbackPosition(
{originX: 'end', originY: 'top'},
{overlayX: 'start', overlayY: 'bottom'})
.withFallbackPosition(
{originX: 'end', originY: 'top'},
{overlayX: 'end', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.top));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left));
});
it('should re-use the preferred position when re-applying while locked in', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'center'},
{overlayX: 'start', overlayY: 'center'})
.withLockedPosition(true)
.withFallbackPosition(
{originX: 'start', originY: 'bottom'},
{overlayX: 'end', overlayY: 'top'});
const recalcSpy = spyOn(strategy, 'recalculateLastPosition');
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(recalcSpy).not.toHaveBeenCalled();
strategy.apply();
expect(recalcSpy).toHaveBeenCalled();
});
/**
* Run all tests for connecting the overlay to the origin such that first preferred
* position does not go off-screen. We do this because there are several cases where we
* want to run the exact same tests with different preconditions (e.g., not scroll, scrolled,
* different element sized, etc.).
*/
function runSimplePositionTests() {
it('should position a panel below, left-aligned', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect!.bottom));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect!.left));
});
it('should position to the right, center aligned vertically', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'center'},
{overlayX: 'start', overlayY: 'center'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originCenterY! - (OVERLAY_HEIGHT / 2)));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect!.right));
});
it('should position to the left, below', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'end', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect!.bottom));
expect(Math.round(overlayRect.right)).toBe(Math.round(originRect!.left));
});
it('should position above, right aligned', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'top'},
{overlayX: 'end', overlayY: 'bottom'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.round(overlayRect.bottom)).toBe(Math.round(originRect!.top));
expect(Math.round(overlayRect.right)).toBe(Math.round(originRect!.right));
});
it('should position below, centered', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'center', originY: 'bottom'},
{overlayX: 'center', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect!.bottom));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originCenterX! - (OVERLAY_WIDTH / 2)));
});
it('should center the overlay on the origin', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'center', originY: 'center'},
{overlayX: 'center', overlayY: 'center'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
let overlayRect = overlayElement.getBoundingClientRect();
expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect!.top));
expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect!.left));
});
}
});
describe('onPositionChange with scrollable view properties', () => {
let overlayElement: HTMLElement;
let strategy: ConnectedPositionStrategy;
let scrollable: HTMLDivElement;
let positionChangeHandler: jasmine.Spy;
let onPositionChangeSubscription: Subscription;
let positionChange: ConnectedOverlayPositionChange;
beforeEach(() => {
// Set up the overlay
overlayElement = createPositionedBlockElement();
overlayContainerElement.appendChild(overlayElement);
// Set up the origin
let originElement = createBlockElement();
originElement.style.margin = '0 1000px 1000px 0'; // Added so that the container scrolls
// Create a scrollable container and put the origin inside
scrollable = createOverflowContainerElement();
document.body.appendChild(scrollable);
scrollable.appendChild(originElement);
// Create a strategy with knowledge of the scrollable container
let fakeElementRef = new ElementRef(originElement);
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'top'});
strategy.withScrollableContainers([
new CdkScrollable(new ElementRef(scrollable), null!, null!)]);
strategy.attach(fakeOverlayRef(overlayElement));
positionChangeHandler = jasmine.createSpy('positionChangeHandler');
onPositionChangeSubscription = strategy.onPositionChange.subscribe(positionChangeHandler);
});
afterEach(() => {
onPositionChangeSubscription.unsubscribe();
document.body.removeChild(scrollable);
});
it('should not have origin or overlay clipped or out of view without scroll', () => {
strategy.apply();
expect(positionChangeHandler).toHaveBeenCalled();
positionChange = positionChangeHandler.calls.mostRecent().args[0];
expect(positionChange.scrollableViewProperties).toEqual({
isOriginClipped: false,
isOriginOutsideView: false,
isOverlayClipped: false,
isOverlayOutsideView: false
});
});
it('should evaluate if origin is clipped if scrolled slightly down', () => {
scrollable.scrollTop = 10; // Clip the origin by 10 pixels
strategy.apply();
expect(positionChangeHandler).toHaveBeenCalled();
positionChange = positionChangeHandler.calls.mostRecent().args[0];
expect(positionChange.scrollableViewProperties).toEqual({
isOriginClipped: true,
isOriginOutsideView: false,
isOverlayClipped: false,
isOverlayOutsideView: false
});
});
it('should evaluate if origin is out of view and overlay is clipped if scrolled enough', () => {
scrollable.scrollTop = 31; // Origin is 30 pixels, move out of view and clip the overlay 1px
strategy.apply();
expect(positionChangeHandler).toHaveBeenCalled();
positionChange = positionChangeHandler.calls.mostRecent().args[0];
expect(positionChange.scrollableViewProperties).toEqual({
isOriginClipped: true,
isOriginOutsideView: true,
isOverlayClipped: true,
isOverlayOutsideView: false
});
});
it('should evaluate the overlay and origin are both out of the view', () => {
scrollable.scrollTop = 61; // Scroll by overlay height + origin height + 1px buffer
strategy.apply();
expect(positionChangeHandler).toHaveBeenCalled();
positionChange = positionChangeHandler.calls.mostRecent().args[0];
expect(positionChange.scrollableViewProperties).toEqual({
isOriginClipped: true,
isOriginOutsideView: true,
isOverlayClipped: true,
isOverlayOutsideView: true
});
});
});
describe('positioning properties', () => {
let originElement: HTMLElement;
let overlayElement: HTMLElement;
let strategy: ConnectedPositionStrategy;
let fakeElementRef: ElementRef;
beforeEach(() => {
// The origin and overlay elements need to be in the document body in order to have geometry.
originElement = createPositionedBlockElement();
overlayElement = createPositionedBlockElement();
document.body.appendChild(originElement);
overlayContainerElement.appendChild(overlayElement);
fakeElementRef = new ElementRef(originElement);
});
afterEach(() => {
document.body.removeChild(originElement);
});
describe('in ltr', () => {
it('should use `left` when positioning an element at the start', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(overlayElement.style.left).toBeTruthy();
expect(overlayElement.style.right).toBeFalsy();
});
it('should use `right` when positioning an element at the end', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'top'},
{overlayX: 'end', overlayY: 'top'});
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(overlayElement.style.right).toBeTruthy();
expect(overlayElement.style.left).toBeFalsy();
});
});
describe('in rtl', () => {
it('should use `right` when positioning an element at the start', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'}
)
.withDirection('rtl');
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(overlayElement.style.right).toBeTruthy();
expect(overlayElement.style.left).toBeFalsy();
});
it('should use `left` when positioning an element at the end', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'end', originY: 'top'},
{overlayX: 'end', overlayY: 'top'}
).withDirection('rtl');
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(overlayElement.style.left).toBeTruthy();
expect(overlayElement.style.right).toBeFalsy();
});
});
describe('vertical', () => {
it('should use `top` when positioning at element along the top', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'top'},
{overlayX: 'start', overlayY: 'top'}
);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(overlayElement.style.top).toBeTruthy();
expect(overlayElement.style.bottom).toBeFalsy();
});
it('should use `bottom` when positioning at element along the bottom', () => {
strategy = positionBuilder.connectedTo(
fakeElementRef,
{originX: 'start', originY: 'bottom'},
{overlayX: 'start', overlayY: 'bottom'}
);
strategy.attach(fakeOverlayRef(overlayElement));
strategy.apply();
expect(overlayElement.style.bottom).toBeTruthy();
expect(overlayElement.style.top).toBeFalsy();
});
});
});
});
/** Creates an absolutely positioned, display: block element with a default size. */
function createPositionedBlockElement() {
let element = createBlockElement();
element.style.position = 'absolute';
return element;
}
/** Creates a block element with a default size. */
function createBlockElement() {
let element = document.createElement('div');
element.style.width = `${DEFAULT_WIDTH}px`;
element.style.height = `${DEFAULT_HEIGHT}px`;
element.style.backgroundColor = 'rebeccapurple';
element.style.zIndex = '100';
return element;
}
/** Creates an overflow container with a set height and width with margin. */
function createOverflowContainerElement() {
let element = document.createElement('div');
element.style.position = 'relative';
element.style.overflow = 'auto';
element.style.height = '300px';
element.style.width = '300px';
element.style.margin = '100px';
return element;
}
function fakeOverlayRef(overlayElement: HTMLElement) {
return {overlayElement} as OverlayRef;
}