From 3892e095416cd385ad4c45c7d82807ab8cea6816 Mon Sep 17 00:00:00 2001 From: mmalerba Date: Wed, 13 Sep 2017 09:29:05 -0700 Subject: [PATCH 1/5] Revert "Revert "fix(menu): multiple close events for a single close" (#7036)" This reverts commit dcfe515f6ba866e87320ee4bd5c1ffcaa128dbf5. --- src/lib/menu/menu-trigger.ts | 28 ++++++++++++++---------- src/lib/menu/menu.spec.ts | 42 ++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index 045b872296e6..67b8f17db2b2 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -43,8 +43,8 @@ import {MdMenuItem} from './menu-item'; import {MdMenuPanel} from './menu-panel'; import {MenuPositionX, MenuPositionY} from './menu-positions'; import {throwMdMenuMissingError} from './menu-errors'; -import {of as observableOf} from 'rxjs/observable/of'; import {merge} from 'rxjs/observable/merge'; +import {of as observableOf} from 'rxjs/observable/of'; import {Subscription} from 'rxjs/Subscription'; /** Injection token that determines the scroll handling while the menu is open. */ @@ -153,7 +153,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { this._checkMenu(); this.menu.close.subscribe(reason => { - this.closeMenu(); + this._destroyMenu(); // If a click closed the menu, we should close the entire chain of nested menus. if (reason === 'click' && this._parentMenu) { @@ -205,7 +205,9 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { openMenu(): void { if (!this._menuOpen) { this._createOverlay().attach(this._portal); - this._closeSubscription = this._menuClosingActions().subscribe(() => this.menu.close.emit()); + this._closeSubscription = this._menuClosingActions().subscribe(() => { + this.menu.close.emit(); + }); this._initMenu(); if (this.menu instanceof MdMenu) { @@ -216,11 +218,20 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { /** Closes the menu. */ closeMenu(): void { + this.menu.close.emit(); + } + + /** Focuses the menu trigger. */ + focus() { + this._element.nativeElement.focus(); + } + + /** Closes the menu and does the necessary cleanup. */ + private _destroyMenu() { if (this._overlayRef && this.menuOpen) { this._resetMenu(); this._overlayRef.detach(); this._closeSubscription.unsubscribe(); - this.menu.close.emit(); if (this.menu instanceof MdMenu) { this.menu._resetAnimation(); @@ -228,11 +239,6 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { } } - /** Focuses the menu trigger. */ - focus() { - this._element.nativeElement.focus(); - } - /** * This method sets the menu state to open and focuses the first item if * the menu was opened via the keyboard. @@ -400,11 +406,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { /** Returns a stream that emits whenever an action that should close the menu occurs. */ private _menuClosingActions() { const backdrop = this._overlayRef!.backdropClick(); - const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(null); + const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(); const hover = this._parentMenu ? RxChain.from(this._parentMenu.hover()) .call(filter, active => active !== this._menuItemInstance) .call(filter, () => this._menuOpen) - .result() : observableOf(null); + .result() : observableOf(); return merge(backdrop, parentClose, hover); } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index 6006dcab4f6b..68b6ebd87d68 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -97,7 +97,7 @@ describe('MdMenu', () => { expect(overlayContainerElement.textContent).toBe(''); })); - it('should close the menu when pressing escape', fakeAsync(() => { + it('should close the menu when pressing ESCAPE', fakeAsync(() => { const fixture = TestBed.createComponent(SimpleMenu); fixture.detectChanges(); fixture.componentInstance.trigger.openMenu(); @@ -493,26 +493,40 @@ describe('MdMenu', () => { menuItem.click(); fixture.detectChanges(); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalled(); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('click'); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); }); it('should emit a close event when the backdrop is clicked', () => { - const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); + const backdrop = overlayContainerElement + .querySelector('.cdk-overlay-backdrop') as HTMLElement; backdrop.click(); fixture.detectChanges(); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalled(); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith(undefined); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); + }); + + it('should emit an event when pressing ESCAPE', () => { + const menu = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement; + + dispatchKeyboardEvent(menu, 'keydown', ESCAPE); + fixture.detectChanges(); + + expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('keydown'); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); }); it('should complete the callback when the menu is destroyed', () => { - let emitCallback = jasmine.createSpy('emit callback'); - let completeCallback = jasmine.createSpy('complete callback'); + const emitCallback = jasmine.createSpy('emit callback'); + const completeCallback = jasmine.createSpy('complete callback'); fixture.componentInstance.menu.close.subscribe(emitCallback, null, completeCallback); fixture.destroy(); - expect(emitCallback).toHaveBeenCalled(); + expect(emitCallback).toHaveBeenCalledWith(undefined); + expect(emitCallback).toHaveBeenCalledTimes(1); expect(completeCallback).toHaveBeenCalled(); }); }); @@ -994,6 +1008,9 @@ describe('MdMenu', () => { tick(500); expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus'); + expect(instance.rootCloseCallback).toHaveBeenCalledTimes(1); + expect(instance.levelOneCloseCallback).toHaveBeenCalledTimes(1); + expect(instance.levelTwoCloseCallback).toHaveBeenCalledTimes(1); })); it('should toggle a nested menu when its trigger is added after init', fakeAsync(() => { @@ -1044,7 +1061,7 @@ describe('MdMenu default overrides', () => { @Component({ template: ` - + @@ -1137,7 +1154,7 @@ class CustomMenu { [mdMenuTriggerFor]="levelTwo" #alternateTrigger="mdMenuTrigger">Toggle alternate menu - + - + - + @@ -1177,12 +1194,15 @@ class NestedMenu { @ViewChild('rootTrigger') rootTrigger: MdMenuTrigger; @ViewChild('rootTriggerEl') rootTriggerEl: ElementRef; @ViewChild('alternateTrigger') alternateTrigger: MdMenuTrigger; + readonly rootCloseCallback = jasmine.createSpy('root menu closed callback'); @ViewChild('levelOne') levelOneMenu: MdMenu; @ViewChild('levelOneTrigger') levelOneTrigger: MdMenuTrigger; + readonly levelOneCloseCallback = jasmine.createSpy('level one menu closed callback'); @ViewChild('levelTwo') levelTwoMenu: MdMenu; @ViewChild('levelTwoTrigger') levelTwoTrigger: MdMenuTrigger; + readonly levelTwoCloseCallback = jasmine.createSpy('level one menu closed callback'); @ViewChild('lazy') lazyMenu: MdMenu; @ViewChild('lazyTrigger') lazyTrigger: MdMenuTrigger; From 4bddcee0b82c885a5d641070f3ea31ded4486a31 Mon Sep 17 00:00:00 2001 From: mmalerba Date: Wed, 13 Sep 2017 09:29:05 -0700 Subject: [PATCH 2/5] Revert "feat(datepicker): Add Moment.js adapter (#6860)" This reverts commit 9545427c73627f0cf91b5086efd5d727459fc44f. From 4011b40dedcaada73b66cf717d66a615ad8bf454 Mon Sep 17 00:00:00 2001 From: mmalerba Date: Wed, 13 Sep 2017 09:29:05 -0700 Subject: [PATCH 3/5] Revert "fix(menu): multiple close events for a single close (#6961)" This reverts commit 1cccd4bda6aa6dbe366d4510f92630fcdd525c92. --- src/lib/menu/menu-trigger.ts | 28 ++++++++++-------------- src/lib/menu/menu.spec.ts | 42 ++++++++++-------------------------- 2 files changed, 22 insertions(+), 48 deletions(-) diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index 67b8f17db2b2..045b872296e6 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -43,8 +43,8 @@ import {MdMenuItem} from './menu-item'; import {MdMenuPanel} from './menu-panel'; import {MenuPositionX, MenuPositionY} from './menu-positions'; import {throwMdMenuMissingError} from './menu-errors'; -import {merge} from 'rxjs/observable/merge'; import {of as observableOf} from 'rxjs/observable/of'; +import {merge} from 'rxjs/observable/merge'; import {Subscription} from 'rxjs/Subscription'; /** Injection token that determines the scroll handling while the menu is open. */ @@ -153,7 +153,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { this._checkMenu(); this.menu.close.subscribe(reason => { - this._destroyMenu(); + this.closeMenu(); // If a click closed the menu, we should close the entire chain of nested menus. if (reason === 'click' && this._parentMenu) { @@ -205,9 +205,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { openMenu(): void { if (!this._menuOpen) { this._createOverlay().attach(this._portal); - this._closeSubscription = this._menuClosingActions().subscribe(() => { - this.menu.close.emit(); - }); + this._closeSubscription = this._menuClosingActions().subscribe(() => this.menu.close.emit()); this._initMenu(); if (this.menu instanceof MdMenu) { @@ -218,20 +216,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { /** Closes the menu. */ closeMenu(): void { - this.menu.close.emit(); - } - - /** Focuses the menu trigger. */ - focus() { - this._element.nativeElement.focus(); - } - - /** Closes the menu and does the necessary cleanup. */ - private _destroyMenu() { if (this._overlayRef && this.menuOpen) { this._resetMenu(); this._overlayRef.detach(); this._closeSubscription.unsubscribe(); + this.menu.close.emit(); if (this.menu instanceof MdMenu) { this.menu._resetAnimation(); @@ -239,6 +228,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { } } + /** Focuses the menu trigger. */ + focus() { + this._element.nativeElement.focus(); + } + /** * This method sets the menu state to open and focuses the first item if * the menu was opened via the keyboard. @@ -406,11 +400,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { /** Returns a stream that emits whenever an action that should close the menu occurs. */ private _menuClosingActions() { const backdrop = this._overlayRef!.backdropClick(); - const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(); + const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(null); const hover = this._parentMenu ? RxChain.from(this._parentMenu.hover()) .call(filter, active => active !== this._menuItemInstance) .call(filter, () => this._menuOpen) - .result() : observableOf(); + .result() : observableOf(null); return merge(backdrop, parentClose, hover); } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index 68b6ebd87d68..6006dcab4f6b 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -97,7 +97,7 @@ describe('MdMenu', () => { expect(overlayContainerElement.textContent).toBe(''); })); - it('should close the menu when pressing ESCAPE', fakeAsync(() => { + it('should close the menu when pressing escape', fakeAsync(() => { const fixture = TestBed.createComponent(SimpleMenu); fixture.detectChanges(); fixture.componentInstance.trigger.openMenu(); @@ -493,40 +493,26 @@ describe('MdMenu', () => { menuItem.click(); fixture.detectChanges(); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('click'); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalled(); }); it('should emit a close event when the backdrop is clicked', () => { - const backdrop = overlayContainerElement - .querySelector('.cdk-overlay-backdrop') as HTMLElement; + const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); backdrop.click(); fixture.detectChanges(); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith(undefined); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); - }); - - it('should emit an event when pressing ESCAPE', () => { - const menu = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement; - - dispatchKeyboardEvent(menu, 'keydown', ESCAPE); - fixture.detectChanges(); - - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('keydown'); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalled(); }); it('should complete the callback when the menu is destroyed', () => { - const emitCallback = jasmine.createSpy('emit callback'); - const completeCallback = jasmine.createSpy('complete callback'); + let emitCallback = jasmine.createSpy('emit callback'); + let completeCallback = jasmine.createSpy('complete callback'); fixture.componentInstance.menu.close.subscribe(emitCallback, null, completeCallback); fixture.destroy(); - expect(emitCallback).toHaveBeenCalledWith(undefined); - expect(emitCallback).toHaveBeenCalledTimes(1); + expect(emitCallback).toHaveBeenCalled(); expect(completeCallback).toHaveBeenCalled(); }); }); @@ -1008,9 +994,6 @@ describe('MdMenu', () => { tick(500); expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus'); - expect(instance.rootCloseCallback).toHaveBeenCalledTimes(1); - expect(instance.levelOneCloseCallback).toHaveBeenCalledTimes(1); - expect(instance.levelTwoCloseCallback).toHaveBeenCalledTimes(1); })); it('should toggle a nested menu when its trigger is added after init', fakeAsync(() => { @@ -1061,7 +1044,7 @@ describe('MdMenu default overrides', () => { @Component({ template: ` - + @@ -1154,7 +1137,7 @@ class CustomMenu { [mdMenuTriggerFor]="levelTwo" #alternateTrigger="mdMenuTrigger">Toggle alternate menu - + - + - + @@ -1194,15 +1177,12 @@ class NestedMenu { @ViewChild('rootTrigger') rootTrigger: MdMenuTrigger; @ViewChild('rootTriggerEl') rootTriggerEl: ElementRef; @ViewChild('alternateTrigger') alternateTrigger: MdMenuTrigger; - readonly rootCloseCallback = jasmine.createSpy('root menu closed callback'); @ViewChild('levelOne') levelOneMenu: MdMenu; @ViewChild('levelOneTrigger') levelOneTrigger: MdMenuTrigger; - readonly levelOneCloseCallback = jasmine.createSpy('level one menu closed callback'); @ViewChild('levelTwo') levelTwoMenu: MdMenu; @ViewChild('levelTwoTrigger') levelTwoTrigger: MdMenuTrigger; - readonly levelTwoCloseCallback = jasmine.createSpy('level one menu closed callback'); @ViewChild('lazy') lazyMenu: MdMenu; @ViewChild('lazyTrigger') lazyTrigger: MdMenuTrigger; From 18fb4a1f89677927b86013bc9a6a30863dd77306 Mon Sep 17 00:00:00 2001 From: mmalerba Date: Wed, 13 Sep 2017 09:29:05 -0700 Subject: [PATCH 4/5] Revert "fix(menu): nested trigger staying highlighted after click (#6853)" This reverts commit 04bf3d1fd54402a5582f6043e2db02fde4598516. From 700d8f7f59d2b7682bfa804eb690de383be3957c Mon Sep 17 00:00:00 2001 From: mmalerba Date: Wed, 13 Sep 2017 09:29:05 -0700 Subject: [PATCH 5/5] Revert "feat(viewport-ruler): add common window resize handler (#6680)" This reverts commit 881630f31d2ebf55ce5c9d8d8cfc1ed59333743c. --- src/cdk/scrolling/scroll-dispatcher.spec.ts | 1 + src/cdk/scrolling/scroll-dispatcher.ts | 6 ++- src/cdk/scrolling/viewport-ruler.spec.ts | 40 +-------------- src/cdk/scrolling/viewport-ruler.ts | 52 ++++---------------- src/lib/tabs/tab-group.spec.ts | 6 ++- src/lib/tabs/tab-header.spec.ts | 4 +- src/lib/tabs/tab-header.ts | 9 ++-- src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts | 8 +-- src/lib/tabs/tab-nav-bar/tab-nav-bar.ts | 18 ++++--- 9 files changed, 45 insertions(+), 99 deletions(-) diff --git a/src/cdk/scrolling/scroll-dispatcher.spec.ts b/src/cdk/scrolling/scroll-dispatcher.spec.ts index 5f10625f990a..d08dc5f8cf4c 100644 --- a/src/cdk/scrolling/scroll-dispatcher.spec.ts +++ b/src/cdk/scrolling/scroll-dispatcher.spec.ts @@ -72,6 +72,7 @@ describe('Scroll Dispatcher', () => { scroll.scrolled(0, () => {}); dispatchFakeEvent(document, 'scroll'); + dispatchFakeEvent(window, 'resize'); expect(spy).not.toHaveBeenCalled(); subscription.unsubscribe(); diff --git a/src/cdk/scrolling/scroll-dispatcher.ts b/src/cdk/scrolling/scroll-dispatcher.ts index 0e0e5b6aa1d1..177f830ac2c0 100644 --- a/src/cdk/scrolling/scroll-dispatcher.ts +++ b/src/cdk/scrolling/scroll-dispatcher.ts @@ -11,6 +11,7 @@ import {Platform} from '@angular/cdk/platform'; import {Subject} from 'rxjs/Subject'; import {Subscription} from 'rxjs/Subscription'; import {fromEvent} from 'rxjs/observable/fromEvent'; +import {merge} from 'rxjs/observable/merge'; import {auditTime} from 'rxjs/operator/auditTime'; import {Scrollable} from './scrollable'; @@ -86,7 +87,10 @@ export class ScrollDispatcher { if (!this._globalSubscription) { this._globalSubscription = this._ngZone.runOutsideAngular(() => { - return fromEvent(window.document, 'scroll').subscribe(() => this._notify()); + return merge( + fromEvent(window.document, 'scroll'), + fromEvent(window, 'resize') + ).subscribe(() => this._notify()); }); } diff --git a/src/cdk/scrolling/viewport-ruler.spec.ts b/src/cdk/scrolling/viewport-ruler.spec.ts index 3eab7427d48d..349b00efb186 100644 --- a/src/cdk/scrolling/viewport-ruler.spec.ts +++ b/src/cdk/scrolling/viewport-ruler.spec.ts @@ -1,7 +1,6 @@ -import {TestBed, inject, fakeAsync, tick} from '@angular/core/testing'; +import {TestBed, inject} from '@angular/core/testing'; import {ScrollDispatchModule} from './public_api'; import {ViewportRuler, VIEWPORT_RULER_PROVIDER} from './viewport-ruler'; -import {dispatchFakeEvent} from '@angular/cdk/testing'; // For all tests, we assume the browser window is 1024x786 (outerWidth x outerHeight). @@ -33,10 +32,6 @@ describe('ViewportRuler', () => { scrollTo(0, 0); })); - afterEach(() => { - ruler.ngOnDestroy(); - }); - it('should get the viewport bounds when the page is not scrolled', () => { let bounds = ruler.getViewportRect(); expect(bounds.top).toBe(0); @@ -106,37 +101,4 @@ describe('ViewportRuler', () => { document.body.removeChild(veryLargeElement); }); - - describe('changed event', () => { - it('should dispatch an event when the window is resized', () => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(0).subscribe(spy); - - dispatchFakeEvent(window, 'resize'); - expect(spy).toHaveBeenCalled(); - subscription.unsubscribe(); - }); - - it('should dispatch an event when the orientation is changed', () => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(0).subscribe(spy); - - dispatchFakeEvent(window, 'orientationchange'); - expect(spy).toHaveBeenCalled(); - subscription.unsubscribe(); - }); - - it('should be able to throttle the callback', fakeAsync(() => { - const spy = jasmine.createSpy('viewport changed spy'); - const subscription = ruler.change(1337).subscribe(spy); - - dispatchFakeEvent(window, 'resize'); - expect(spy).not.toHaveBeenCalled(); - - tick(1337); - - expect(spy).toHaveBeenCalledTimes(1); - subscription.unsubscribe(); - })); - }); }); diff --git a/src/cdk/scrolling/viewport-ruler.ts b/src/cdk/scrolling/viewport-ruler.ts index 1643cf687da0..fbd55ad9e2d6 100644 --- a/src/cdk/scrolling/viewport-ruler.ts +++ b/src/cdk/scrolling/viewport-ruler.ts @@ -6,49 +6,23 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injectable, Optional, SkipSelf, NgZone, OnDestroy} from '@angular/core'; -import {Platform} from '@angular/cdk/platform'; +import {Injectable, Optional, SkipSelf} from '@angular/core'; import {ScrollDispatcher} from './scroll-dispatcher'; -import {Observable} from 'rxjs/Observable'; -import {fromEvent} from 'rxjs/observable/fromEvent'; -import {merge} from 'rxjs/observable/merge'; -import {auditTime} from 'rxjs/operator/auditTime'; -import {Subscription} from 'rxjs/Subscription'; -import {of as observableOf} from 'rxjs/observable/of'; -/** Time in ms to throttle the resize events by default. */ -export const DEFAULT_RESIZE_TIME = 20; /** * Simple utility for getting the bounds of the browser viewport. * @docs-private */ @Injectable() -export class ViewportRuler implements OnDestroy { +export class ViewportRuler { /** Cached document client rectangle. */ private _documentRect?: ClientRect; - /** Stream of viewport change events. */ - private _change: Observable; - - /** Subscriptions to streams that invalidate the cached viewport dimensions. */ - private _invalidateCacheSubscriptions: Subscription[]; - - constructor(platform: Platform, ngZone: NgZone, scrollDispatcher: ScrollDispatcher) { - this._change = platform.isBrowser ? ngZone.runOutsideAngular(() => { - return merge(fromEvent(window, 'resize'), fromEvent(window, 'orientationchange')); - }) : observableOf(); - + constructor(scrollDispatcher: ScrollDispatcher) { // Subscribe to scroll and resize events and update the document rectangle on changes. - this._invalidateCacheSubscriptions = [ - scrollDispatcher.scrolled(0, () => this._cacheViewportGeometry()), - this.change().subscribe(() => this._cacheViewportGeometry()) - ]; - } - - ngOnDestroy() { - this._invalidateCacheSubscriptions.forEach(subscription => subscription.unsubscribe()); + scrollDispatcher.scrolled(0, () => this._cacheViewportGeometry()); } /** Gets a ClientRect for the viewport's bounds. */ @@ -82,6 +56,7 @@ export class ViewportRuler implements OnDestroy { }; } + /** * Gets the (top, left) scroll position of the viewport. * @param documentRect @@ -100,7 +75,7 @@ export class ViewportRuler implements OnDestroy { // `document.documentElement` works consistently, where the `top` and `left` values will // equal negative the scroll position. const top = -documentRect!.top || document.body.scrollTop || window.scrollY || - document.documentElement.scrollTop || 0; + document.documentElement.scrollTop || 0; const left = -documentRect!.left || document.body.scrollLeft || window.scrollX || document.documentElement.scrollLeft || 0; @@ -108,32 +83,23 @@ export class ViewportRuler implements OnDestroy { return {top, left}; } - /** - * Returns a stream that emits whenever the size of the viewport changes. - * @param throttle Time in milliseconds to throttle the stream. - */ - change(throttleTime: number = DEFAULT_RESIZE_TIME): Observable { - return throttleTime > 0 ? auditTime.call(this._change, throttleTime) : this._change; - } - /** Caches the latest client rectangle of the document element. */ _cacheViewportGeometry() { this._documentRect = document.documentElement.getBoundingClientRect(); } + } /** @docs-private */ export function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler: ViewportRuler, - platform: Platform, - ngZone: NgZone, scrollDispatcher: ScrollDispatcher) { - return parentRuler || new ViewportRuler(platform, ngZone, scrollDispatcher); + return parentRuler || new ViewportRuler(scrollDispatcher); } /** @docs-private */ export const VIEWPORT_RULER_PROVIDER = { // If there is already a ViewportRuler available, use that. Otherwise, provide a new one. provide: ViewportRuler, - deps: [[new Optional(), new SkipSelf(), ViewportRuler], Platform, NgZone, ScrollDispatcher], + deps: [[new Optional(), new SkipSelf(), ViewportRuler], ScrollDispatcher], useFactory: VIEWPORT_RULER_PROVIDER_FACTORY }; diff --git a/src/lib/tabs/tab-group.spec.ts b/src/lib/tabs/tab-group.spec.ts index 0718faa6c7ad..a82befc61f89 100644 --- a/src/lib/tabs/tab-group.spec.ts +++ b/src/lib/tabs/tab-group.spec.ts @@ -2,7 +2,8 @@ import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/t import {Component, QueryList, ViewChild, ViewChildren} from '@angular/core'; import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; import {By} from '@angular/platform-browser'; -import {dispatchFakeEvent} from '@angular/cdk/testing'; +import {ViewportRuler} from '@angular/cdk/scrolling'; +import {dispatchFakeEvent, FakeViewportRuler} from '@angular/cdk/testing'; import {Observable} from 'rxjs/Observable'; import {MdTab, MdTabGroup, MdTabHeaderPosition, MdTabsModule} from './index'; @@ -18,6 +19,9 @@ describe('MdTabGroup', () => { AsyncTabsTestApp, DisabledTabsTestApp, TabGroupWithSimpleApi, + ], + providers: [ + {provide: ViewportRuler, useClass: FakeViewportRuler}, ] }); diff --git a/src/lib/tabs/tab-header.spec.ts b/src/lib/tabs/tab-header.spec.ts index 564f6fe53cc9..64239dfc60a2 100644 --- a/src/lib/tabs/tab-header.spec.ts +++ b/src/lib/tabs/tab-header.spec.ts @@ -6,8 +6,9 @@ import {CommonModule} from '@angular/common'; import {By} from '@angular/platform-browser'; import {ENTER, LEFT_ARROW, RIGHT_ARROW, SPACE} from '@angular/cdk/keycodes'; import {PortalModule} from '@angular/cdk/portal'; +import {ViewportRuler} from '@angular/cdk/scrolling'; import {Direction, Directionality} from '@angular/cdk/bidi'; -import {dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing'; +import {dispatchFakeEvent, dispatchKeyboardEvent, FakeViewportRuler} from '@angular/cdk/testing'; import {MdTabHeader} from './tab-header'; import {MdRippleModule} from '../core/ripple/index'; import {MdInkBar} from './ink-bar'; @@ -34,6 +35,7 @@ describe('MdTabHeader', () => { ], providers: [ {provide: Directionality, useFactory: () => ({value: dir, change: change.asObservable()})}, + {provide: ViewportRuler, useClass: FakeViewportRuler}, ] }); diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index 17ceb10e02e6..00f0987bc69e 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -26,14 +26,14 @@ import { } from '@angular/core'; import {Directionality, Direction} from '@angular/cdk/bidi'; import {RIGHT_ARROW, LEFT_ARROW, ENTER, SPACE} from '@angular/cdk/keycodes'; -import {startWith} from '@angular/cdk/rxjs'; +import {auditTime, startWith} from '@angular/cdk/rxjs'; import {Subscription} from 'rxjs/Subscription'; import {of as observableOf} from 'rxjs/observable/of'; import {merge} from 'rxjs/observable/merge'; +import {fromEvent} from 'rxjs/observable/fromEvent'; import {MdTabLabelWrapper} from './tab-label-wrapper'; import {MdInkBar} from './ink-bar'; import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple'; -import {ViewportRuler} from '@angular/cdk/scrolling'; /** * The directions that scrolling can go in when the header's tabs exceed the header width. 'After' @@ -132,7 +132,6 @@ export class MdTabHeader extends _MdTabHeaderMixinBase constructor(private _elementRef: ElementRef, private _renderer: Renderer2, private _changeDetectorRef: ChangeDetectorRef, - private _viewportRuler: ViewportRuler, @Optional() private _dir: Directionality) { super(); } @@ -185,7 +184,9 @@ export class MdTabHeader extends _MdTabHeaderMixinBase */ ngAfterContentInit() { const dirChange = this._dir ? this._dir.change : observableOf(null); - const resize = this._viewportRuler.change(150); + const resize = typeof window !== 'undefined' ? + auditTime.call(fromEvent(window, 'resize'), 150) : + observableOf(null); this._realignInkBar = startWith.call(merge(dirChange, resize), null).subscribe(() => { this._updatePagination(); diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts index 6328fc651918..e38e006ad6a0 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts @@ -1,7 +1,8 @@ import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {Component, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; -import {dispatchFakeEvent, dispatchMouseEvent} from '@angular/cdk/testing'; +import {ViewportRuler} from '@angular/cdk/scrolling'; +import {dispatchFakeEvent, dispatchMouseEvent, FakeViewportRuler} from '@angular/cdk/testing'; import {Direction, Directionality} from '@angular/cdk/bidi'; import {Subject} from 'rxjs/Subject'; import {MdTabNav, MdTabsModule, MdTabLink} from '../index'; @@ -22,7 +23,8 @@ describe('MdTabNavBar', () => { {provide: Directionality, useFactory: () => ({ value: dir, change: dirChange.asObservable() - })} + })}, + {provide: ViewportRuler, useClass: FakeViewportRuler}, ] }); @@ -171,7 +173,7 @@ describe('MdTabNavBar', () => { spyOn(inkBar, 'alignToElement'); dispatchFakeEvent(window, 'resize'); - tick(150); + tick(10); fixture.detectChanges(); expect(inkBar.alignToElement).toHaveBeenCalled(); diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts index 3e5e961d0f27..d247017fe418 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts @@ -28,10 +28,11 @@ import { import {ViewportRuler} from '@angular/cdk/scrolling'; import {Directionality} from '@angular/cdk/bidi'; import {Platform} from '@angular/cdk/platform'; -import {takeUntil} from '@angular/cdk/rxjs'; +import {auditTime, takeUntil} from '@angular/cdk/rxjs'; import {Subject} from 'rxjs/Subject'; import {of as observableOf} from 'rxjs/observable/of'; import {merge} from 'rxjs/observable/merge'; +import {fromEvent} from 'rxjs/observable/fromEvent'; import {CanDisableRipple, mixinDisableRipple} from '../../core/common-behaviors/disable-ripple'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {CanDisable, mixinDisabled} from '../../core/common-behaviors/disabled'; @@ -104,8 +105,7 @@ export class MdTabNav extends _MdTabNavMixinBase implements AfterContentInit, Ca elementRef: ElementRef, @Optional() private _dir: Directionality, private _ngZone: NgZone, - private _changeDetectorRef: ChangeDetectorRef, - private _viewportRuler: ViewportRuler) { + private _changeDetectorRef: ChangeDetectorRef) { super(renderer, elementRef); } @@ -121,10 +121,14 @@ export class MdTabNav extends _MdTabNavMixinBase implements AfterContentInit, Ca ngAfterContentInit(): void { this._ngZone.runOutsideAngular(() => { - const dirChange = this._dir ? this._dir.change : observableOf(null); - - return takeUntil.call(merge(dirChange, this._viewportRuler.change(10)), this._onDestroy) - .subscribe(() => this._alignInkBar()); + let dirChange = this._dir ? this._dir.change : observableOf(null); + let resize = typeof window !== 'undefined' ? + auditTime.call(fromEvent(window, 'resize'), 10) : + observableOf(null); + + return takeUntil.call(merge(dirChange, resize), this._onDestroy).subscribe(() => { + this._alignInkBar(); + }); }); this._setLinkDisableRipple();