diff --git a/src/lib/input/autosize.spec.ts b/src/lib/input/autosize.spec.ts index 3156435410b..0f3517e54b7 100644 --- a/src/lib/input/autosize.spec.ts +++ b/src/lib/input/autosize.spec.ts @@ -1,12 +1,13 @@ import {Component, ViewChild} from '@angular/core'; import {FormsModule} from '@angular/forms'; -import {ComponentFixture, TestBed, async, fakeAsync, flushMicrotasks} from '@angular/core/testing'; +import {ComponentFixture, TestBed, async, fakeAsync, flush, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {MatInputModule} from './index'; import {MatTextareaAutosize} from './autosize'; import {MatStepperModule} from '@angular/material/stepper'; import {MatTabsModule} from '@angular/material/tabs'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {dispatchFakeEvent} from '@angular/cdk/testing'; describe('MatTextareaAutosize', () => { @@ -185,14 +186,14 @@ describe('MatTextareaAutosize', () => { autosize = fixtureWithPlaceholder.debugElement.query( By.directive(MatTextareaAutosize)).injector.get(MatTextareaAutosize); - triggerTextareaResize(); + autosize.resizeToFitContent(true); const heightWithLongPlaceholder = textarea.clientHeight; fixtureWithPlaceholder.componentInstance.placeholder = 'Short'; fixtureWithPlaceholder.detectChanges(); - triggerTextareaResize(); + autosize.resizeToFitContent(true); expect(textarea.clientHeight).toBe(heightWithLongPlaceholder, 'Expected the textarea height to be the same with a long placeholder.'); @@ -213,7 +214,7 @@ describe('MatTextareaAutosize', () => { Some late visitor entreating entrance at my chamber door;— This it is and nothing more.” `; fixtureWithForms.detectChanges(); - flushMicrotasks(); + flush(); fixtureWithForms.detectChanges(); expect(textarea.clientHeight) @@ -229,7 +230,7 @@ describe('MatTextareaAutosize', () => { `; fixture.detectChanges(); - flushMicrotasks(); + flush(); fixture.detectChanges(); expect(textarea.clientHeight) @@ -250,16 +251,14 @@ describe('MatTextareaAutosize', () => { expect(textarea.getBoundingClientRect().height).toBeGreaterThan(1); }); - /** Triggers a textarea resize to fit the content. */ - function triggerTextareaResize() { - // To be able to trigger a new calculation of the height with a short placeholder, the - // textarea value needs to be changed. - textarea.value = '1'; - autosize.resizeToFitContent(); + it('should trigger a resize when the window is resized', fakeAsync(() => { + spyOn(autosize, 'resizeToFitContent'); - textarea.value = ''; - autosize.resizeToFitContent(); - } + dispatchFakeEvent(window, 'resize'); + tick(16); + + expect(autosize.resizeToFitContent).toHaveBeenCalled(); + })); }); // Styles to reset padding and border to make measurement comparisons easier. diff --git a/src/lib/input/autosize.ts b/src/lib/input/autosize.ts index 6c2fd14f695..b16d58693e8 100644 --- a/src/lib/input/autosize.ts +++ b/src/lib/input/autosize.ts @@ -6,8 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, Input, AfterViewInit, DoCheck} from '@angular/core'; +import { + Directive, + ElementRef, + Input, + AfterViewInit, + DoCheck, + OnDestroy, + NgZone, +} from '@angular/core'; import {Platform} from '@angular/cdk/platform'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {auditTime} from 'rxjs/operators/auditTime'; +import {takeUntil} from 'rxjs/operators/takeUntil'; +import {Subject} from 'rxjs/Subject'; /** @@ -23,9 +35,10 @@ import {Platform} from '@angular/cdk/platform'; 'rows': '1', }, }) -export class MatTextareaAutosize implements AfterViewInit, DoCheck { +export class MatTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy { /** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */ private _previousValue: string; + private _destroyed = new Subject(); private _minRows: number; private _maxRows: number; @@ -49,7 +62,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck { /** Cached height of a textarea with a single row. */ private _cachedLineHeight: number; - constructor(private _elementRef: ElementRef, private _platform: Platform) {} + constructor( + private _elementRef: ElementRef, + private _platform: Platform, + private _ngZone?: NgZone) {} + + // TODO(crisbeto): make the `_ngZone` a required param in the next major version. /** Sets the minimum height of the textarea as determined by minRows. */ _setMinHeight(): void { @@ -74,9 +92,22 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck { ngAfterViewInit() { if (this._platform.isBrowser) { this.resizeToFitContent(); + + if (this._ngZone) { + this._ngZone.runOutsideAngular(() => { + fromEvent(window, 'resize') + .pipe(auditTime(16), takeUntil(this._destroyed)) + .subscribe(() => this.resizeToFitContent(true)); + }); + } } } + ngOnDestroy() { + this._destroyed.next(); + this._destroyed.complete(); + } + /** Sets a style property on the textarea element. */ private _setTextareaStyle(property: string, value: string): void { const textarea = this._elementRef.nativeElement as HTMLTextAreaElement; @@ -134,8 +165,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck { } } - /** Resize the textarea to fit its content. */ - resizeToFitContent() { + /** + * Resize the textarea to fit its content. + * @param force Whether to force a height recalculation. By default the height will be + * recalculated only if the value changed since the last call. + */ + resizeToFitContent(force = false) { this._cacheTextareaLineHeight(); // If we haven't determined the line-height yet, we know we're still hidden and there's no point @@ -148,7 +183,7 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck { const value = textarea.value; // Only resize of the value changed since these calculations can be expensive. - if (value === this._previousValue) { + if (value === this._previousValue && !force) { return; }