diff --git a/src/lib/tabs/tab-header.spec.ts b/src/lib/tabs/tab-header.spec.ts index 3ff61a32310d..b5aaa2a08485 100644 --- a/src/lib/tabs/tab-header.spec.ts +++ b/src/lib/tabs/tab-header.spec.ts @@ -19,6 +19,7 @@ import {MatInkBar} from './ink-bar'; import {MatTabHeader} from './tab-header'; import {MatTabLabelWrapper} from './tab-label-wrapper'; import {Subject} from 'rxjs'; +import {ObserversModule, MutationObserverFactory} from '@angular/cdk/observers'; describe('MatTabHeader', () => { @@ -30,7 +31,7 @@ describe('MatTabHeader', () => { beforeEach(async(() => { dir = 'ltr'; TestBed.configureTestingModule({ - imports: [CommonModule, PortalModule, MatRippleModule, ScrollingModule], + imports: [CommonModule, PortalModule, MatRippleModule, ScrollingModule, ObserversModule], declarations: [ MatTabHeader, MatInkBar, @@ -345,6 +346,41 @@ describe('MatTabHeader', () => { discardPeriodicTasks(); })); + it('should update the pagination state if the content of the labels changes', () => { + const mutationCallbacks: Function[] = []; + TestBed.overrideProvider(MutationObserverFactory, { + useValue: { + // Stub out the MutationObserver since the native one is async. + create: function(callback: Function) { + mutationCallbacks.push(callback); + return {observe: () => {}, disconnect: () => {}}; + } + } + }); + + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.detectChanges(); + + const tabHeaderElement: HTMLElement = + fixture.nativeElement.querySelector('.mat-tab-header'); + const labels = + Array.from(fixture.nativeElement.querySelectorAll('.label-content')); + const extraText = new Array(100).fill('w').join(); + const enabledClass = 'mat-tab-header-pagination-controls-enabled'; + + expect(tabHeaderElement.classList).not.toContain(enabledClass); + + labels.forEach(label => { + label.style.width = ''; + label.textContent += extraText; + }); + + mutationCallbacks.forEach(callback => callback()); + fixture.detectChanges(); + + expect(tabHeaderElement.classList).toContain(enabledClass); + }); + }); }); @@ -359,7 +395,7 @@ interface Tab { -
diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index cd930500c6a8..058bc62e5258 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -20,6 +20,7 @@ import { ElementRef, EventEmitter, Input, + NgZone, OnDestroy, Optional, Output, @@ -137,7 +138,9 @@ export class MatTabHeader extends _MatTabHeaderMixinBase constructor(private _elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef, private _viewportRuler: ViewportRuler, - @Optional() private _dir: Directionality) { + @Optional() private _dir: Directionality, + // @breaking-change 8.0.0 `_ngZone` parameter to be made required. + private _ngZone?: NgZone) { super(); } @@ -234,9 +237,16 @@ export class MatTabHeader extends _MatTabHeaderMixinBase * Callback for when the MutationObserver detects that the content has changed. */ _onContentChanges() { - this._updatePagination(); - this._alignInkBarToSelectedTab(); - this._changeDetectorRef.markForCheck(); + const zoneCallback = () => { + this._updatePagination(); + this._alignInkBarToSelectedTab(); + this._changeDetectorRef.markForCheck(); + }; + + // The content observer runs outside the `NgZone` by default, which + // means that we need to bring the callback back in ourselves. + // @breaking-change 8.0.0 Remove null check for `_ngZone` once it's a required parameter. + this._ngZone ? this._ngZone.run(zoneCallback) : zoneCallback(); } /**