From 13b24ab50bfbae51d33558e39c562f7875985630 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 14 Sep 2021 20:02:28 +0200 Subject: [PATCH] fix(material-experimental/mdc-form-field): fix outline notch width (#23005) * fix(material-experimental/mdc-form-field): fix outline notch width This commit fixes a couple of issues that have been introduced by accident over time: 1. The outline notch width is incorrectly calculated since https://github.com/angular/components/pull/21676. This happens because we manually override the label's typography level to `body1` but didn't account for the `font-size` set by the `notched-outline`. This resulted in the computed label width being incorrect. 2. https://github.com/angular/components/pull/22089 added `line-height: 0` to fix textarea's accidentally expanding the infix vertically. This breaks custom controls and their alignment. Textarea's behave a little different compared to a native input, and the actual fix seems to be to fix the alignment from `top` to `middle` so that the infix does not try to add additional padding (due to the inherited `body1` line-height and to satisfy `min-height`). Example: https://jsfiddle.net/rpvm4bkL/6/. 3. Typography has been moved with https://github.com/angular/components/pull/21676 to the input control (notice how `font: inherit` is removed too). This breaks custom form controls that rely on the typography provided by the form-field (these controls would just inherit typography) 4. Not an actual fix, but a cleanup since 21ab17fb1eea3a7e91463bcacc282ef455f7845b added a class to avoid styles from the form-field leaking into standalone inputs. The class has been updated to be more specific that it only matches form-control inputs, and not _any_ type of form control. * fix(material-experimental/mdc-form-field): increase specificity of `line-height: normal` on the floating label Co-authored-by: Miles Malerba (cherry picked from commit 04d2aaaf6d1778f5c432977a14918c6788640948) --- src/dev-app/mdc-input/mdc-input-demo.html | 19 +++++++++++ .../mdc-chips/chip-input.spec.ts | 2 +- .../mdc-chips/chip-input.ts | 2 +- .../_form-field-native-select.scss | 6 ++-- .../mdc-form-field/_form-field-sizing.scss | 4 --- .../mdc-form-field/_form-field-subscript.scss | 16 +++------- .../mdc-form-field/_form-field-theme.scss | 30 ++++++++++++++---- .../_mdc-text-field-structure-overrides.scss | 25 +++++++++------ .../_mdc-text-field-textarea-overrides.scss | 10 +++++- .../mdc-form-field/form-field.scss | 18 +++++++---- .../mdc-form-field/form-field.ts | 18 +++++++---- .../mdc-input/input.spec.ts | 4 +-- src/material-experimental/mdc-input/input.ts | 4 +-- src/material/datepicker/date-range-input.ts | 2 +- src/material/form-field/.form-field.md.swp | Bin 16384 -> 0 bytes 15 files changed, 104 insertions(+), 56 deletions(-) delete mode 100644 src/material/form-field/.form-field.md.swp diff --git a/src/dev-app/mdc-input/mdc-input-demo.html b/src/dev-app/mdc-input/mdc-input-demo.html index a091105202f6..2e6a578f0119 100644 --- a/src/dev-app/mdc-input/mdc-input-demo.html +++ b/src/dev-app/mdc-input/mdc-input-demo.html @@ -731,6 +731,25 @@

<textarea> with bindable autosize

Label + +

+ Textarea alignment +

+ + + Textarea + + + + + Input + + + + + Input + + diff --git a/src/material-experimental/mdc-chips/chip-input.spec.ts b/src/material-experimental/mdc-chips/chip-input.spec.ts index 8f38955ba7d9..88ae71264b2d 100644 --- a/src/material-experimental/mdc-chips/chip-input.spec.ts +++ b/src/material-experimental/mdc-chips/chip-input.spec.ts @@ -141,7 +141,7 @@ describe('MDC-based MatChipInput', () => { it('should set input styling classes', () => { expect(inputNativeElement.classList).toContain('mat-mdc-input-element'); - expect(inputNativeElement.classList).toContain('mat-mdc-form-field-control'); + expect(inputNativeElement.classList).toContain('mat-mdc-form-field-input-control'); expect(inputNativeElement.classList).toContain('mat-mdc-chip-input'); expect(inputNativeElement.classList).toContain('mdc-text-field__input'); }); diff --git a/src/material-experimental/mdc-chips/chip-input.ts b/src/material-experimental/mdc-chips/chip-input.ts index e77a2aa98d63..0e6b52c98a8c 100644 --- a/src/material-experimental/mdc-chips/chip-input.ts +++ b/src/material-experimental/mdc-chips/chip-input.ts @@ -135,7 +135,7 @@ export class MatChipInput implements MatChipTextControl, AfterContentInit, OnCha this.inputElement = this._elementRef.nativeElement as HTMLInputElement; if (formField) { - this.inputElement.classList.add('mat-mdc-form-field-control'); + this.inputElement.classList.add('mat-mdc-form-field-input-control'); } } diff --git a/src/material-experimental/mdc-form-field/_form-field-native-select.scss b/src/material-experimental/mdc-form-field/_form-field-native-select.scss index 0cda173843c2..5978cb082398 100644 --- a/src/material-experimental/mdc-form-field/_form-field-native-select.scss +++ b/src/material-experimental/mdc-form-field/_form-field-native-select.scss @@ -18,7 +18,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt // Remove the native select down arrow and ensure that the native appearance // does not conflict with the form-field. e.g. Focus indication of the native // select is undesired since we handle focus as part of the form-field. - select.mat-mdc-form-field-control { + select.mat-mdc-form-field-input-control { -moz-appearance: none; -webkit-appearance: none; background-color: transparent; @@ -83,7 +83,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt // Add padding on the end of the native select so that the content does not // overlap with the Material Design arrow. - .mat-mdc-form-field-control { + .mat-mdc-form-field-input-control { padding-right: $mat-form-field-select-horizontal-end-padding; [dir='rtl'] & { padding-right: 0; @@ -105,7 +105,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt $dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.54); $disabled-dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.38); - select.mat-mdc-form-field-control { + select.mat-mdc-form-field-input-control { // On dark themes we set the native `select` color to some shade of white, // however the color propagates to all of the `option` elements, which are // always on a white background inside the dropdown, causing them to blend in. diff --git a/src/material-experimental/mdc-form-field/_form-field-sizing.scss b/src/material-experimental/mdc-form-field/_form-field-sizing.scss index 532fd60171cd..3b3fbcd1174b 100644 --- a/src/material-experimental/mdc-form-field/_form-field-sizing.scss +++ b/src/material-experimental/mdc-form-field/_form-field-sizing.scss @@ -38,7 +38,3 @@ $mat-form-field-no-label-padding-top: 16px; // The amount of padding between the icon prefix/suffix and the infix. // This assumes that the icon will be a 24px square with 12px padding. $mat-form-field-icon-prefix-infix-padding: 4px; - -// The amount of padding between the end of the form-field and the infix for a form-field with no -// icons. -$mat-form-field-end-padding: 16px; diff --git a/src/material-experimental/mdc-form-field/_form-field-subscript.scss b/src/material-experimental/mdc-form-field/_form-field-subscript.scss index 3a84607b8dff..4eaa74f2cb2d 100644 --- a/src/material-experimental/mdc-form-field/_form-field-subscript.scss +++ b/src/material-experimental/mdc-form-field/_form-field-subscript.scss @@ -1,6 +1,7 @@ @use '@material/textfield' as mdc-textfield; @use '@material/theme/theme' as mdc-theme; @use '@material/typography' as mdc-typography; +@use '@material/textfield/variables' as mdc-textfield-variables; @use 'form-field-sizing'; @use '../mdc-helpers/mdc-helpers'; @use '../../material/core/theming/theming'; @@ -13,16 +14,13 @@ position: relative; } - // The horizontal padding between the edge of the text box and the start of the subscript text. - $subscript-horizontal-padding: 16px; - .mat-mdc-form-field-hint-wrapper, .mat-mdc-form-field-error-wrapper { position: absolute; top: 0; left: 0; right: 0; - padding: 0 $subscript-horizontal-padding; + padding: 0 mdc-textfield-variables.$padding-horizontal; } .mat-mdc-form-field-bottom-align::before { @@ -31,14 +29,8 @@ height: 16px; } - // Scale down icons in the subscript to be the same size as the text. - .mat-mdc-form-field-subscript-wrapper, - .mat-mdc-form-field label { - .mat-icon { - width: 1em; - height: 1em; - font-size: inherit; - } + .mat-mdc-form-field-hint-end { + order: 1; } // Clears the floats on the hints. This is necessary for the hint animation to work. diff --git a/src/material-experimental/mdc-form-field/_form-field-theme.scss b/src/material-experimental/mdc-form-field/_form-field-theme.scss index 882b95bdf843..3cd6d01776a5 100644 --- a/src/material-experimental/mdc-form-field/_form-field-theme.scss +++ b/src/material-experimental/mdc-form-field/_form-field-theme.scss @@ -3,7 +3,7 @@ @use '@material/notched-outline' as mdc-notched-outline; @use '@material/line-ripple' as mdc-line-ripple; @use '@material/theme/theme-color' as mdc-theme-color; -@use '@material/typography' as mdc-typography; +@use '@material/typography/typography' as mdc-typography; @use '../mdc-helpers/mdc-helpers'; @use '../../material/core/typography/typography'; @use 'form-field-density'; @@ -72,14 +72,30 @@ @include mdc-line-ripple.core-styles($query: mdc-helpers.$mat-typography-styles-query); @include form-field-subscript.private-form-field-subscript-typography($config); - // MDC uses the `subtitle1` level for the input label and value, but the spec shows `body1` as - // the correct level. - .mat-mdc-form-field-control, - .mat-mdc-form-field label, - .mat-mdc-form-field-text-prefix, - .mat-mdc-form-field-text-suffix { + // MDC uses `subtitle1` for the input value, placeholder and floating label. The spec + // shows `body1` for text fields though, so we manually override the typography. + // Note: Form controls inherit the typography from the parent form field. + .mat-mdc-form-field, + .mat-mdc-form-field .mdc-floating-label { @include mdc-typography.typography(body1, $query: mdc-helpers.$mat-typography-styles-query); } + + // Above, we updated the floating label to use the `body1` typography level. The MDC notched + // outline overrides this accidentally (only when the label floats) to a `rem`-based value. + // This results in different label widths when floated/docked and ultimately breaks the notch + // width as it has been measured in the docked state (where `body1` is applied). We try to + // unset these styles set by the `mdc-notched-outline`: + // https://github.com/material-components/material-components-web/blob/master/packages/mdc-notched-outline/_mixins.scss#L272-L292. + .mat-mdc-form-field .mdc-text-field--outlined { + // For the non-upgraded notch label (i.e. when rendered on the server), also + // use the correct `body1` typography level. + .mdc-floating-label--float-above { + font-size: mdc-typography.get-size(body1) * 0.75; + } + .mdc-notched-outline--upgraded .mdc-floating-label--float-above { + font-size: mdc-typography.get-size(body1); + } + } } } diff --git a/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss b/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss index 226509f38fe0..873ff08b4a0d 100644 --- a/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss +++ b/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss @@ -1,4 +1,4 @@ -@use 'form-field-sizing'; +@use '@material/textfield/variables' as mdc-textfield-variables; // Mixin that can be included to override the default MDC text-field // styles to fit our needs. See individual comments for context on why @@ -6,15 +6,21 @@ @mixin private-text-field-structure-overrides() { // Unset the border set by MDC. We move the border (which serves as the Material Design // text-field bottom line) into its own element. This is necessary because we want the - // bottom-line to span across the whole form-field (including prefixes and suffixes). - .mat-mdc-form-field-control { + // bottom-line to span across the whole form-field (including prefixes and suffixes). Also + // we ensure that font styles are inherited for input elements. We do this because inputs by + // default have explicit font styles from the user agent, and we set the desired font styles + // in the parent `mat-form-field` element (for better custom form-field control support). + // Note: We increase specificity here because the MDC textfield seems to override this, + // depending on the CSS order, with an affix selector joint with the input. + .mat-mdc-form-field-input-control.mat-mdc-form-field-input-control { + font: inherit; border: none; } // In order to ensure proper alignment of the floating label, we reset its line-height. // The line-height is not important as the element is absolutely positioned and only has one line // of text. - .mat-mdc-form-field .mdc-floating-label { + .mat-mdc-form-field .mdc-floating-label.mdc-floating-label { line-height: normal; } @@ -24,15 +30,16 @@ // not work for us since we support arbitrary form field controls which don't necessarily // use an `input` element. We organize the vertical spacing on the infix container. .mdc-text-field--no-label:not(.mdc-text-field--textarea) - .mat-mdc-form-field-control.mdc-text-field__input, - .mat-mdc-text-field-wrapper .mat-mdc-form-field-control { + .mat-mdc-form-field-input-control.mdc-text-field__input, + .mat-mdc-text-field-wrapper .mat-mdc-form-field-input-control { height: auto; } // Color inputs are a special case, because setting their height to // `auto` will collapse them. The height value is an arbitrary number // which was extracted from the user agent styles of Chrome and Firefox. - .mat-mdc-text-field-wrapper .mat-mdc-form-field-control.mdc-text-field__input[type='color'] { + .mat-mdc-text-field-wrapper + .mat-mdc-form-field-input-control.mdc-text-field__input[type='color'] { height: 23px; } @@ -57,8 +64,8 @@ [dir='rtl'] { // Undo the above padding removals which only apply in LTR languages. .mat-mdc-text-field-wrapper { - padding-left: form-field-sizing.$mat-form-field-end-padding; - padding-right: form-field-sizing.$mat-form-field-end-padding; + padding-left: mdc-textfield-variables.$padding-horizontal; + padding-right: mdc-textfield-variables.$padding-horizontal; } // ...and apply the correct padding resets for RTL languages. .mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper { diff --git a/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss b/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss index 008321138c11..cee19bbf8536 100644 --- a/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss +++ b/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss @@ -10,7 +10,15 @@ @mixin private-text-field-textarea-overrides() { // Ensures that textarea elements inside of the form-field have proper vertical spacing // to account for the floating label. Also ensures that there is no vertical text overflow. - .mat-mdc-textarea-input { + // **Note**: Before changing this selector, make sure the `cdk-textarea-autosize` class is + // still able to override the `resize` property to `none`. + .mat-mdc-form-field-textarea-control { + // Set the vertical alignment for textareas inside form fields to be the middle. This + // ensures that textareas do not stretch the infix container vertically without having + // multiple rows of text. See: https://github.com/angular/components/pull/22089. + vertical-align: middle; + // Textareas by default also allow users to resize the textarea horizontally. This + // causes the textarea to overflow the form-field. We only allow vertical resizing. resize: vertical; box-sizing: border-box; height: auto; diff --git a/src/material-experimental/mdc-form-field/form-field.scss b/src/material-experimental/mdc-form-field/form-field.scss index 7b365b37e900..e1b573d8aea3 100644 --- a/src/material-experimental/mdc-form-field/form-field.scss +++ b/src/material-experimental/mdc-form-field/form-field.scss @@ -90,13 +90,21 @@ } } +// Scale down icons in the subscript and floating label to be the same size +// as the text. +.mat-mdc-form-field-subscript-wrapper, +.mat-mdc-form-field label { + .mat-icon { + width: 1em; + height: 1em; + font-size: inherit; + } +} + // Infix that contains the projected content (usually an input or a textarea). We ensure // that the projected form-field control and content can stretch as needed, but we also // apply a default infix width to make the form-field's look natural. .mat-mdc-form-field-infix { - // Prevent extra height from being added around the textarea, which throws off the overall - // height of the form-field - line-height: 0; flex: auto; min-width: 0; width: form-field-sizing.$mat-form-field-default-infix-width; @@ -105,10 +113,6 @@ box-sizing: border-box; } -.mat-mdc-form-field-hint-end { - order: 1; -} - // In order to make it possible for developers to disable animations for form-fields, // we only activate the animation styles if animations are not explicitly disabled. .mat-mdc-form-field:not(.mat-form-field-no-animations) { diff --git a/src/material-experimental/mdc-form-field/form-field.ts b/src/material-experimental/mdc-form-field/form-field.ts index 571152ccc965..a73f11e0d837 100644 --- a/src/material-experimental/mdc-form-field/form-field.ts +++ b/src/material-experimental/mdc-form-field/form-field.ts @@ -93,6 +93,12 @@ const DEFAULT_FLOAT_LABEL: FloatLabelType = 'auto'; */ const FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM = `translateY(-50%)`; +/** + * Horizontal padding in pixels used by the MDC for the wrapper containing infix. + * This value is extracted from MDC's Sass variables. See `$padding-horizontal`. + */ +const WRAPPER_HORIZONTAL_PADDING = 16; + /** Container for form controls that applies Material Design styling and behavior. */ @Component({ selector: 'mat-form-field', @@ -691,16 +697,16 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck } const iconPrefixContainer = this._iconPrefixContainer?.nativeElement; const textPrefixContainer = this._textPrefixContainer?.nativeElement; + const iconPrefixContainerWidth = iconPrefixContainer?.getBoundingClientRect().width ?? 0; + const textPrefixContainerWidth = textPrefixContainer?.getBoundingClientRect().width ?? 0; // If the directionality is RTL, the x-axis transform needs to be inverted. This // is because `transformX` does not change based on the page directionality. const labelHorizontalOffset = (this._dir.value === 'rtl' ? -1 : 1) * ( - (iconPrefixContainer ? - // If there's an icon prefix, we disable the default 16px padding, - // so make sure to account for that. - (iconPrefixContainer?.getBoundingClientRect().width ?? 0) - 16 : 0 - ) + - (textPrefixContainer?.getBoundingClientRect().width ?? 0) + // If there's an icon prefix, we subtract the default horizontal padding as we + // reset the horizontal padding in CSS too. + (iconPrefixContainer ? iconPrefixContainerWidth - WRAPPER_HORIZONTAL_PADDING : 0) + + textPrefixContainerWidth ); // Update the transform the floating label to account for the prefix container. Note diff --git a/src/material-experimental/mdc-input/input.spec.ts b/src/material-experimental/mdc-input/input.spec.ts index b971b87f59cd..3570665cceb9 100644 --- a/src/material-experimental/mdc-input/input.spec.ts +++ b/src/material-experimental/mdc-input/input.spec.ts @@ -858,8 +858,8 @@ describe('MatMdcInput without forms', () => { const inFormField = fixture.nativeElement.querySelector('.inside'); const outsideFormField = fixture.nativeElement.querySelector('.outside'); - expect(inFormField.classList).toContain('mat-mdc-form-field-control'); - expect(outsideFormField.classList).not.toContain('mat-mdc-form-field-control'); + expect(inFormField.classList).toContain('mat-mdc-form-field-input-control'); + expect(outsideFormField.classList).not.toContain('mat-mdc-form-field-input-control'); })); }); diff --git a/src/material-experimental/mdc-input/input.ts b/src/material-experimental/mdc-input/input.ts index 329d386fc21a..06c0942316d0 100644 --- a/src/material-experimental/mdc-input/input.ts +++ b/src/material-experimental/mdc-input/input.ts @@ -27,8 +27,8 @@ import {MatInput as BaseMatInput} from '@angular/material/input'; '[class.mat-input-element]': 'false', '[class.mat-form-field-control]': 'false', '[class.mat-input-server]': '_isServer', - '[class.mat-mdc-textarea-input]': '_isTextarea', - '[class.mat-mdc-form-field-control]': '_isInFormField', + '[class.mat-mdc-form-field-textarea-control]': '_isInFormField && _isTextarea', + '[class.mat-mdc-form-field-input-control]': '_isInFormField', '[class.mdc-text-field__input]': '_isInFormField', // Native input properties that are overwritten by Angular inputs need to be synced with // the native input element. Otherwise property bindings for those don't work. diff --git a/src/material/datepicker/date-range-input.ts b/src/material/datepicker/date-range-input.ts index 1a8411e5c626..47c498394884 100644 --- a/src/material/datepicker/date-range-input.ts +++ b/src/material/datepicker/date-range-input.ts @@ -249,7 +249,7 @@ export class MatDateRangeInput implements MatFormFieldControl>, if (_formField?._elementRef.nativeElement.classList.contains('mat-mdc-form-field')) { const classList = _elementRef.nativeElement.classList; classList.add('mat-mdc-input-element'); - classList.add('mat-mdc-form-field-control'); + classList.add('mat-mdc-form-field-input-control'); } // TODO(crisbeto): remove `as any` after #18206 lands. diff --git a/src/material/form-field/.form-field.md.swp b/src/material/form-field/.form-field.md.swp deleted file mode 100644 index 9039143c4e5936f079ed01342107936832b7d1a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 E0)yxfUH||9