UNPKG

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
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(() =>