igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,044 lines (884 loc) • 235 kB
text/typescript
import {
ApplicationRef,
Component,
ComponentRef,
ElementRef,
HostBinding,
Inject,
ViewChild,
ViewContainerRef,
ViewEncapsulation
} from '@angular/core';
import { fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { first } from 'rxjs/operators';
import { scaleInVerTop, scaleOutVerTop } from '../../animations/main';
import { IgxAvatarComponent } from '../../avatar/avatar.component';
import { IgxCalendarComponent } from '../../calendar/public_api';
import { IgxCalendarContainerComponent } from '../../date-common/calendar-container/calendar-container.component';
import { configureTestSuite } from '../../test-utils/configure-suite';
import { UIInteractions } from '../../test-utils/ui-interactions.spec';
import { IgxAngularAnimationService } from '../animation/angular-animation-service';
import { AnimationService } from '../animation/animation';
import { IgxOverlayOutletDirective, IgxToggleDirective } from './../../directives/toggle/toggle.directive';
import { IgxOverlayService } from './overlay';
import { ContainerPositionStrategy } from './position';
import { AutoPositionStrategy } from './position/auto-position-strategy';
import { BaseFitPositionStrategy } from './position/base-fit-position-strategy';
import { ConnectedPositioningStrategy } from './position/connected-positioning-strategy';
import { ElasticPositionStrategy } from './position/elastic-position-strategy';
import { GlobalPositionStrategy } from './position/global-position-strategy';
import { IPositionStrategy } from './position/IPositionStrategy';
import { AbsoluteScrollStrategy } from './scroll/absolute-scroll-strategy';
import { BlockScrollStrategy } from './scroll/block-scroll-strategy';
import { CloseScrollStrategy } from './scroll/close-scroll-strategy';
import { NoOpScrollStrategy } from './scroll/NoOpScrollStrategy';
import {
HorizontalAlignment,
OverlayCancelableEventArgs,
OverlayEventArgs,
OverlaySettings,
Point,
PositionSettings,
VerticalAlignment
} from './utilities';
import { NgIf } from '@angular/common';
const CLASS_OVERLAY_CONTENT = 'igx-overlay__content';
const CLASS_OVERLAY_CONTENT_MODAL = 'igx-overlay__content--modal';
const CLASS_OVERLAY_CONTENT_RELATIVE = 'igx-overlay__content--relative';
const CLASS_OVERLAY_WRAPPER = 'igx-overlay__wrapper';
const CLASS_OVERLAY_WRAPPER_MODAL = 'igx-overlay__wrapper--modal';
const CLASS_OVERLAY_WRAPPER_FLEX = 'igx-overlay__wrapper--flex';
const CLASS_OVERLAY_MAIN = 'igx-overlay';
const CLASS_SCROLLABLE_DIV = 'scrollableDiv';
const DEBOUNCE_TIME = 16;
// Utility function to get all applied to element css from all sources.
const css = (element) => {
const sheets = document.styleSheets;
const ret = [];
element.matches =
element.matches ||
element.webkitMatchesSelector ||
element.mozMatchesSelector ||
element.msMatchesSelector ||
element.oMatchesSelector;
for (const key in sheets) {
if (sheets.hasOwnProperty(key)) {
const sheet = sheets[key];
const rules: any = sheet.cssRules;
for (const r in rules) {
if (element.matches(rules[r].selectorText)) {
ret.push(rules[r].cssText);
}
}
}
}
return ret;
};
export const addScrollDivToElement = (parent) => {
const scrollDiv = document.createElement('div');
scrollDiv.style.width = '100px';
scrollDiv.style.height = '100px';
scrollDiv.style.top = '10000px';
scrollDiv.style.left = '10000px';
scrollDiv.style.position = 'absolute';
parent.appendChild(scrollDiv);
};
/**
* Returns the top left location of the shown element
*
* @param positionSettings Overlay setting to get location for
* @param targetRect Rectangle of overlaySettings.target
* @param wrapperRect Rectangle of shown element
* @param screenRect Rectangle of the visible area
* @param elastic Is elastic position strategy, defaults to false
*/
// const getOverlayWrapperLocation = (
// positionSettings: PositionSettings,
// targetRect: ClientRect,
// wrapperRect: ClientRect,
// screenRect: ClientRect,
// elastic = false): Point => {
// const location: Point = new Point(0, 0);
// location.x =
// targetRect.left +
// targetRect.width * (1 + positionSettings.horizontalStartPoint) +
// wrapperRect.width * positionSettings.horizontalDirection;
// if (location.x < screenRect.left) {
// if (elastic) {
// let offset = screenRect.left - location.x;
// if (offset > wrapperRect.width - positionSettings.minSize.width) {
// offset = wrapperRect.width - positionSettings.minSize.width;
// }
// location.x += offset;
// } else {
// const flipOffset = wrapperRect.width * (1 + positionSettings.horizontalDirection);
// if (positionSettings.horizontalStartPoint === HorizontalAlignment.Left) {
// location.x = Math.max(0, targetRect.right - flipOffset);
// } else if (positionSettings.horizontalStartPoint === HorizontalAlignment.Center) {
// location.x =
// Math.max(0, targetRect.left + targetRect.width / 2 - flipOffset);
// } else {
// location.x = Math.max(0, targetRect.left - flipOffset);
// }
// }
// } else if (location.x + wrapperRect.width > screenRect.right && !elastic) {
// const flipOffset = wrapperRect.width * (1 + positionSettings.horizontalDirection);
// if (positionSettings.horizontalStartPoint === HorizontalAlignment.Left) {
// location.x = Math.min(screenRect.right, targetRect.right - flipOffset);
// } else if (positionSettings.horizontalStartPoint === HorizontalAlignment.Center) {
// location.x = Math.min(screenRect.right, targetRect.left + targetRect.width / 2 - flipOffset);
// } else {
// location.x = Math.min(screenRect.right, targetRect.left - flipOffset);
// }
// }
// location.y =
// targetRect.top +
// targetRect.height * (1 + positionSettings.verticalStartPoint) +
// wrapperRect.height * positionSettings.verticalDirection;
// if (location.y < screenRect.top) {
// if (elastic) {
// let offset = screenRect.top - location.y;
// if (offset > wrapperRect.height - positionSettings.minSize.height) {
// offset = wrapperRect.height - positionSettings.minSize.height;
// }
// location.y += offset;
// } else {
// const flipOffset = wrapperRect.height * (1 + positionSettings.verticalDirection);
// if (positionSettings.verticalStartPoint === VerticalAlignment.Top) {
// location.y = Math.max(0, targetRect.bottom - flipOffset);
// } else if (positionSettings.verticalStartPoint === VerticalAlignment.Middle) {
// location.y = Math.max(0, targetRect.top + targetRect.height / 2 - flipOffset);
// } else {
// location.y = Math.max(0, targetRect.top - flipOffset);
// }
// }
// } else if (location.y + wrapperRect.height > screenRect.bottom && !elastic) {
// const flipOffset = wrapperRect.height * (1 + positionSettings.verticalDirection);
// if (positionSettings.verticalStartPoint === VerticalAlignment.Top) {
// location.y = Math.min(screenRect.bottom, targetRect.bottom - flipOffset);
// } else if (positionSettings.verticalStartPoint === VerticalAlignment.Middle) {
// location.y = Math.min(screenRect.bottom, targetRect.top + targetRect.height / 2 - flipOffset);
// } else {
// location.y = Math.min(screenRect.bottom, targetRect.top - flipOffset);
// }
// }
// return location;
// };
/**
* Formats a string according to the given formatters
*
* @param inputString String to be formatted
* @param formatters Each formatter should include regex expressions and replacements to be applied on the inputString
*/
const formatString = (inputString: string, formatters: any[]) => {
formatters.forEach(formatter => inputString = inputString.replace(formatter.pattern, formatter.replacement));
return inputString;
};
describe('igxOverlay', () => {
const formatters = [
{ pattern: /:\s/g, replacement: ':' },
{ pattern: /red;/, replacement: 'red' }
];
beforeEach(waitForAsync(() => {
UIInteractions.clearOverlay();
}));
afterAll(() => {
UIInteractions.clearOverlay();
});
const verifyOverlayMargins = (overlaySettings: OverlaySettings, overlay: IgxOverlayService, fix, expectedMargin) => {
overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings));
tick();
fix.detectChanges();
const overlayWrapper = document.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0];
const overlayContent = document.getElementsByClassName(CLASS_OVERLAY_CONTENT)[0];
const overlayElement = overlayContent.children[0];
const wrapperMargin = window.getComputedStyle(overlayWrapper, null).getPropertyValue('margin');
const contentMargin = window.getComputedStyle(overlayContent, null).getPropertyValue('margin');
const elementMargin = window.getComputedStyle(overlayElement, null).getPropertyValue('margin');
expect(wrapperMargin).toEqual(expectedMargin);
expect(contentMargin).toEqual(expectedMargin);
expect(elementMargin).toEqual(expectedMargin);
overlay.detachAll();
};
describe('Pure Unit Test', () => {
configureTestSuite();
let mockElement: any;
let mockElementRef: any;
let mockFactoryResolver: any;
let mockApplicationRef: any;
let mockInjector: any;
let mockAnimationBuilder: any;
let mockDocument: any;
let mockNgZone: any;
let mockPlatformUtil: any;
let overlay: IgxOverlayService;
let mockAnimationService: AnimationService;
beforeEach(() => {
mockElement = {
style: { visibility: '', cursor: '', transitionDuration: '' },
children: [],
classList: { add: () => { }, remove: () => { } },
appendChild(element: any) {
this.children.push(element);
},
removeChild(element: any) {
const index = this.children.indexOf(element);
if (index !== -1) {
this.children.splice(index, 1);
}
},
addEventListener: () => { },
removeEventListener: () => { },
getBoundingClientRect: () => ({ width: 10, height: 10 }),
insertBefore(newChild: HTMLDivElement, refChild: Node) {
let refIndex = this.children.indexOf(refChild);
if (refIndex === -1) {
refIndex = 0;
}
this.children.splice(refIndex, 0, newChild);
},
contains(element: any) {
return this.children.indexOf(element) !== -1;
}
};
mockElement.parent = mockElement;
mockElement.parentElement = mockElement;
mockElement.parentNode = mockElement;
mockElementRef = { nativeElement: mockElement };
mockFactoryResolver = {
resolveComponentFactory: () => ({
create: () => ({
hostView: '',
location: mockElementRef,
changeDetectorRef: { detectChanges: () => { } },
destroy: () => { },
onDestroy: () => { }
})
})
};
mockApplicationRef = { attachView: () => { }, detachView: () => { } };
mockInjector = {};
mockAnimationBuilder = {};
mockDocument = {
body: mockElement,
listeners: {},
defaultView: mockElement,
// this is used be able to properly invoke rxjs `fromEvent` operator, which, turns out
// just adds an event listener to the element and emits accordingly
dispatchEvent(event: KeyboardEvent) {
const type = event.type;
if (this.listeners[type]) {
this.listeners[type].forEach(listener => {
listener(event);
});
}
},
createElement: () => mockElement,
appendChild: () => { },
addEventListener(type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) {
if (!this.listeners[type]) {
this.listeners[type] = [];
}
this.listeners[type].push(listener);
},
removeEventListener(type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) {
if (this.listeners[type]) {
const index = this.listeners[type].indexOf(listener);
if (index !== -1) {
this.listeners[type].splice(index, 1);
}
}
}
};
mockNgZone = {};
mockPlatformUtil = { isIOS: false };
mockAnimationService = new IgxAngularAnimationService(mockAnimationBuilder);
overlay = new IgxOverlayService(
mockFactoryResolver, mockApplicationRef, mockInjector, mockDocument, mockNgZone, mockPlatformUtil, mockAnimationService);
});
it('Should set cursor to pointer on iOS', () => {
mockPlatformUtil.isIOS = true;
mockDocument.body.style.cursor = 'initialCursorValue';
const mockOverlaySettings: OverlaySettings = {
modal: false,
positionStrategy: new GlobalPositionStrategy({ openAnimation: null, closeAnimation: null })
};
let id = overlay.attach(mockElementRef, mockOverlaySettings);
overlay.show(id);
expect(mockDocument.body.style.cursor).toEqual('pointer');
overlay.hide(id);
overlay.detach(id);
expect(mockDocument.body.style.cursor).toEqual('initialCursorValue');
mockPlatformUtil.isIOS = false;
id = overlay.attach(mockElementRef, mockOverlaySettings);
overlay.show(id);
expect(mockDocument.body.style.cursor).toEqual('initialCursorValue');
overlay.hide(id);
overlay.detach(id);
expect(mockDocument.body.style.cursor).toEqual('initialCursorValue');
});
it('Should clear listener for escape key when overlay settings have outlet specified', () => {
const mockOverlaySettings: OverlaySettings = {
modal: false,
closeOnEscape: true,
outlet: mockElement,
positionStrategy: new GlobalPositionStrategy({ openAnimation: null, closeAnimation: null })
};
const id = overlay.attach(mockElementRef, mockOverlaySettings);
// show the overlay
overlay.show(id);
// expect escape listener to be added to document
expect(mockDocument.listeners['keydown'].length > 0).toBeTruthy();
const keydownListener = mockDocument.listeners['keydown'][0];
spyOn(overlay, 'hide').and.callThrough();
spyOn(mockDocument, 'removeEventListener').and.callThrough();
mockDocument.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
// expect hide to have been called
expect(overlay.hide).toHaveBeenCalledTimes(1);
expect(mockDocument.removeEventListener).not.toHaveBeenCalled();
overlay.detach(id);
expect(mockDocument.removeEventListener).toHaveBeenCalled();
// the keydown listener is now removed
expect(mockDocument.removeEventListener).toHaveBeenCalledWith('keydown', keydownListener, undefined);
// fire event again, expecting hide NOT to be fired again
mockDocument.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
expect(overlay.hide).toHaveBeenCalledTimes(1);
expect(mockDocument.listeners['keydown'].length).toBe(0);
});
});
describe('Unit Tests: ', () => {
configureTestSuite();
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, SimpleDynamicWithDirectiveComponent]
}).compileComponents();
}));
it('OverlayElement should return a div attached to Document\'s body.', fakeAsync(() => {
const fixture = TestBed.createComponent(EmptyPageComponent);
fixture.detectChanges();
fixture.componentInstance.buttonElement.nativeElement.click();
tick();
const overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayDiv).toBeDefined();
expect(overlayDiv).toHaveClass(CLASS_OVERLAY_MAIN);
fixture.componentInstance.overlay.detachAll();
}));
it('Should attach to setting target or default to body', fakeAsync(() => {
const fixture = TestBed.createComponent(EmptyPageComponent);
const button = fixture.componentInstance.buttonElement;
const overlay = fixture.componentInstance.overlay;
fixture.detectChanges();
let id = overlay.attach(SimpleDynamicComponent, { outlet: button, modal: false });
overlay.show(id);
tick();
let wrapperElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0] as HTMLElement;
expect(wrapperElement).toBeDefined();
expect(wrapperElement.parentNode).toBe(button.nativeElement);
overlay.detach(id);
tick();
id = overlay.attach(SimpleDynamicComponent, { modal: false });
overlay.show(id);
tick();
wrapperElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0] as HTMLElement;
expect(wrapperElement).toBeDefined();
expect(wrapperElement.parentElement.classList).toContain(CLASS_OVERLAY_MAIN);
expect(wrapperElement.parentElement.parentElement).toBe(document.body);
overlay.detach(id);
tick();
const outlet = document.createElement('div');
fixture.debugElement.nativeElement.appendChild(outlet);
id = overlay.attach(SimpleDynamicComponent, { modal: false, outlet: new IgxOverlayOutletDirective(new ElementRef(outlet)) });
overlay.show(id);
tick();
wrapperElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0] as HTMLElement;
expect(wrapperElement).toBeDefined();
expect(wrapperElement.parentNode).toBe(outlet);
overlay.detachAll();
}));
it('Should show component passed to overlay.', fakeAsync(() => {
const fixture = TestBed.createComponent(EmptyPageComponent);
fixture.detectChanges();
fixture.componentInstance.buttonElement.nativeElement.click();
tick(DEBOUNCE_TIME);
const overlayElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayElement).toBeDefined();
expect(overlayElement.children.length).toEqual(1);
const wrapperElement = overlayElement.children[0];
expect(wrapperElement).toBeDefined();
expect(wrapperElement).toHaveClass(CLASS_OVERLAY_WRAPPER_MODAL);
expect(wrapperElement.children[0].localName).toEqual('div');
const contentElement = wrapperElement.children[0];
expect(contentElement).toBeDefined();
expect(contentElement).toHaveClass(CLASS_OVERLAY_CONTENT_MODAL);
fixture.componentInstance.overlay.detachAll();
}));
it('Should hide component and the overlay when Hide() is called.', fakeAsync(() => {
const fixture = TestBed.createComponent(EmptyPageComponent);
fixture.detectChanges();
const overlay = fixture.componentInstance.overlay;
overlay.show(overlay.attach(SimpleDynamicComponent));
tick();
overlay.show(overlay.attach(SimpleDynamicComponent));
tick();
let overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayDiv).toBeDefined();
expect(overlayDiv.children.length).toEqual(2);
expect((overlayDiv.children[0] as HTMLElement).style.visibility).toEqual('');
expect((overlayDiv.children[1] as HTMLElement).style.visibility).toEqual('');
overlay.hide('0');
tick();
overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect((overlayDiv.children[0] as HTMLElement).style.visibility).toEqual('hidden');
expect((overlayDiv.children[1] as HTMLElement).style.visibility).toEqual('');
overlay.hide('1');
tick();
overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect((overlayDiv.children[0] as HTMLElement).style.visibility).toEqual('hidden');
expect((overlayDiv.children[1] as HTMLElement).style.visibility).toEqual('hidden');
overlay.detachAll();
}));
it('Should hide all components and the overlay when HideAll() is called.', fakeAsync(() => {
const fixture = TestBed.createComponent(EmptyPageComponent);
fixture.detectChanges();
const overlay = fixture.componentInstance.overlay;
overlay.show(overlay.attach(SimpleDynamicComponent));
overlay.show(overlay.attach(SimpleDynamicComponent));
tick();
fixture.detectChanges();
let overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayDiv).toBeDefined();
expect(overlayDiv.children.length).toEqual(2);
expect((overlayDiv.children[0] as HTMLElement).style.visibility).toEqual('');
expect((overlayDiv.children[1] as HTMLElement).style.visibility).toEqual('');
overlay.hideAll();
tick();
overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect((overlayDiv.children[0] as HTMLElement).style.visibility).toEqual('hidden');
expect((overlayDiv.children[1] as HTMLElement).style.visibility).toEqual('hidden');
overlay.detachAll();
}));
it('Should show and hide component via directive.', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleDynamicWithDirectiveComponent);
fixture.detectChanges();
fixture.componentInstance.show();
tick();
let overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayDiv).toBeDefined();
expect(overlayDiv.children.length).toEqual(1);
expect(overlayDiv.children[0].localName).toEqual('div');
fixture.componentInstance.hide();
tick();
overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayDiv).toBeUndefined();
}));
it('Should properly emit events.', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleRefComponent);
fixture.detectChanges();
const overlayInstance = fixture.componentInstance.overlay;
spyOn(overlayInstance.closed, 'emit');
spyOn(overlayInstance.closing, 'emit');
spyOn(overlayInstance.opened, 'emit');
spyOn(overlayInstance.contentAppending, 'emit');
spyOn(overlayInstance.contentAppended, 'emit');
spyOn(overlayInstance.opening, 'emit');
spyOn(overlayInstance.animationStarting, 'emit');
const firstCallId = overlayInstance.attach(SimpleDynamicComponent);
overlayInstance.show(firstCallId);
tick();
expect(overlayInstance.opening.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.opening.emit)
.toHaveBeenCalledWith({ id: firstCallId, componentRef: jasmine.any(ComponentRef) as any, cancel: false });
const args: OverlayEventArgs = (overlayInstance.opening.emit as jasmine.Spy).calls.mostRecent().args[0];
expect(args.componentRef.instance).toEqual(jasmine.any(SimpleDynamicComponent));
expect(overlayInstance.contentAppending.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.contentAppended.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.animationStarting.emit).toHaveBeenCalledTimes(1);
tick();
expect(overlayInstance.opened.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.opened.emit).toHaveBeenCalledWith({ id: firstCallId, componentRef: jasmine.any(ComponentRef) as any });
overlayInstance.hide(firstCallId);
tick();
expect(overlayInstance.closing.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.closing.emit)
.toHaveBeenCalledWith({ id: firstCallId, componentRef: jasmine.any(ComponentRef) as any, cancel: false, event: undefined });
expect(overlayInstance.animationStarting.emit).toHaveBeenCalledTimes(2);
tick();
expect(overlayInstance.closed.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.closed.emit).
toHaveBeenCalledWith({ id: firstCallId, componentRef: jasmine.any(ComponentRef) as any, event: undefined });
const secondCallId = overlayInstance.attach(fixture.componentInstance.item);
overlayInstance.show(secondCallId);
tick();
expect(overlayInstance.opening.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.opening.emit).toHaveBeenCalledWith({ componentRef: undefined, id: secondCallId, cancel: false });
expect(overlayInstance.contentAppending.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.contentAppended.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.animationStarting.emit).toHaveBeenCalledTimes(3);
tick();
expect(overlayInstance.opened.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.opened.emit).toHaveBeenCalledWith({ componentRef: undefined, id: secondCallId });
overlayInstance.hide(secondCallId);
tick();
expect(overlayInstance.closing.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.closing.emit).
toHaveBeenCalledWith({ componentRef: undefined, id: secondCallId, cancel: false, event: undefined });
expect(overlayInstance.animationStarting.emit).toHaveBeenCalledTimes(4);
tick();
expect(overlayInstance.closed.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.closed.emit).toHaveBeenCalledWith({ componentRef: undefined, id: secondCallId, event: undefined });
overlayInstance.detachAll();
}));
it('Should properly emit contentAppending event', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleRefComponent);
fixture.detectChanges();
const overlayInstance = fixture.componentInstance.overlay;
const os: OverlaySettings = {
excludeFromOutsideClick: [],
positionStrategy: new GlobalPositionStrategy(),
scrollStrategy: new NoOpScrollStrategy(),
modal: true,
closeOnOutsideClick: true,
closeOnEscape: false
};
spyOn(overlayInstance.contentAppending, 'emit');
spyOn(overlayInstance.contentAppended, 'emit');
const firstCallId = overlayInstance.attach(SimpleDynamicComponent);
overlayInstance.show(firstCallId);
tick();
expect(overlayInstance.contentAppending.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.contentAppended.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.contentAppending.emit).toHaveBeenCalledWith({ id: firstCallId, elementRef: jasmine.any(Object),
componentRef: jasmine.any(ComponentRef) as any, settings: os })
}));
it('Should properly be able to override OverlaySettings using contentAppending event args', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleRefComponent);
fixture.detectChanges();
const overlayInstance = fixture.componentInstance.overlay;
const os: OverlaySettings = {
excludeFromOutsideClick: [],
positionStrategy: new GlobalPositionStrategy(),
scrollStrategy: new CloseScrollStrategy(),
modal: false,
closeOnOutsideClick: false,
closeOnEscape: true
};
overlayInstance.contentAppending.pipe(first()).subscribe((e: OverlayEventArgs) => {
// override the default settings
e.settings = os;
});
overlayInstance.contentAppended.pipe(first()).subscribe((e: OverlayEventArgs) => {
const overlay = overlayInstance.getOverlayById(e.id);
expect(overlay.settings.closeOnEscape).toBeTrue();
expect(overlay.settings.modal).toBeFalsy();
expect(overlay.settings.closeOnOutsideClick).toBeFalsy();
});
spyOn(overlayInstance.contentAppended, 'emit').and.callThrough();
spyOn(overlayInstance.contentAppending, 'emit').and.callThrough();
const firstCallId = overlayInstance.attach(SimpleDynamicComponent);
overlayInstance.show(firstCallId);
tick();
expect(overlayInstance.contentAppending.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.contentAppended.emit).toHaveBeenCalledTimes(1);
}));
it('Should properly set style on position method call - GlobalPosition.', () => {
const mockParent = document.createElement('div');
const mockItem = document.createElement('div');
mockParent.appendChild(mockItem);
const mockPositioningSettings1: PositionSettings = {
horizontalDirection: HorizontalAlignment.Right,
verticalDirection: VerticalAlignment.Bottom,
target: mockItem
};
const horAl = Object.keys(HorizontalAlignment).filter(key => !isNaN(Number(HorizontalAlignment[key])));
const verAl = Object.keys(VerticalAlignment).filter(key => !isNaN(Number(VerticalAlignment[key])));
const mockDirection: string[] = ['flex-start', 'center', 'flex-end'];
for (let i = 0; i < mockDirection.length; i++) {
for (let j = 0; j < mockDirection.length; j++) {
mockPositioningSettings1.horizontalDirection = HorizontalAlignment[horAl[i]];
mockPositioningSettings1.verticalDirection = VerticalAlignment[verAl[j]];
const globalStrat1 = new GlobalPositionStrategy(mockPositioningSettings1);
globalStrat1.position(mockItem);
expect(mockParent.style.justifyContent).toEqual(mockDirection[i]);
expect(mockParent.style.alignItems).toEqual(mockDirection[j]);
}
}
});
it('Should properly set style on position method call - ConnectedPosition.', () => {
const top = 0;
const left = 0;
const width = 200;
const right = 200;
const height = 200;
const bottom = 200;
const mockElement = document.createElement('div');
spyOn(mockElement, 'getBoundingClientRect').and.callFake(() => ({
left, top, width, height, right, bottom
} as DOMRect));
const mockItem = document.createElement('div');
mockElement.append(mockItem);
spyOn(mockItem, 'getBoundingClientRect').and.callFake(() => new DOMRect(top, left, width, height));
const mockPositioningSettings1: PositionSettings = {
horizontalDirection: HorizontalAlignment.Right,
verticalDirection: VerticalAlignment.Bottom,
horizontalStartPoint: HorizontalAlignment.Left,
verticalStartPoint: VerticalAlignment.Top
};
const connectedStrat1 = new ConnectedPositioningStrategy(mockPositioningSettings1);
connectedStrat1.position(mockItem, { width, height }, null, false, mockItem);
expect(mockItem.style.top).toEqual('0px');
expect(mockItem.style.left).toEqual('0px');
connectedStrat1.settings.horizontalStartPoint = HorizontalAlignment.Center;
connectedStrat1.position(mockItem, { width, height }, null, false, mockItem);
expect(mockItem.style.top).toEqual('0px');
expect(mockItem.style.left).toEqual('100px');
connectedStrat1.settings.horizontalStartPoint = HorizontalAlignment.Right;
connectedStrat1.position(mockItem, { width, height }, null, false, mockItem);
expect(mockItem.style.top).toEqual('0px');
expect(mockItem.style.left).toEqual('200px');
connectedStrat1.settings.verticalStartPoint = VerticalAlignment.Middle;
connectedStrat1.position(mockItem, { width, height }, null, false, mockItem);
expect(mockItem.style.top).toEqual('100px');
expect(mockItem.style.left).toEqual('200px');
connectedStrat1.settings.verticalStartPoint = VerticalAlignment.Bottom;
connectedStrat1.position(mockItem, { width, height }, null, false, mockItem);
expect(mockItem.style.top).toEqual('200px');
expect(mockItem.style.left).toEqual('200px');
// If target is Point
connectedStrat1.position(mockItem, { width, height }, null, false, new Point(0, 0));
expect(mockItem.style.top).toEqual('0px');
expect(mockItem.style.left).toEqual('0px');
// If target is not point or html element, should fallback to new Point(0,0)
connectedStrat1.position(mockItem, { width, height }, null, false, 'g' as any);
expect(mockItem.style.top).toEqual('0px');
expect(mockItem.style.left).toEqual('0px');
});
it('Should properly call position method - AutoPosition.', () => {
spyOn(BaseFitPositionStrategy.prototype, 'position');
spyOn<any>(ConnectedPositioningStrategy.prototype, 'setStyle');
const mockDiv = document.createElement('div');
const autoStrat1 = new AutoPositionStrategy();
autoStrat1.position(mockDiv, null, document, false);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledTimes(1);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledWith(mockDiv, null, document, false);
const autoStrat2 = new AutoPositionStrategy();
autoStrat2.position(mockDiv, null, document, false);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledTimes(2);
const autoStrat3 = new AutoPositionStrategy();
autoStrat3.position(mockDiv, null, document, false);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledTimes(3);
});
it('Should properly call position method - ElasticPosition.', () => {
spyOn(BaseFitPositionStrategy.prototype, 'position');
spyOn<any>(ConnectedPositioningStrategy.prototype, 'setStyle');
const mockDiv = document.createElement('div');
const autoStrat1 = new ElasticPositionStrategy();
autoStrat1.position(mockDiv, null, document, false);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledTimes(1);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledWith(mockDiv, null, document, false);
const autoStrat2 = new ElasticPositionStrategy();
autoStrat2.position(mockDiv, null, document, false);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledTimes(2);
const autoStrat3 = new ElasticPositionStrategy();
autoStrat3.position(mockDiv, null, document, false);
expect(BaseFitPositionStrategy.prototype.position).toHaveBeenCalledTimes(3);
});
it('Should properly call setOffset method', fakeAsync(() => {
const fixture = TestBed.createComponent(WidthTestOverlayComponent);
const overlayInstance = fixture.componentInstance.overlay;
const id = fixture.componentInstance.overlay.attach(SimpleRefComponent);
overlayInstance.show(id);
fixture.detectChanges();
tick();
overlayInstance.setOffset(id, 40, 40);
const contentElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0] as HTMLElement;
const componentElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName('simpleRef')[0] as HTMLElement;
const contentElementRect = contentElement.getBoundingClientRect();
const componentElementRect = componentElement.getBoundingClientRect();
let overlayContentTransform = contentElement.style.transform;
const firstTransform = 'translate(40px, 40px)';
const secondTransform = 'translate(30px, 60px)';
expect(contentElementRect.top).toEqual(componentElementRect.top);
expect(contentElementRect.left).toEqual(componentElementRect.left);
expect(overlayContentTransform).toEqual(firstTransform);
// Set the offset again and verify it is changed correctly
overlayInstance.setOffset(id, -10, 20);
fixture.detectChanges();
tick();
const contentElementRectNew = contentElement.getBoundingClientRect();
const componentElementRectNew = componentElement.getBoundingClientRect();
overlayContentTransform = contentElement.style.transform;
expect(contentElementRectNew.top).toEqual(componentElementRectNew.top);
expect(contentElementRectNew.left).toEqual(componentElementRectNew.left);
expect(contentElementRectNew.top).not.toEqual(contentElementRect.top);
expect(contentElementRectNew.left).not.toEqual(contentElementRect.left);
expect(componentElementRectNew.top).not.toEqual(componentElementRect.top);
expect(componentElementRectNew.left).not.toEqual(componentElementRect.left);
expect(overlayContentTransform).toEqual(secondTransform);
overlayInstance.detachAll();
}));
it('#1690 - click on second filter does not close first one.', fakeAsync(() => {
const fixture = TestBed.createComponent(TwoButtonsComponent);
const button1 = fixture.nativeElement.getElementsByClassName('buttonOne')[0];
const button2 = fixture.nativeElement.getElementsByClassName('buttonTwo')[0];
button1.click();
tick();
const overlayDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
const wrapperElement1 = overlayDiv.children[0] as HTMLElement;
expect(wrapperElement1.style.visibility).toEqual('');
button2.click();
tick();
const wrapperElement2 = overlayDiv.children[1] as HTMLElement;
expect(wrapperElement1.style.visibility).toEqual('');
expect(wrapperElement2.style.visibility).toEqual('');
fixture.componentInstance.overlay.detachAll();
}));
it('#1692 - scroll strategy closes overlay when shown component is scrolled.', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleDynamicWithDirectiveComponent);
const overlaySettings: OverlaySettings = { scrollStrategy: new CloseScrollStrategy() };
fixture.componentInstance.show(overlaySettings);
tick();
let overlayElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayElement).toBeDefined();
const scrollableDiv = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_SCROLLABLE_DIV)[0] as HTMLElement;
scrollableDiv.scrollTop += 5;
scrollableDiv.dispatchEvent(new Event('scroll'));
tick();
overlayElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayElement).toBeDefined();
scrollableDiv.scrollTop += 100;
scrollableDiv.dispatchEvent(new Event('scroll'));
tick();
overlayElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement;
expect(overlayElement).toBeDefined();
fixture.componentInstance.hide();
}));
// TODO: refactor utilities to include all exported methods in a class
// it('#1799 - content div should reposition on window resize.', fakeAsync(() => {
// const rect: ClientRect = {
// bottom: 50,
// height: 0,
// left: 50,
// right: 50,
// top: 50,
// width: 0
// };
// const getPointSpy = spyOn(Util, 'getTargetRect').and.returnValue(rect);
// const fixture = TestBed.createComponent(FlexContainerComponent);
// fixture.detectChanges();
// const overlayInstance = fixture.componentInstance.overlay;
// const buttonElement: HTMLElement = fixture.componentInstance.buttonElement.nativeElement;
// const id = overlayInstance.attach(
// SimpleDynamicComponent,
// { positionStrategy: new ConnectedPositioningStrategy({ target: buttonElement }) });
// overlayInstance.show(id);
// tick(DEBOUNCE_TIME);
// let contentElement = (fixture.nativeElement as HTMLElement)
// .parentElement.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0] as HTMLElement;
// let contentRect = contentElement.getBoundingClientRect();
// expect(50).toEqual(contentRect.left);
// expect(50).toEqual(contentRect.top);
// rect.left = 200;
// rect.right = 200;
// rect.top = 200;
// rect.bottom = 200;
// getPointSpy.and.callThrough().and.returnValue(rect);
// window.resizeBy(200, 200);
// window.dispatchEvent(new Event('resize'));
// tick(DEBOUNCE_TIME);
// contentElement = (fixture.nativeElement as HTMLElement)
// .parentElement.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0] as HTMLElement;
// contentRect = contentElement.getBoundingClientRect();
// expect(200).toEqual(contentRect.left);
// expect(200).toEqual(contentRect.top);
// overlayInstance.hide(id);
// }));
it('#2475 - An error is thrown for IgxOverlay when showing a component' +
'instance that is not attached to the DOM', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleRefComponent);
fixture.detectChanges();
const overlay = fixture.componentInstance.overlay;
// remove SimpleRefComponent HTML element from the DOM tree
fixture.elementRef.nativeElement.parentElement.removeChild(fixture.elementRef.nativeElement);
overlay.show(overlay.attach(fixture.elementRef));
tick(DEBOUNCE_TIME);
const componentElement = fixture.nativeElement as HTMLElement;
expect(componentElement).toBeDefined();
const contentElement = componentElement.parentElement;
expect(contentElement).toBeDefined();
expect(contentElement).toHaveClass(CLASS_OVERLAY_CONTENT_MODAL);
expect(contentElement).toHaveClass(CLASS_OVERLAY_CONTENT_RELATIVE);
const wrapperElement = contentElement.parentElement;
expect(wrapperElement).toBeDefined();
expect(wrapperElement).toHaveClass(CLASS_OVERLAY_WRAPPER_MODAL);
expect(wrapperElement).toHaveClass(CLASS_OVERLAY_WRAPPER_FLEX);
const overlayElement = wrapperElement.parentElement;
expect(overlayElement).toBeDefined();
expect(overlayElement).toHaveClass(CLASS_OVERLAY_MAIN);
overlay.detachAll();
}));
it('#2486 - filtering dropdown is not correctly positioned', fakeAsync(() => {
const fixture = TestBed.createComponent(WidthTestOverlayComponent);
fixture.debugElement.nativeElement.style.transform = 'translatex(100px)';
fixture.detectChanges();
tick();
fixture.componentInstance.overlaySettings.outlet = fixture.componentInstance.elementRef;
const buttonElement: HTMLElement = fixture.componentInstance.buttonElement.nativeElement;
buttonElement.click();
fixture.detectChanges();
tick();
const wrapperElement = (fixture.nativeElement as HTMLElement)
.parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0] as HTMLElement;
expect(wrapperElement.getBoundingClientRect().left).toBe(100);
expect(fixture.componentInstance.customComponent.nativeElement.getBoundingClientRect().left).toBe(400);
fixture.componentInstance.overlay.detachAll();
}));
it('#2798 - Allow canceling of open and close of IgxDropDown through opening and closing events', fakeAsync(() => {
const fixture = TestBed.createComponent(SimpleRefComponent);
fixture.detectChanges();
const overlayInstance = fixture.componentInstance.overlay;
overlayInstance.closing.subscribe((e: OverlayCancelableEventArgs) => {
e.cancel = true;
});
spyOn(overlayInstance.closed, 'emit').and.callThrough();
spyOn(overlayInstance.closing, 'emit').and.callThrough();
spyOn(overlayInstance.opened, 'emit').and.callThrough();
spyOn(overlayInstance.opening, 'emit').and.callThrough();
const firstCallId = overlayInstance.attach(SimpleDynamicComponent);
overlayInstance.show(firstCallId);
tick();
expect(overlayInstance.opening.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.opened.emit).toHaveBeenCalledTimes(1);
overlayInstance.hide(firstCallId);
tick();
expect(overlayInstance.closing.emit).toHaveBeenCalledTimes(1);
expect(overlayInstance.closed.emit).toHaveBeenCalledTimes(0);
overlayInstance.opening.subscribe((e: OverlayCancelableEventArgs) => {
e.cancel = true;
});
overlayInstance.show(firstCallId);
tick();
expect(overlayInstance.opening.emit).toHaveBeenCalledTimes(2);
expect(overlayInstance.opened.emit).toHaveBeenCalledTimes(1);
overlayInstance.detachAll();
}));
it('#3673 - Should not close dropdown in dropdown', fakeAsync(() =>