UNPKG

ipsos-components

Version:

Material Design components for Angular

346 lines (270 loc) 12.6 kB
import {Component, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; import {ComponentFixture, TestBed, async, inject} from '@angular/core/testing'; import {Directionality} from '@angular/cdk/bidi'; import {dispatchKeyboardEvent} from '@angular/cdk/testing'; import {ESCAPE} from '@angular/cdk/keycodes'; import {CdkConnectedOverlay, OverlayModule, CdkOverlayOrigin} from './index'; import {OverlayContainer} from './overlay-container'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; import {ConnectedOverlayPositionChange} from './position/connected-position'; describe('Overlay directives', () => { let overlayContainer: OverlayContainer; let overlayContainerElement: HTMLElement; let fixture: ComponentFixture<ConnectedOverlayDirectiveTest>; let dir: {value: string}; beforeEach(() => { TestBed.configureTestingModule({ imports: [OverlayModule], declarations: [ConnectedOverlayDirectiveTest, ConnectedOverlayPropertyInitOrder], providers: [ {provide: Directionality, useFactory: () => { return dir = {value: 'ltr'}; }} ], }); }); beforeEach(() => { fixture = TestBed.createComponent(ConnectedOverlayDirectiveTest); fixture.detectChanges(); }); beforeEach(inject([OverlayContainer], (oc: OverlayContainer) => { overlayContainer = oc; overlayContainerElement = oc.getContainerElement(); })); afterEach(() => { overlayContainer.ngOnDestroy(); }); /** Returns the current open overlay pane element. */ function getPaneElement() { return overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; } it(`should attach the overlay based on the open property`, () => { fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(overlayContainerElement.textContent).toContain('Menu content'); expect(getPaneElement().style.pointerEvents) .toBe('auto', 'Expected the overlay pane to enable pointerEvents when attached.'); fixture.componentInstance.isOpen = false; fixture.detectChanges(); expect(overlayContainerElement.textContent).toBe(''); expect(getPaneElement().style.pointerEvents) .toBe('none', 'Expected the overlay pane to disable pointerEvents when detached.'); }); it('should destroy the overlay when the directive is destroyed', () => { fixture.componentInstance.isOpen = true; fixture.detectChanges(); fixture.destroy(); expect(overlayContainerElement.textContent!.trim()).toBe(''); expect(getPaneElement()) .toBeFalsy('Expected the overlay pane element to be removed when disposed.'); }); it('should use a connected position strategy with a default set of positions', () => { fixture.componentInstance.isOpen = true; fixture.detectChanges(); let testComponent: ConnectedOverlayDirectiveTest = fixture.debugElement.componentInstance; let overlayDirective = testComponent.connectedOverlayDirective; let strategy = <ConnectedPositionStrategy> overlayDirective.overlayRef.getConfig().positionStrategy; expect(strategy instanceof ConnectedPositionStrategy).toBe(true); let positions = strategy.positions; expect(positions.length).toBeGreaterThan(0); }); it('should set and update the `dir` attribute', () => { dir.value = 'rtl'; fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(getPaneElement().getAttribute('dir')).toBe('rtl'); fixture.componentInstance.isOpen = false; fixture.detectChanges(); dir.value = 'ltr'; fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(getPaneElement().getAttribute('dir')).toBe('ltr'); }); it('should close when pressing escape', () => { fixture.componentInstance.isOpen = true; fixture.detectChanges(); dispatchKeyboardEvent(document, 'keydown', ESCAPE); fixture.detectChanges(); expect(overlayContainerElement.textContent!.trim()).toBe('', 'Expected overlay to have been detached.'); }); it('should not depend on the order in which the `origin` and `open` are set', async(() => { fixture.destroy(); const propOrderFixture = TestBed.createComponent(ConnectedOverlayPropertyInitOrder); propOrderFixture.detectChanges(); const overlayDirective = propOrderFixture.componentInstance.connectedOverlayDirective; expect(() => { overlayDirective.open = true; overlayDirective.origin = propOrderFixture.componentInstance.trigger; propOrderFixture.detectChanges(); }).not.toThrow(); })); describe('inputs', () => { it('should set the width', () => { fixture.componentInstance.width = 250; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.width).toEqual('250px'); }); it('should set the height', () => { fixture.componentInstance.height = '100vh'; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.height).toEqual('100vh'); }); it('should set the min width', () => { fixture.componentInstance.minWidth = 250; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.minWidth).toEqual('250px'); }); it('should set the min height', () => { fixture.componentInstance.minHeight = '500px'; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.minHeight).toEqual('500px'); }); it('should create the backdrop if designated', () => { fixture.componentInstance.hasBackdrop = true; fixture.componentInstance.isOpen = true; fixture.detectChanges(); let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); expect(backdrop).toBeTruthy(); }); it('should not create the backdrop by default', () => { fixture.componentInstance.isOpen = true; fixture.detectChanges(); let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); expect(backdrop).toBeNull(); }); it('should set the custom backdrop class', () => { fixture.componentInstance.hasBackdrop = true; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; expect(backdrop.classList).toContain('mat-test-class'); }); it('should set the offsetX', () => { const trigger = fixture.debugElement.query(By.css('button')).nativeElement; const startX = trigger.getBoundingClientRect().left; fixture.componentInstance.offsetX = 5; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.left) .toBe(startX + 5 + 'px', `Expected overlay translateX to equal the original X + the offsetX.`); fixture.componentInstance.isOpen = false; fixture.detectChanges(); fixture.componentInstance.offsetX = 15; fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(pane.style.left) .toBe(startX + 15 + 'px', `Expected overlay directive to reflect new offsetX if it changes.`); }); it('should set the offsetY', () => { const trigger = fixture.debugElement.query(By.css('button')).nativeElement; trigger.style.position = 'absolute'; trigger.style.top = '30px'; trigger.style.height = '20px'; fixture.componentInstance.offsetY = 45; fixture.componentInstance.isOpen = true; fixture.detectChanges(); // expected y value is the starting y + trigger height + offset y // 30 + 20 + 45 = 95px const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.top) .toBe('95px', `Expected overlay translateY to equal the start Y + height + offsetY.`); fixture.componentInstance.isOpen = false; fixture.detectChanges(); fixture.componentInstance.offsetY = 55; fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(pane.style.top) .toBe('105px', `Expected overlay directive to reflect new offsetY if it changes.`); }); }); describe('outputs', () => { it('should emit backdropClick appropriately', () => { fixture.componentInstance.hasBackdrop = true; fixture.componentInstance.isOpen = true; fixture.detectChanges(); const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; backdrop.click(); fixture.detectChanges(); expect(fixture.componentInstance.backdropClicked).toBe(true); }); it('should emit positionChange appropriately', () => { expect(fixture.componentInstance.positionChangeHandler).not.toHaveBeenCalled(); fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(fixture.componentInstance.positionChangeHandler).toHaveBeenCalled(); const latestCall = fixture.componentInstance.positionChangeHandler.calls.mostRecent(); expect(latestCall.args[0] instanceof ConnectedOverlayPositionChange) .toBe(true, `Expected directive to emit an instance of ConnectedOverlayPositionChange.`); }); it('should emit attach and detach appropriately', () => { expect(fixture.componentInstance.attachHandler).not.toHaveBeenCalled(); expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled(); fixture.componentInstance.isOpen = true; fixture.detectChanges(); expect(fixture.componentInstance.attachHandler).toHaveBeenCalled(); expect(fixture.componentInstance.attachResult instanceof HTMLElement) .toBe(true, `Expected pane to be populated with HTML elements when attach was called.`); expect(fixture.componentInstance.detachHandler).not.toHaveBeenCalled(); fixture.componentInstance.isOpen = false; fixture.detectChanges(); expect(fixture.componentInstance.detachHandler).toHaveBeenCalled(); }); }); }); @Component({ template: ` <button cdk-overlay-origin #trigger="cdkOverlayOrigin">Toggle menu</button> <ng-template cdk-connected-overlay [open]="isOpen" [width]="width" [height]="height" [origin]="trigger" [hasBackdrop]="hasBackdrop" backdropClass="mat-test-class" (backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY" (positionChange)="positionChangeHandler($event)" (attach)="attachHandler()" (detach)="detachHandler()" [minWidth]="minWidth" [minHeight]="minHeight"> <p>Menu content</p> </ng-template>`, }) class ConnectedOverlayDirectiveTest { isOpen = false; width: number | string; height: number | string; minWidth: number | string; minHeight: number | string; offsetX: number = 0; offsetY: number = 0; hasBackdrop: boolean; backdropClicked = false; positionChangeHandler = jasmine.createSpy('positionChangeHandler'); attachHandler = jasmine.createSpy('attachHandler').and.callFake(() => { this.attachResult = this.connectedOverlayDirective.overlayRef.overlayElement.querySelector('p') as HTMLElement; }); detachHandler = jasmine.createSpy('detachHandler'); attachResult: HTMLElement; @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; } @Component({ template: ` <button cdk-overlay-origin #trigger="cdkOverlayOrigin">Toggle menu</button> <ng-template cdk-connected-overlay>Menu content</ng-template>`, }) class ConnectedOverlayPropertyInitOrder { @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; @ViewChild('trigger') trigger: CdkOverlayOrigin; }