fix(navbar): update ink bar when links change (#4897)

* fix(navbar): update ink bar when links change

* remove add link

* fix test

* change strategy - use mutation observer

* cleanup
This commit is contained in:
Andrew Seguin
2017-06-05 14:45:19 -07:00
committed by GitHub
parent 2e3910c86c
commit 41c43cc3ec
6 changed files with 77 additions and 32 deletions
+4
View File
@@ -1,5 +1,9 @@
<h1>Tab Nav Bar</h1>
<button md-button (click)="tabLinks.shift()">Remove tab</button>
<button md-button (click)="swapTabLinks()">Swap first two</button>
<button md-button (click)="addToLabel()">Add to labels</button>
<div class="demo-nav-bar">
<nav md-tab-nav-bar aria-label="weather navigation links">
<a md-tab-link
+10
View File
@@ -83,6 +83,16 @@ export class TabsDemo {
deleteTab(tab: any) {
this.dynamicTabs.splice(this.dynamicTabs.indexOf(tab), 1);
}
swapTabLinks() {
const temp = this.tabLinks[0];
this.tabLinks[0] = this.tabLinks[1];
this.tabLinks[1] = temp;
}
addToLabel() {
this.tabLinks.forEach(link => link.label += 'extracontent');
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="mat-tab-links">
<div class="mat-tab-links" (cdkObserveContent)="_alignInkBar()">
<ng-content></ng-content>
<md-ink-bar></md-ink-bar>
</div>
+40 -10
View File
@@ -1,12 +1,12 @@
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {MdTabsModule} from '../index';
import {MdTabNavBar} from './tab-nav-bar';
import {Component, ViewChild} from '@angular/core';
import {By} from '@angular/platform-browser';
import {ViewportRuler} from '../../core/overlay/position/viewport-ruler';
import {FakeViewportRuler} from '../../core/overlay/position/fake-viewport-ruler';
import {dispatchMouseEvent, dispatchFakeEvent} from '../../core/testing/dispatch-events';
import {LayoutDirection, Dir} from '../../core/rtl/dir';
import {dispatchFakeEvent, dispatchMouseEvent} from '../../core/testing/dispatch-events';
import {Dir, LayoutDirection} from '../../core/rtl/dir';
import {Subject} from 'rxjs/Subject';
@@ -37,20 +37,19 @@ describe('MdTabNavBar', () => {
beforeEach(() => {
fixture = TestBed.createComponent(SimpleTabNavBarTestApp);
fixture.detectChanges();
});
it('should change active index on click', () => {
let component = fixture.debugElement.componentInstance;
// select the second link
let tabLink = fixture.debugElement.queryAll(By.css('a'))[1];
tabLink.nativeElement.click();
expect(component.activeIndex).toBe(1);
expect(fixture.componentInstance.activeIndex).toBe(1);
// select the third link
tabLink = fixture.debugElement.queryAll(By.css('a'))[2];
tabLink.nativeElement.click();
expect(component.activeIndex).toBe(2);
expect(fixture.componentInstance.activeIndex).toBe(2);
});
it('should re-align the ink bar when the direction changes', () => {
@@ -64,6 +63,31 @@ describe('MdTabNavBar', () => {
expect(inkBar.alignToElement).toHaveBeenCalled();
});
it('should re-align the ink bar when the tabs list change', () => {
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
spyOn(inkBar, 'alignToElement');
fixture.componentInstance.tabs = [1, 2, 3, 4];
fixture.detectChanges();
expect(inkBar.alignToElement).toHaveBeenCalled();
});
it('should re-align the ink bar when the tab labels change the width', done => {
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
const spy = spyOn(inkBar, 'alignToElement').and.callFake(() => {
expect(spy.calls.any()).toBe(true);
done();
});
fixture.componentInstance.label = 'label change';
fixture.detectChanges();
expect(spy.calls.any()).toBe(false);
});
it('should re-align the ink bar when the window is resized', fakeAsync(() => {
const inkBar = fixture.componentInstance.tabNavBar._inkBar;
@@ -97,15 +121,21 @@ describe('MdTabNavBar', () => {
selector: 'test-app',
template: `
<nav md-tab-nav-bar>
<a md-tab-link [active]="activeIndex === 0" (click)="activeIndex = 0">Tab One</a>
<a md-tab-link [active]="activeIndex === 1" (click)="activeIndex = 1">Tab Two</a>
<a md-tab-link [active]="activeIndex === 2" (click)="activeIndex = 2">Tab Three</a>
<a md-tab-link
*ngFor="let tab of tabs; let index = index"
[active]="activeIndex === index"
(click)="activeIndex = index">
Tab link {{label}}
</a>
</nav>
`
})
class SimpleTabNavBarTestApp {
@ViewChild(MdTabNavBar) tabNavBar: MdTabNavBar;
label = '';
tabs = [0, 1, 2];
activeIndex = 0;
}
+21 -21
View File
@@ -1,25 +1,26 @@
import {
Component,
Input,
ViewChild,
ElementRef,
ViewEncapsulation,
Directive,
NgZone,
Inject,
Optional,
OnDestroy,
AfterContentInit,
Component,
Directive,
ElementRef,
Inject,
Input,
NgZone,
OnDestroy,
Optional,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import {MdInkBar} from '../ink-bar';
import {MdRipple} from '../../core/ripple/index';
import {ViewportRuler} from '../../core/overlay/position/viewport-ruler';
import {MD_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions, Dir, Platform} from '../../core';
import {Dir, MD_RIPPLE_GLOBAL_OPTIONS, Platform, RippleGlobalOptions} from '../../core';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/operator/auditTime';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/merge';
import {Subject} from 'rxjs/Subject';
/**
* Navigation component matching the styles of the tab group header.
@@ -34,8 +35,8 @@ import 'rxjs/add/observable/merge';
encapsulation: ViewEncapsulation.None,
})
export class MdTabNavBar implements AfterContentInit, OnDestroy {
/** Combines listeners that will re-align the ink bar whenever they're invoked. */
private _realignInkBar: Subscription = null;
/** Subject that emits when the component has been destroyed. */
private _onDestroy = new Subject<void>();
_activeLinkChanged: boolean;
_activeLinkElement: ElementRef;
@@ -51,13 +52,15 @@ export class MdTabNavBar implements AfterContentInit, OnDestroy {
}
ngAfterContentInit(): void {
this._realignInkBar = this._ngZone.runOutsideAngular(() => {
this._ngZone.runOutsideAngular(() => {
let dirChange = this._dir ? this._dir.dirChange : Observable.of(null);
let resize = typeof window !== 'undefined' ?
Observable.fromEvent(window, 'resize').auditTime(10) :
Observable.of(null);
return Observable.merge(dirChange, resize).subscribe(() => this._alignInkBar());
return Observable.merge(dirChange, resize)
.takeUntil(this._onDestroy)
.subscribe(() => this._alignInkBar());
});
}
@@ -70,14 +73,11 @@ export class MdTabNavBar implements AfterContentInit, OnDestroy {
}
ngOnDestroy() {
if (this._realignInkBar) {
this._realignInkBar.unsubscribe();
this._realignInkBar = null;
}
this._onDestroy.next();
}
/** Aligns the ink bar to the active link. */
private _alignInkBar(): void {
_alignInkBar(): void {
if (this._activeLinkElement) {
this._inkBar.alignToElement(this._activeLinkElement.nativeElement);
}
+1
View File
@@ -42,6 +42,7 @@ const ROLLUP_GLOBALS = {
'rxjs/add/operator/share': 'Rx.Observable.prototype',
'rxjs/add/operator/startWith': 'Rx.Observable.prototype',
'rxjs/add/operator/switchMap': 'Rx.Observable.prototype',
'rxjs/add/operator/takeUntil': 'Rx.Observable.prototype',
'rxjs/add/operator/toPromise': 'Rx.Observable.prototype',
'rxjs/BehaviorSubject': 'Rx',
'rxjs/Observable': 'Rx',