From 7e1fd898083867e1c082aac02210b0b877435ed7 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 13 May 2020 04:09:36 +0200 Subject: [PATCH] fix(text-field): unable to undo/redo in autosized text field on firefox (#19238) --- src/cdk/text-field/_text-field.scss | 23 +++++++++++++++++++---- src/cdk/text-field/autosize.ts | 14 ++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/cdk/text-field/_text-field.scss b/src/cdk/text-field/_text-field.scss index ccafa82fccd7..0649a51f3624 100644 --- a/src/cdk/text-field/_text-field.scss +++ b/src/cdk/text-field/_text-field.scss @@ -27,13 +27,28 @@ // removed when measuring is complete. We use `!important` rules here to make sure user-specified // rules do not interfere with the measurement. textarea.cdk-textarea-autosize-measuring { + @include _cdk-textarea-autosize-measuring-base; height: auto !important; overflow: hidden !important; - // Having 2px top and bottom padding seems to fix a bug where Chrome gets an incorrect - // measurement. We just have to account for it later and subtract it off the final result. - padding: 2px 0 !important; - box-sizing: content-box !important; } + + // Similar to the `cdk-textarea-autosize-measuring` class, but only applied on Firefox. We need + // to use this class, because Firefox has a bug where changing the `overflow` breaks the user's + // ability to undo/redo what they were typing (see #16629). This class is only scoped to Firefox, + // because the measurements there don't seem to be affected by the `height: 0`, whereas on other + // browsers they are, e.g. Chrome detects longer text and IE does't resize back to normal. + // Identical issue report: https://bugzilla.mozilla.org/show_bug.cgi?id=448784 + textarea.cdk-textarea-autosize-measuring-firefox { + @include _cdk-textarea-autosize-measuring-base; + height: 0 !important; + } +} + +@mixin _cdk-textarea-autosize-measuring-base { + // Having 2px top and bottom padding seems to fix a bug where Chrome gets an incorrect + // measurement. We just have to account for it later and subtract it off the final result. + padding: 2px 0 !important; + box-sizing: content-box !important; } // Used to generate UIDs for keyframes used to change the text field autofill styles. diff --git a/src/cdk/text-field/autosize.ts b/src/cdk/text-field/autosize.ts index 98486a71f4cd..74752924f0b6 100644 --- a/src/cdk/text-field/autosize.ts +++ b/src/cdk/text-field/autosize.ts @@ -94,6 +94,9 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy { /** Used to reference correct document/window */ protected _document?: Document; + /** Class that should be applied to the textarea while it's being measured. */ + private _measuringClass: string; + constructor(private _elementRef: ElementRef, private _platform: Platform, private _ngZone: NgZone, @@ -102,6 +105,9 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy { this._document = document; this._textareaElement = this._elementRef.nativeElement as HTMLTextAreaElement; + this._measuringClass = _platform.FIREFOX ? + 'cdk-textarea-autosize-measuring-firefox' : + 'cdk-textarea-autosize-measuring'; } /** Sets the minimum height of the textarea as determined by minRows. */ @@ -229,16 +235,16 @@ export class CdkTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy { // 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.classList.add('cdk-textarea-autosize-measuring'); + textarea.classList.add(this._measuringClass); textarea.placeholder = ''; - // The cdk-textarea-autosize-measuring class includes a 2px padding to workaround an issue with - // Chrome, so we account for that extra space here by subtracting 4 (2px top + 2px bottom). + // The measuring class includes a 2px padding to workaround an issue with Chrome, + // so we account for that extra space here by subtracting 4 (2px top + 2px bottom). const height = textarea.scrollHeight - 4; // Use the scrollHeight to know how large the textarea *would* be if fit its entire value. textarea.style.height = `${height}px`; - textarea.classList.remove('cdk-textarea-autosize-measuring'); + textarea.classList.remove(this._measuringClass); textarea.placeholder = placeholderText; this._ngZone.runOutsideAngular(() => {