From 781c5c04eb022d19635682714ff25227093cabc2 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 25 Oct 2017 17:12:03 +0200 Subject: [PATCH] fix(autosize): incorrect height with long placeholders Long placeholders can cause the `scrollHeight` to be bigger than the actual content is. To ensure the `scrollHeight` is correct, the placeholders need to be removed temporarily. Fixes #8013 --- src/lib/input/autosize.spec.ts | 47 +++++++++++++++++++++++++++++++++- src/lib/input/autosize.ts | 7 +++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/lib/input/autosize.spec.ts b/src/lib/input/autosize.spec.ts index b984be03340f..6876f14306f1 100644 --- a/src/lib/input/autosize.spec.ts +++ b/src/lib/input/autosize.spec.ts @@ -28,7 +28,8 @@ describe('MatTextareaAutosize', () => { AutosizeTextareaInATab, AutosizeTextAreaWithContent, AutosizeTextAreaWithValue, - AutosizeTextareaWithNgModel + AutosizeTextareaWithNgModel, + AutosizeTextareaWithLongPlaceholder ], }); @@ -176,6 +177,39 @@ describe('MatTextareaAutosize', () => { .toBe(textarea.scrollHeight, 'Expected textarea height to match its scrollHeight'); }); + it('should not calculate wrong content height due to long placeholders', () => { + const fixtureWithPlaceholder = TestBed.createComponent(AutosizeTextareaWithLongPlaceholder); + fixtureWithPlaceholder.detectChanges(); + + textarea = fixtureWithPlaceholder.nativeElement.querySelector('textarea'); + autosize = fixtureWithPlaceholder.debugElement.query( + By.directive(MatTextareaAutosize)).injector.get(MatTextareaAutosize); + + // To be able to trigger a new calculation of the height with a long placeholder, the textarea + // value needs to be changed. + textarea.value = '1'; + autosize.resizeToFitContent(); + + textarea.value = ''; + autosize.resizeToFitContent(); + + const heightWithLongPlaceholder = textarea.clientHeight; + + fixtureWithPlaceholder.componentInstance.placeholder = 'Short'; + fixtureWithPlaceholder.detectChanges(); + + // 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(); + + textarea.value = ''; + autosize.resizeToFitContent(); + + expect(textarea.clientHeight).toBe(heightWithLongPlaceholder, + 'Expected the textarea height to be the same with a long placeholder.'); + }); + it('should resize when an associated form control value changes', fakeAsync(() => { const fixtureWithForms = TestBed.createComponent(AutosizeTextareaWithNgModel); textarea = fixtureWithForms.nativeElement.querySelector('textarea'); @@ -269,6 +303,17 @@ class AutosizeTextareaWithNgModel { model = ''; } +@Component({ + template: ` + + + `, + styles: [textareaStyleReset], +}) +class AutosizeTextareaWithLongPlaceholder { + placeholder = 'Long Long Long Long Long Long Long Long Placeholder'; +} + @Component({ template: ` diff --git a/src/lib/input/autosize.ts b/src/lib/input/autosize.ts index 02e6d1756f6b..a208157134fd 100644 --- a/src/lib/input/autosize.ts +++ b/src/lib/input/autosize.ts @@ -150,14 +150,21 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck { return; } + const previousPlaceholder = textarea.placeholder; + // Reset the textarea height to auto in order to shrink back to its default size. // Also temporarily force overflow:hidden, so scroll bars do not interfere with calculations. + // Long placeholders that are wider than the textarea width may lead to a bigger scrollHeight + // value. To ensure that the scrollHeight is not bigger than the content, the placeholders + // need to be removed temporarily. textarea.style.height = 'auto'; textarea.style.overflow = 'hidden'; + textarea.placeholder = ''; // Use the scrollHeight to know how large the textarea *would* be if fit its entire value. textarea.style.height = `${textarea.scrollHeight}px`; textarea.style.overflow = ''; + textarea.placeholder = previousPlaceholder; this._previousValue = value; }