Skip to content

Commit

Permalink
fix(autosize): incorrect height with long placeholders
Browse files Browse the repository at this point in the history
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
  • Loading branch information
devversion authored and jelbourn committed Nov 18, 2017
1 parent 8b3c8a5 commit 781c5c0
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 1 deletion.
47 changes: 46 additions & 1 deletion src/lib/input/autosize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ describe('MatTextareaAutosize', () => {
AutosizeTextareaInATab,
AutosizeTextAreaWithContent,
AutosizeTextAreaWithValue,
AutosizeTextareaWithNgModel
AutosizeTextareaWithNgModel,
AutosizeTextareaWithLongPlaceholder
],
});

Expand Down Expand Up @@ -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>(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');
Expand Down Expand Up @@ -269,6 +303,17 @@ class AutosizeTextareaWithNgModel {
model = '';
}

@Component({
template: `
<mat-form-field style="width: 100px">
<textarea matInput matTextareaAutosize [placeholder]="placeholder"></textarea>
</mat-form-field>`,
styles: [textareaStyleReset],
})
class AutosizeTextareaWithLongPlaceholder {
placeholder = 'Long Long Long Long Long Long Long Long Placeholder';
}

@Component({
template: `
<mat-tab-group>
Expand Down
7 changes: 7 additions & 0 deletions src/lib/input/autosize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 781c5c0

Please sign in to comment.