From f03c0ace63b107cab18b6c2f23efad7bd2a2307d Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 20 Feb 2021 09:22:23 +0100 Subject: [PATCH] fix(material-experimental/mdc-input): only apply styling when inside a form field Currently `mat-form-field` brings in the styles for `MatInput`. but the problem is that they target a class that is applied even to inputs that aren't inside a form field. These changes aim to prevent CSS from bleeding out by only styling inputs inside a `mat-form-field`. Fixes #21871. --- .../mdc-chips/chip-input.spec.ts | 1 + .../mdc-chips/chip-input.ts | 13 ++++++++--- .../_form-field-native-select.scss | 6 ++--- .../mdc-form-field/_form-field-theme.scss | 2 +- .../_mdc-text-field-structure-overrides.scss | 6 ++--- .../mdc-input/input.spec.ts | 23 +++++++++++++++++++ src/material-experimental/mdc-input/input.ts | 10 +++++--- src/material/input/input.ts | 4 ++++ tools/public_api_guard/material/input.d.ts | 1 + 9 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/material-experimental/mdc-chips/chip-input.spec.ts b/src/material-experimental/mdc-chips/chip-input.spec.ts index 508cd66f2529..a05c94531b65 100644 --- a/src/material-experimental/mdc-chips/chip-input.spec.ts +++ b/src/material-experimental/mdc-chips/chip-input.spec.ts @@ -130,6 +130,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-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 652f926f0714..a05a970fc5eb 100644 --- a/src/material-experimental/mdc-chips/chip-input.ts +++ b/src/material-experimental/mdc-chips/chip-input.ts @@ -17,8 +17,10 @@ import { Input, OnChanges, OnDestroy, + Optional, Output } from '@angular/core'; +import {MatFormField, MAT_FORM_FIELD} from '@angular/material-experimental/mdc-form-field'; import {MatChipsDefaultOptions, MAT_CHIPS_DEFAULT_OPTIONS} from './chip-default-options'; import {MatChipGrid} from './chip-grid'; import {MatChipTextControl} from './chip-text-control'; @@ -127,9 +129,14 @@ export class MatChipInput implements MatChipTextControl, AfterContentInit, OnCha constructor( protected _elementRef: ElementRef, - @Inject(MAT_CHIPS_DEFAULT_OPTIONS) private _defaultOptions: MatChipsDefaultOptions) { - this.inputElement = this._elementRef.nativeElement as HTMLInputElement; - } + @Inject(MAT_CHIPS_DEFAULT_OPTIONS) private _defaultOptions: MatChipsDefaultOptions, + @Optional() @Inject(MAT_FORM_FIELD) formField?: MatFormField) { + this.inputElement = this._elementRef.nativeElement as HTMLInputElement; + + if (formField) { + this.inputElement.classList.add('mat-mdc-form-field-control'); + } + } ngOnChanges() { this._chipGrid.stateChanges.next(); 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 0a1b04feaecf..ae4ee91aa419 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 @@ -17,7 +17,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-input-element { + select.mat-mdc-form-field-control { -moz-appearance: none; -webkit-appearance: none; background-color: transparent; @@ -82,7 +82,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-input-element { + .mat-mdc-form-field-control { padding-right: $mat-form-field-select-horizontal-end-padding; [dir='rtl'] & { padding-right: 0; @@ -104,7 +104,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-input-element { + select.mat-mdc-form-field-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-theme.scss b/src/material-experimental/mdc-form-field/_form-field-theme.scss index 670ce9407286..c18bf366a7ac 100644 --- a/src/material-experimental/mdc-form-field/_form-field-theme.scss +++ b/src/material-experimental/mdc-form-field/_form-field-theme.scss @@ -74,7 +74,7 @@ // MDC uses the `subtitle1` level for the input label and value, but the spec shows `body1` as // the correct level. - .mat-mdc-input-element, + .mat-mdc-form-field-control, .mat-mdc-form-field label, .mat-mdc-form-field-text-prefix, .mat-mdc-form-field-text-suffix { 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 793cf6cbeac1..9f7c427563c8 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 @@ -8,7 +8,7 @@ // 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-input-element { + .mat-mdc-form-field-control { border: none; } @@ -25,8 +25,8 @@ // 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-input-element.mdc-text-field__input, - .mat-mdc-text-field-wrapper .mat-mdc-input-element { + .mat-mdc-form-field-control.mdc-text-field__input, + .mat-mdc-text-field-wrapper .mat-mdc-form-field-control { height: auto; } diff --git a/src/material-experimental/mdc-input/input.spec.ts b/src/material-experimental/mdc-input/input.spec.ts index 6a81d8f5ba9b..4d7a47f0a18b 100644 --- a/src/material-experimental/mdc-input/input.spec.ts +++ b/src/material-experimental/mdc-input/input.spec.ts @@ -896,6 +896,17 @@ describe('MatMdcInput without forms', () => { expect(formField.classList).toContain('mat-warn'); })); + it('should set a class on the input depending on whether it is in a form field', fakeAsync(() => { + const fixture = createComponent(MatInputInsideOutsideFormField); + fixture.detectChanges(); + + 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'); + })); + }); describe('MatMdcInput with forms', () => { @@ -1800,3 +1811,15 @@ class CustomMatInputAccessor { class MatInputWithColor { color: ThemePalette; } + + +@Component({ + template: ` + + + + + + ` +}) +class MatInputInsideOutsideFormField {} diff --git a/src/material-experimental/mdc-input/input.ts b/src/material-experimental/mdc-input/input.ts index 534491dbb4e6..c16577810517 100644 --- a/src/material-experimental/mdc-input/input.ts +++ b/src/material-experimental/mdc-input/input.ts @@ -19,13 +19,17 @@ import {MatInput as BaseMatInput} from '@angular/material/input'; input[matNativeControl], textarea[matNativeControl]`, exportAs: 'matInput', host: { - 'class': 'mat-mdc-input-element mdc-text-field__input', - // The BaseMatInput parent class adds `mat-input-element` and `mat-form-field-autofill-control` - // to the CSS classlist, but this should not be added for this MDC equivalent input. + 'class': 'mat-mdc-input-element', + // The BaseMatInput parent class adds `mat-input-element`, `mat-form-field-control` and + // `mat-form-field-autofill-control` to the CSS class list, but this should not be added for + // this MDC equivalent input. '[class.mat-form-field-autofill-control]': 'false', '[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.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. '[id]': 'id', diff --git a/src/material/input/input.ts b/src/material/input/input.ts index 226294044ac3..b3620b6c280d 100644 --- a/src/material/input/input.ts +++ b/src/material/input/input.ts @@ -107,6 +107,9 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl< /** Whether the component is a textarea. */ readonly _isTextarea: boolean; + /** Whether the input is inside of a form field. */ + readonly _isInFormField: boolean; + /** * Implemented as part of MatFormFieldControl. * @docs-private @@ -282,6 +285,7 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl< this._isServer = !this._platform.isBrowser; this._isNativeSelect = nodeName === 'select'; this._isTextarea = nodeName === 'textarea'; + this._isInFormField = !!_formField; if (this._isNativeSelect) { this.controlType = (element as HTMLSelectElement).multiple ? 'mat-native-select-multiple' : diff --git a/tools/public_api_guard/material/input.d.ts b/tools/public_api_guard/material/input.d.ts index fee31c374f3a..a5663e9baf4e 100644 --- a/tools/public_api_guard/material/input.d.ts +++ b/tools/public_api_guard/material/input.d.ts @@ -8,6 +8,7 @@ export declare class MatInput extends _MatInputMixinBase implements MatFormField protected _disabled: boolean; protected _elementRef: ElementRef; protected _id: string; + readonly _isInFormField: boolean; readonly _isNativeSelect: boolean; readonly _isServer: boolean; readonly _isTextarea: boolean;