diff --git a/projects/igniteui-angular/src/lib/core/touch.ts b/projects/igniteui-angular/src/lib/core/touch.ts index daf8640a082..65580066f9e 100644 --- a/projects/igniteui-angular/src/lib/core/touch.ts +++ b/projects/igniteui-angular/src/lib/core/touch.ts @@ -1,6 +1,7 @@ import { Inject, Injectable, NgZone } from '@angular/core'; import { ɵgetDOM as getDOM } from '@angular/platform-browser'; import { DOCUMENT } from '@angular/common'; +import { PlatformUtil } from './utils'; const EVENT_SUFFIX = 'precise'; @@ -11,26 +12,31 @@ const EVENT_SUFFIX = 'precise'; */ @Injectable() export class HammerGesturesManager { + private platformBrowser: boolean; /** * Event option defaults for each recognizer, see http://hammerjs.github.io/api/ for API listing. */ - protected hammerOptions: HammerOptions = { - // D.P. #447 Force TouchInput due to PointerEventInput bug (https://github.com/hammerjs/hammer.js/issues/1065) - // see https://github.com/IgniteUI/igniteui-angular/issues/447#issuecomment-324601803 - inputClass: Hammer.TouchInput, - recognizers: [ - [ Hammer.Pan, { threshold: 0 } ], - [ Hammer.Swipe, { - direction: Hammer.DIRECTION_HORIZONTAL - }], - [Hammer.Tap], - [Hammer.Tap, { event: 'doubletap', taps: 2 }, ['tap']] - ] - }; + protected hammerOptions: HammerOptions = {}; private _hammerManagers: Array<{ element: EventTarget, manager: HammerManager; }> = []; - constructor(private _zone: NgZone, @Inject(DOCUMENT) private doc: any) { + constructor(private _zone: NgZone, @Inject(DOCUMENT) private doc: any, private platformUtil: PlatformUtil) { + this.platformBrowser = this.platformUtil.isBrowser; + if (this.platformBrowser) { + this.hammerOptions = { + // D.P. #447 Force TouchInput due to PointerEventInput bug (https://github.com/hammerjs/hammer.js/issues/1065) + // see https://github.com/IgniteUI/igniteui-angular/issues/447#issuecomment-324601803 + inputClass: Hammer.TouchInput, + recognizers: [ + [Hammer.Pan, { threshold: 0 }], + [Hammer.Swipe, { + direction: Hammer.DIRECTION_HORIZONTAL + }], + [Hammer.Tap], + [Hammer.Tap, { event: 'doubletap', taps: 2 }, ['tap']] + ] + }; + } } public supports(eventName: string): boolean { @@ -41,10 +47,14 @@ export class HammerGesturesManager { * Add listener extended with options for Hammer.js. Will use defaults if none are provided. * Modeling after other event plugins for easy future modifications. */ - public addEventListener(element: HTMLElement, - eventName: string, - eventHandler: (eventObj) => void, - options: HammerOptions = null): () => void { + public addEventListener( + element: HTMLElement, + eventName: string, + eventHandler: (eventObj) => void, + options: HammerOptions = null): () => void { + if (!this.platformBrowser) { + return; + } // Creating the manager bind events, must be done outside of angular return this._zone.runOutsideAngular(() => { @@ -67,6 +77,10 @@ export class HammerGesturesManager { * @param target Can be one of either window, body or document(fallback default). */ public addGlobalEventListener(target: string, eventName: string, eventHandler: (eventObj) => void): () => void { + if (!this.platformBrowser) { + return; + } + const element = this.getGlobalEventTarget(target); // Creating the manager bind events, must be done outside of angular diff --git a/projects/igniteui-angular/src/lib/core/utils.ts b/projects/igniteui-angular/src/lib/core/utils.ts index 4a62129e24f..0bf147ad16b 100644 --- a/projects/igniteui-angular/src/lib/core/utils.ts +++ b/projects/igniteui-angular/src/lib/core/utils.ts @@ -1,3 +1,6 @@ +import { Injectable, PLATFORM_ID, Inject } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; + /** *@hidden */ @@ -228,12 +231,14 @@ export function isFirefox(): boolean { /** * @hidden - * TODO: make injectable, check isPlatformBrowser() */ +@Injectable({ providedIn: 'root' }) export class PlatformUtil { - static isIOS(): boolean { - const iosBrowser = typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window); - return iosBrowser; + public isBrowser: boolean = isPlatformBrowser(this.platformId); + + public isIOS = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && !('MSStream' in window); + + constructor(@Inject(PLATFORM_ID) private platformId: Object) { } } @@ -246,8 +251,21 @@ export function isLeftClick(event: PointerEvent) { /** @hidden */ export function isNavigationKey(key: string): boolean { - return ['down', 'up', 'left', 'right', 'arrowdown', 'arrowup', 'arrowleft', 'arrowright', - 'home', 'end', 'space', 'spacebar', ' '].indexOf(key) !== -1; + return [ + 'down', + 'up', + 'left', + 'right', + 'arrowdown', + 'arrowup', + 'arrowleft', + 'arrowright', + 'home', + 'end', + 'space', + 'spacebar', + ' ' + ].indexOf(key) !== -1; } /** @@ -278,8 +296,21 @@ export interface CancelableBrowserEventArgs extends CancelableEventArgs { event?: Event; } -export const NAVIGATION_KEYS = new Set(['down', 'up', 'left', 'right', 'arrowdown', 'arrowup', 'arrowleft', 'arrowright', - 'home', 'end', 'space', 'spacebar', ' ']); +export const NAVIGATION_KEYS = new Set([ + 'down', + 'up', + 'left', + 'right', + 'arrowdown', + 'arrowup', + 'arrowleft', + 'arrowright', + 'home', + 'end', + 'space', + 'spacebar', + ' ' +]); export const ROW_EXPAND_KEYS = new Set('right down arrowright arrowdown'.split(' ')); export const ROW_COLLAPSE_KEYS = new Set('left up arrowleft arrowup'.split(' ')); export const SUPPORTED_KEYS = new Set([...Array.from(NAVIGATION_KEYS), 'tab', 'enter', 'f2', 'escape', 'esc']); diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 5d92e91bfd4..32005748c76 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -538,7 +538,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy { public cdr: ChangeDetectorRef, private element: ElementRef, protected zone: NgZone, - private touchManager: HammerGesturesManager) { } + private touchManager: HammerGesturesManager, + protected platformUtil: PlatformUtil) { } /** @@ -560,7 +561,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy { this.nativeElement.addEventListener('compositionend', this.compositionEndHandler); } }); - if (PlatformUtil.isIOS()) { + if (this.platformUtil.isIOS) { this.touchManager.addEventListener(this.nativeElement, 'doubletap', this.onDoubleClick, { cssProps: { } /* don't disable user-select, etc */ } as HammerOptions); diff --git a/projects/igniteui-angular/src/lib/grids/grid/cell.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/cell.spec.ts index 60f198f9951..098efcabb7f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/cell.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/cell.spec.ts @@ -193,19 +193,26 @@ describe('IgxGrid - Cell component', () => { it('Should not attach doubletap handler for non-iOS', () => { const addListenerSpy = spyOn(HammerGesturesManager.prototype, 'addEventListener'); - spyOn(PlatformUtil, 'isIOS').and.returnValue(false); + const platformUtil: PlatformUtil = TestBed.get(PlatformUtil); + const oldIsIOS = platformUtil.isIOS; + platformUtil.isIOS = false; const fix = TestBed.createComponent(DefaultGridComponent); fix.detectChanges(); + // spyOnProperty(PlatformUtil.prototype, 'isIOS').and.returnValue(false); + expect(addListenerSpy).not.toHaveBeenCalled(); + + platformUtil.isIOS = oldIsIOS; }); it('Should handle doubletap on iOS, trigger onDoubleClick event', () => { const addListenerSpy = spyOn(HammerGesturesManager.prototype, 'addEventListener'); - spyOn(PlatformUtil, 'isIOS').and.returnValue(true); + const platformUtil: PlatformUtil = TestBed.get(PlatformUtil); + const oldIsIOS = platformUtil.isIOS; + platformUtil.isIOS = true; const fix = TestBed.createComponent(DefaultGridComponent); fix.detectChanges(); const grid = fix.componentInstance.instance; - const cellElem = fix.debugElement.query(By.css(CELL_CSS_CLASS)); const firstCell = grid.getCellByColumn(0, 'index'); // should attach 'doubletap' @@ -228,6 +235,8 @@ describe('IgxGrid - Cell component', () => { expect(event.preventDefault).toHaveBeenCalled(); expect(grid.onDoubleClick.emit).toHaveBeenCalledWith(args); expect(firstCell).toBe(fix.componentInstance.clickedCell); + + platformUtil.isIOS = oldIsIOS; }); it('Should blur selected cell when scrolling with mouse wheel', (async () => { diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts index 06c0dbdba4c..a2635964356 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts @@ -6,6 +6,7 @@ import { IgxHierarchicalGridComponent } from './hierarchical-grid.component'; import { IgxHierarchicalSelectionAPIService } from './selection'; import { IgxGridSelectionService, IgxGridCRUDService } from '../../core/grid-selection'; import { HammerGesturesManager } from '../../core/touch'; +import { PlatformUtil } from '../../core/utils'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -27,9 +28,10 @@ export class IgxHierarchicalGridCellComponent extends IgxGridCellComponent imple public cdr: ChangeDetectorRef, private helement: ElementRef, protected zone: NgZone, - touchManager: HammerGesturesManager + touchManager: HammerGesturesManager, + protected platformUtil: PlatformUtil ) { - super(selectionService, crudService, gridAPI, selection, cdr, helement, zone, touchManager); + super(selectionService, crudService, gridAPI, selection, cdr, helement, zone, touchManager, platformUtil); this.hSelection = selection; } diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.ts index 7cc36bb10d1..5979f056874 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.ts @@ -3,7 +3,7 @@ import { IgxGridCellComponent } from '../cell.component'; import { IgxTreeGridAPIService } from './tree-grid-api.service'; import { GridBaseAPIService } from '../api.service'; import { IgxSelectionAPIService } from '../../core/selection'; -import { getNodeSizeViaRange } from '../../core/utils'; +import { getNodeSizeViaRange, PlatformUtil } from '../../core/utils'; import { DOCUMENT } from '@angular/common'; import { IgxGridBaseComponent, IGridDataBindable } from '../grid'; import { IgxGridSelectionService, IgxGridCRUDService } from '../../core/grid-selection'; @@ -27,8 +27,9 @@ export class IgxTreeGridCellComponent extends IgxGridCellComponent implements On element: ElementRef, protected zone: NgZone, touchManager: HammerGesturesManager, - @Inject(DOCUMENT) public document) { - super(selectionService, crudService, gridAPI, selection, cdr, element, zone, touchManager); + @Inject(DOCUMENT) public document, + protected platformUtil: PlatformUtil) { + super(selectionService, crudService, gridAPI, selection, cdr, element, zone, touchManager, platformUtil); this.treeGridAPI = gridAPI; } diff --git a/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.spec.ts b/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.spec.ts index 730b8c250b5..36af4b07945 100644 --- a/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.spec.ts +++ b/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.spec.ts @@ -1,18 +1,12 @@ -import { - async, - ComponentFixture, - fakeAsync, - TestBed, - tick -} from '@angular/core/testing'; - -import { Component, ViewChild } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Component, ViewChild, PLATFORM_ID } from '@angular/core'; import { By } from '@angular/platform-browser'; import { wait } from '../test-utils/ui-interactions.spec'; import { IgxNavigationDrawerModule } from './navigation-drawer.module'; import { IgxNavigationToggleDirective, IgxNavigationCloseDirective } from '../core/navigation/directives'; import { IgxNavigationDrawerComponent } from './navigation-drawer.component'; import { IgxNavigationService } from '../core/navigation/nav.service'; +import { PlatformUtil } from '../core/utils'; // HammerJS simulator from https://github.com/hammerjs/simulator, manual typings TODO declare var Simulator: any; @@ -45,13 +39,17 @@ describe('Navigation Drawer', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = oldTimeout; }); + afterAll(() => { + TestBed.resetTestingModule(); + }); + it('should initialize without DI service', async(() => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); - expect(fixture.componentInstance.viewChild instanceof + expect(fixture.componentInstance.navDrawer instanceof IgxNavigationDrawerComponent).toBeTruthy(); - expect(fixture.componentInstance.viewChild.state).toBeNull(); + expect(fixture.componentInstance.navDrawer.state).toBeNull(); }); })); @@ -60,10 +58,10 @@ describe('Navigation Drawer', () => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - expect(fixture.componentInstance.viewChild).toBeDefined(); - expect(fixture.componentInstance.viewChild instanceof + expect(fixture.componentInstance.navDrawer).toBeDefined(); + expect(fixture.componentInstance.navDrawer instanceof IgxNavigationDrawerComponent).toBeTruthy(); - expect(fixture.componentInstance.viewChild.state instanceof IgxNavigationService) + expect(fixture.componentInstance.navDrawer.state instanceof IgxNavigationService) .toBeTruthy(); }); })); @@ -75,8 +73,8 @@ describe('Navigation Drawer', () => { const fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); - expect(fixture.componentInstance.viewChild).toBeDefined(); - expect(fixture.componentInstance.viewChild instanceof + expect(fixture.componentInstance.navDrawer).toBeDefined(); + expect(fixture.componentInstance.navDrawer instanceof IgxNavigationDrawerComponent).toBeTruthy(); expect(() => fixture.destroy()).not.toThrow(); }); @@ -88,17 +86,17 @@ describe('Navigation Drawer', () => { fixture.detectChanges(); const domNavDrawer = fixture.debugElement.query(By.css('igx-nav-drawer')).nativeElement; - expect(fixture.componentInstance.viewChild.id).toContain('igx-nav-drawer-'); + expect(fixture.componentInstance.navDrawer.id).toContain('igx-nav-drawer-'); expect(domNavDrawer.id).toContain('igx-nav-drawer-'); - expect(fixture.componentInstance.viewChild.drawer.classList).toContain('igx-nav-drawer__aside'); - expect(fixture.componentInstance.viewChild.overlay.classList).toContain('igx-nav-drawer__overlay'); - expect(fixture.componentInstance.viewChild.styleDummy.classList).toContain('igx-nav-drawer__style-dummy'); - expect(fixture.componentInstance.viewChild.hasAnimateWidth).toBeFalsy(); + expect(fixture.componentInstance.navDrawer.drawer.classList).toContain('igx-nav-drawer__aside'); + expect(fixture.componentInstance.navDrawer.overlay.classList).toContain('igx-nav-drawer__overlay'); + expect(fixture.componentInstance.navDrawer.styleDummy.classList).toContain('igx-nav-drawer__style-dummy'); + expect(fixture.componentInstance.navDrawer.hasAnimateWidth).toBeFalsy(); - fixture.componentInstance.viewChild.id = 'customNavDrawer'; + fixture.componentInstance.navDrawer.id = 'customNavDrawer'; fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.id).toBe('customNavDrawer'); + expect(fixture.componentInstance.navDrawer.id).toBe('customNavDrawer'); expect(domNavDrawer.id).toBe('customNavDrawer'); }).catch((reason) => { @@ -118,8 +116,8 @@ describe('Navigation Drawer', () => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - const state: IgxNavigationService = fixture.componentInstance.viewChild.state; - const touchManager = fixture.componentInstance.viewChild.touchManager; + const state: IgxNavigationService = fixture.componentInstance.navDrawer.state; + const touchManager = fixture.componentInstance.navDrawer.touchManager; expect(state.get('testNav')).toBeDefined(); expect(touchManager.getManagerForElement(document) instanceof Hammer.Manager).toBeTruthy(); @@ -137,7 +135,7 @@ describe('Navigation Drawer', () => { TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - const drawer: IgxNavigationDrawerComponent = fixture.componentInstance.viewChild; + const drawer: IgxNavigationDrawerComponent = fixture.componentInstance.navDrawer; expect(drawer.isOpen).toBeFalsy(); drawer.open(); @@ -168,7 +166,7 @@ describe('Navigation Drawer', () => { TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - drawer = fixture.componentInstance.viewChild; + drawer = fixture.componentInstance.navDrawer; spyOn(drawer.closing, 'emit'); spyOn(drawer.closed, 'emit'); @@ -210,7 +208,7 @@ describe('Navigation Drawer', () => { const fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.hasAnimateWidth).toBeTruthy(); + expect(fixture.componentInstance.navDrawer.hasAnimateWidth).toBeTruthy(); expect(fixture.debugElement.query((x) => x.nativeNode.nodeName === 'ASIDE').nativeElement.classList) .toContain('igx-nav-drawer__aside--mini'); }).catch((reason) => { @@ -249,7 +247,7 @@ describe('Navigation Drawer', () => { fixture.componentInstance.miniView = true; fixture.detectChanges(); - expect(asideElem.styles['width']).toEqual(fixture.componentInstance.viewChild.miniWidth); + expect(asideElem.styles['width']).toEqual(fixture.componentInstance.navDrawer.miniWidth); }).catch((reason) => { return Promise.reject(reason); }); @@ -269,15 +267,15 @@ describe('Navigation Drawer', () => { const fixture = TestBed.createComponent(TestComponentPin); fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.pin).toBeTruthy(); + expect(fixture.componentInstance.navDrawer.pin).toBeTruthy(); expect(fixture.debugElement.query((x) => x.nativeNode.nodeName === 'ASIDE').nativeElement.classList) .toContain('igx-nav-drawer__aside--pinned'); - expect(fixture.componentInstance.viewChild.enableGestures).toBe(false); + expect(fixture.componentInstance.navDrawer.enableGestures).toBe(false); fixture.componentInstance.enableGestures = 'true'; fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.enableGestures).toBeTruthy(); + expect(fixture.componentInstance.navDrawer.enableGestures).toBeTruthy(); }).catch((reason) => { return Promise.reject(reason); @@ -326,7 +324,7 @@ describe('Navigation Drawer', () => { TestBed.compileComponents() .then(() => { const fixture = TestBed.createComponent(TestComponentPin); - const drawer = fixture.componentInstance.viewChild; + const drawer = fixture.componentInstance.navDrawer; drawer.isOpen = true; fixture.detectChanges(); const drawerElem = fixture.debugElement.query((x) => x.nativeNode.nodeName === 'IGX-NAV-DRAWER').nativeElement; @@ -355,22 +353,22 @@ describe('Navigation Drawer', () => { TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.isOpen).toEqual(false); + expect(fixture.componentInstance.navDrawer.isOpen).toEqual(false); // timeouts are +50 on the gesture to allow the swipe to be detected and triggered after the touches: return swipe(document.body, 80, 10, 100, 250, 0); }) .then(() => { - expect(fixture.componentInstance.viewChild.isOpen) + expect(fixture.componentInstance.navDrawer.isOpen) .toEqual(false, 'should ignore swipes too far away from the edge'); return swipe(document.body, 10, 10, 150, 250, 0); }) .then(() => { - expect(fixture.componentInstance.viewChild.isOpen).toEqual(true, 'Should accept edge swipe'); + expect(fixture.componentInstance.navDrawer.isOpen).toEqual(true, 'Should accept edge swipe'); return swipe(document.body, 180, 10, 150, -180, 0); }) .then(() => { - expect(fixture.componentInstance.viewChild.isOpen).toEqual(false); + expect(fixture.componentInstance.navDrawer.isOpen).toEqual(false); done(); }) .catch((reason) => { @@ -386,9 +384,9 @@ describe('Navigation Drawer', () => { TestBed.compileComponents().then(() => { fixture = TestBed.createComponent(TestComponentDIComponent); fixture.detectChanges(); - navDrawer = fixture.componentInstance.viewChild; + navDrawer = fixture.componentInstance.navDrawer; - expect(fixture.componentInstance.viewChild.isOpen).toEqual(false); + expect(fixture.componentInstance.navDrawer.isOpen).toEqual(false); const listener = navDrawer.renderer.listen(document.body, 'panmove', () => { @@ -443,12 +441,12 @@ describe('Navigation Drawer', () => { fixture.componentInstance.drawerMiniWidth = 68; fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.maxEdgeZone) + expect(fixture.componentInstance.navDrawer.maxEdgeZone) .toBe(fixture.componentInstance.drawerMiniWidth * 1.1); fixture.componentInstance.drawerMiniWidth = 80; fixture.detectChanges(); - expect(fixture.componentInstance.viewChild.maxEdgeZone) + expect(fixture.componentInstance.navDrawer.maxEdgeZone) .toBe(fixture.componentInstance.drawerMiniWidth * 1.1); }).catch((reason) => { @@ -456,7 +454,7 @@ describe('Navigation Drawer', () => { }); })); - it('should update width from css or property', fakeAsync((done) => { + it('should update width from css or property', async(done) => { const template = ` @@ -469,41 +467,36 @@ describe('Navigation Drawer', () => { }); // compile after overrides, not in before each: https://github.com/angular/angular/issues/10712 - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestComponentDIComponent); - fixture.detectChanges(); - // const comp: DebugElement = fixture.debugElement.query(By.component(IgxNavbarComponent)); + await TestBed.compileComponents(); + fixture = TestBed.createComponent(TestComponentDIComponent); + fixture.detectChanges(); + // const comp: DebugElement = fixture.debugElement.query(By.component(IgxNavbarComponent)); - // defaults: - expect(fixture.componentInstance.viewChild.drawer.style.width).toBe(''); - return fixture.componentInstance.viewChild.open(); - }) - .then(() => { - expect(fixture.componentInstance.viewChild.drawer.style.width).toBe(''); + // defaults: + expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe(''); + fixture.componentInstance.navDrawer.open(); + await wait(200); - fixture.componentInstance.drawerMiniWidth = '80px'; - fixture.componentInstance.drawerWidth = '250px'; - fixture.detectChanges(); - return fixture.whenStable(); // let changes apply in the meantime - }) - .then(() => { - expect(fixture.componentInstance.viewChild.drawer.style.width).toBe('250px'); - return fixture.componentInstance.viewChild.close(); - }) - .then(() => { - tick(1000); - expect(fixture.componentInstance.viewChild.drawer.style.width).toBe('80px'); - fixture.componentInstance.drawerWidth = '350px'; - fixture.detectChanges(); - return fixture.componentInstance.viewChild.open(); - }) - .then(() => { - expect(fixture.componentInstance.viewChild.drawer.style.width).toBe('350px'); - done(); - }).catch((reason) => { - return Promise.reject(reason); - }); - })); + expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe(''); + + fixture.componentInstance.drawerMiniWidth = '80px'; + fixture.componentInstance.drawerWidth = '250px'; + fixture.detectChanges(); + await wait(200); + + expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe('250px'); + fixture.componentInstance.navDrawer.close(); + await wait(200); + + expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe('80px'); + fixture.componentInstance.drawerWidth = '350px'; + fixture.detectChanges(); + fixture.componentInstance.navDrawer.open(); + await wait(200); + + expect(fixture.componentInstance.navDrawer.drawer.style.width).toBe('350px'); + done(); + }); it('should update pin based on window width (pinThreshold)', async (done) => { const template = `''`; @@ -523,7 +516,7 @@ describe('Navigation Drawer', () => { await fixture.whenStable(); // defaults: - expect(fixture.componentInstance.viewChild.pin).toBe(false, 'Should be initially unpinned'); + expect(fixture.componentInstance.navDrawer.pin).toBe(false, 'Should be initially unpinned'); expect(fixture.componentInstance.pin).toBe(false, 'Parent component pin should update initially'); // manual pin override @@ -533,7 +526,7 @@ describe('Navigation Drawer', () => { // wait for debounce await wait(200); - expect(fixture.componentInstance.viewChild.pin).toBe(false, `Shouldn't change state on resize if window width is the same`); + expect(fixture.componentInstance.navDrawer.pin).toBe(false, `Shouldn't change state on resize if window width is the same`); expect(fixture.componentInstance.pin).toBe(true, 'Parent component pin remain on resize if window width is the same'); fixture.componentInstance.pin = true; fixture.detectChanges(); @@ -543,7 +536,7 @@ describe('Navigation Drawer', () => { // wait for debounce await wait(200); - expect(fixture.componentInstance.viewChild.pin).toBe(true, 'Should pin on window resize over threshold'); + expect(fixture.componentInstance.navDrawer.pin).toBe(true, 'Should pin on window resize over threshold'); expect(fixture.componentInstance.pin).toBe(true, 'Parent pin update on window resize over threshold'); widthSpyOverride.and.returnValue(768); @@ -551,24 +544,29 @@ describe('Navigation Drawer', () => { // wait for debounce await wait(200); - expect(fixture.componentInstance.viewChild.pin).toBe(false, 'Should un-pin on window resize below threshold'); + expect(fixture.componentInstance.navDrawer.pin).toBe(false, 'Should un-pin on window resize below threshold'); expect(fixture.componentInstance.pin).toBe(false, 'Parent pin update on window resize below threshold'); fixture.componentInstance.pinThreshold = 500; expect(() => fixture.detectChanges()).not.toThrow(); await fixture.whenStable(); - expect(fixture.componentInstance.viewChild.pin).toBe(true, 'Should re-pin on window resize over threshold'); + expect(fixture.componentInstance.navDrawer.pin).toBe(true, 'Should re-pin on window resize over threshold'); expect(fixture.componentInstance.pin).toBe(true, 'Parent pin update on re-pin'); done(); }); it('should get correct window width', (done) => { const originalWidth = window.innerWidth; + const platformUtil: PlatformUtil = new PlatformUtil(TestBed.get(PLATFORM_ID)); + const drawer = new IgxNavigationDrawerComponent(null, null, null, null, platformUtil); // re-enable `getWindowWidth` const widthSpy = (widthSpyOverride as jasmine.Spy).and.callThrough(); - expect(widthSpy.call(null)).toEqual(originalWidth); + let width = widthSpy.call(drawer); + expect(width).toEqual(originalWidth); + (window as any).innerWidth = 0; // not that readonly in Chrome - expect(widthSpy.call(null)).toEqual(screen.width); + width = widthSpy.call(drawer); + expect(width).toEqual(screen.width); (window as any).innerWidth = originalWidth; done(); }); @@ -615,7 +613,7 @@ describe('Navigation Drawer', () => { template: '' }) class TestComponent { - @ViewChild(IgxNavigationDrawerComponent, { static: true }) public viewChild: IgxNavigationDrawerComponent; + @ViewChild(IgxNavigationDrawerComponent, { static: true }) public navDrawer: IgxNavigationDrawerComponent; } @Component({ @@ -626,7 +624,7 @@ class TestComponent { class TestComponentDIComponent { public drawerMiniWidth: string | number; public drawerWidth: string | number; - @ViewChild(IgxNavigationDrawerComponent, { static: true }) public viewChild: IgxNavigationDrawerComponent; + @ViewChild(IgxNavigationDrawerComponent, { static: true }) public navDrawer: IgxNavigationDrawerComponent; } class TestComponentPin extends TestComponentDIComponent { diff --git a/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.ts b/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.ts index edc5bad8ff8..710cbce5b24 100644 --- a/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.ts +++ b/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.ts @@ -14,7 +14,6 @@ import { Output, Renderer, SimpleChange, - TemplateRef, ViewChild } from '@angular/core'; import { fromEvent, interval, Subscription } from 'rxjs'; @@ -22,6 +21,7 @@ import { debounce } from 'rxjs/operators'; import { IgxNavigationService, IToggleView } from '../core/navigation'; import { HammerGesturesManager } from '../core/touch'; import { IgxNavDrawerMiniTemplateDirective, IgxNavDrawerTemplateDirective } from './navigation-drawer.directives'; +import { PlatformUtil } from '../core/utils'; let NEXT_ID = 0; /** @@ -400,7 +400,8 @@ export class IgxNavigationDrawerComponent implements @Optional() private _state: IgxNavigationService, // private animate: AnimationBuilder, TODO protected renderer: Renderer, - private _touchManager: HammerGesturesManager) { + private _touchManager: HammerGesturesManager, + private platformUtil: PlatformUtil) { } /** @@ -602,11 +603,15 @@ export class IgxNavigationDrawerComponent implements * Sets the drawer width. */ private setDrawerWidth(width: string) { - requestAnimationFrame(() => { - if (this.drawer) { - this.renderer.setElementStyle(this.drawer, 'width', width); - } - }); + if (this.platformUtil.isBrowser) { + requestAnimationFrame(() => { + if (this.drawer) { + this.renderer.setElementStyle(this.drawer, 'width', width); + } + }); + } else { + this.renderer.setElementStyle(this.drawer, 'width', width); + } } /** @@ -632,7 +637,7 @@ export class IgxNavigationDrawerComponent implements this._touchManager.addGlobalEventListener('document', 'panmove', this.pan); this._touchManager.addGlobalEventListener('document', 'panend', this.panEnd); } - if (!this._resizeObserver) { + if (!this._resizeObserver && this.platformUtil.isBrowser) { this._resizeObserver = fromEvent(window, 'resize').pipe(debounce(() => interval(150))) .subscribe((value) => { this.checkPinThreshold(value); @@ -650,6 +655,9 @@ export class IgxNavigationDrawerComponent implements } private checkPinThreshold = (evt?: Event) => { + if (!this.platformUtil.isBrowser) { + return; + } let windowWidth; if (this.pinThreshold) { windowWidth = this.getWindowWidth(); @@ -793,9 +801,9 @@ export class IgxNavigationDrawerComponent implements if (this.hasAnimateWidth) { this.renderer.setElementStyle(this.drawer, 'width', x ? Math.abs(x) + 'px' : ''); } else { - this.renderer.setElementStyle(this.drawer, 'transform', x ? 'translate3d(' + x + 'px,0,0)' : ''); - this.renderer.setElementStyle(this.drawer, '-webkit-transform', - x ? 'translate3d(' + x + 'px,0,0)' : ''); + const transform = x ? 'translate3d(' + x + 'px,0,0)' : ''; + this.renderer.setElementStyle(this.drawer, 'transform', transform); + this.renderer.setElementStyle(this.drawer, '-webkit-transform', transform); } if (opacity !== undefined) { this.renderer.setElementStyle(this.overlay, 'opacity', opacity);