Skip to content

Commit 21ab17f

Browse files
authored
fix(material-experimental/mdc-input): only apply styling when inside a form field (#21876)
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.
1 parent 1371a85 commit 21ab17f

File tree

9 files changed

+53
-13
lines changed

9 files changed

+53
-13
lines changed

src/material-experimental/mdc-chips/chip-input.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ describe('MDC-based MatChipInput', () => {
130130

131131
it('should set input styling classes', () => {
132132
expect(inputNativeElement.classList).toContain('mat-mdc-input-element');
133+
expect(inputNativeElement.classList).toContain('mat-mdc-form-field-control');
133134
expect(inputNativeElement.classList).toContain('mat-mdc-chip-input');
134135
expect(inputNativeElement.classList).toContain('mdc-text-field__input');
135136
});

src/material-experimental/mdc-chips/chip-input.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import {
1717
Input,
1818
OnChanges,
1919
OnDestroy,
20+
Optional,
2021
Output
2122
} from '@angular/core';
23+
import {MatFormField, MAT_FORM_FIELD} from '@angular/material-experimental/mdc-form-field';
2224
import {MatChipsDefaultOptions, MAT_CHIPS_DEFAULT_OPTIONS} from './chip-default-options';
2325
import {MatChipGrid} from './chip-grid';
2426
import {MatChipTextControl} from './chip-text-control';
@@ -127,9 +129,14 @@ export class MatChipInput implements MatChipTextControl, AfterContentInit, OnCha
127129

128130
constructor(
129131
protected _elementRef: ElementRef<HTMLInputElement>,
130-
@Inject(MAT_CHIPS_DEFAULT_OPTIONS) private _defaultOptions: MatChipsDefaultOptions) {
131-
this.inputElement = this._elementRef.nativeElement as HTMLInputElement;
132-
}
132+
@Inject(MAT_CHIPS_DEFAULT_OPTIONS) private _defaultOptions: MatChipsDefaultOptions,
133+
@Optional() @Inject(MAT_FORM_FIELD) formField?: MatFormField) {
134+
this.inputElement = this._elementRef.nativeElement as HTMLInputElement;
135+
136+
if (formField) {
137+
this.inputElement.classList.add('mat-mdc-form-field-control');
138+
}
139+
}
133140

134141
ngOnChanges() {
135142
this._chipGrid.stateChanges.next();

src/material-experimental/mdc-form-field/_form-field-native-select.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt
1717
// Remove the native select down arrow and ensure that the native appearance
1818
// does not conflict with the form-field. e.g. Focus indication of the native
1919
// select is undesired since we handle focus as part of the form-field.
20-
select.mat-mdc-input-element {
20+
select.mat-mdc-form-field-control {
2121
-moz-appearance: none;
2222
-webkit-appearance: none;
2323
background-color: transparent;
@@ -82,7 +82,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt
8282

8383
// Add padding on the end of the native select so that the content does not
8484
// overlap with the Material Design arrow.
85-
.mat-mdc-input-element {
85+
.mat-mdc-form-field-control {
8686
padding-right: $mat-form-field-select-horizontal-end-padding;
8787
[dir='rtl'] & {
8888
padding-right: 0;
@@ -104,7 +104,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt
104104
$dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.54);
105105
$disabled-dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.38);
106106

107-
select.mat-mdc-input-element {
107+
select.mat-mdc-form-field-control {
108108
// On dark themes we set the native `select` color to some shade of white,
109109
// however the color propagates to all of the `option` elements, which are
110110
// always on a white background inside the dropdown, causing them to blend in.

src/material-experimental/mdc-form-field/_form-field-theme.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474

7575
// MDC uses the `subtitle1` level for the input label and value, but the spec shows `body1` as
7676
// the correct level.
77-
.mat-mdc-input-element,
77+
.mat-mdc-form-field-control,
7878
.mat-mdc-form-field label,
7979
.mat-mdc-form-field-text-prefix,
8080
.mat-mdc-form-field-text-suffix {

src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// Unset the border set by MDC. We move the border (which serves as the Material Design
99
// text-field bottom line) into its own element. This is necessary because we want the
1010
// bottom-line to span across the whole form-field (including prefixes and suffixes).
11-
.mat-mdc-input-element {
11+
.mat-mdc-form-field-control {
1212
border: none;
1313
}
1414

@@ -25,8 +25,8 @@
2525
// not work for us since we support arbitrary form field controls which don't necessarily
2626
// use an `input` element. We organize the vertical spacing on the infix container.
2727
.mdc-text-field--no-label:not(.mdc-text-field--textarea)
28-
.mat-mdc-input-element.mdc-text-field__input,
29-
.mat-mdc-text-field-wrapper .mat-mdc-input-element {
28+
.mat-mdc-form-field-control.mdc-text-field__input,
29+
.mat-mdc-text-field-wrapper .mat-mdc-form-field-control {
3030
height: auto;
3131
}
3232

src/material-experimental/mdc-input/input.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,17 @@ describe('MatMdcInput without forms', () => {
896896
expect(formField.classList).toContain('mat-warn');
897897
}));
898898

899+
it('should set a class on the input depending on whether it is in a form field', fakeAsync(() => {
900+
const fixture = createComponent(MatInputInsideOutsideFormField);
901+
fixture.detectChanges();
902+
903+
const inFormField = fixture.nativeElement.querySelector('.inside');
904+
const outsideFormField = fixture.nativeElement.querySelector('.outside');
905+
906+
expect(inFormField.classList).toContain('mat-mdc-form-field-control');
907+
expect(outsideFormField.classList).not.toContain('mat-mdc-form-field-control');
908+
}));
909+
899910
});
900911

901912
describe('MatMdcInput with forms', () => {
@@ -1800,3 +1811,15 @@ class CustomMatInputAccessor {
18001811
class MatInputWithColor {
18011812
color: ThemePalette;
18021813
}
1814+
1815+
1816+
@Component({
1817+
template: `
1818+
<mat-form-field>
1819+
<input class="inside" matNativeControl>
1820+
</mat-form-field>
1821+
1822+
<input class="outside" matNativeControl>
1823+
`
1824+
})
1825+
class MatInputInsideOutsideFormField {}

src/material-experimental/mdc-input/input.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ import {MatInput as BaseMatInput} from '@angular/material/input';
1919
input[matNativeControl], textarea[matNativeControl]`,
2020
exportAs: 'matInput',
2121
host: {
22-
'class': 'mat-mdc-input-element mdc-text-field__input',
23-
// The BaseMatInput parent class adds `mat-input-element` and `mat-form-field-autofill-control`
24-
// to the CSS classlist, but this should not be added for this MDC equivalent input.
22+
'class': 'mat-mdc-input-element',
23+
// The BaseMatInput parent class adds `mat-input-element`, `mat-form-field-control` and
24+
// `mat-form-field-autofill-control` to the CSS class list, but this should not be added for
25+
// this MDC equivalent input.
2526
'[class.mat-form-field-autofill-control]': 'false',
2627
'[class.mat-input-element]': 'false',
28+
'[class.mat-form-field-control]': 'false',
2729
'[class.mat-input-server]': '_isServer',
2830
'[class.mat-mdc-textarea-input]': '_isTextarea',
31+
'[class.mat-mdc-form-field-control]': '_isInFormField',
32+
'[class.mdc-text-field__input]': '_isInFormField',
2933
// Native input properties that are overwritten by Angular inputs need to be synced with
3034
// the native input element. Otherwise property bindings for those don't work.
3135
'[id]': 'id',

src/material/input/input.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
107107
/** Whether the component is a textarea. */
108108
readonly _isTextarea: boolean;
109109

110+
/** Whether the input is inside of a form field. */
111+
readonly _isInFormField: boolean;
112+
110113
/**
111114
* Implemented as part of MatFormFieldControl.
112115
* @docs-private
@@ -282,6 +285,7 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
282285
this._isServer = !this._platform.isBrowser;
283286
this._isNativeSelect = nodeName === 'select';
284287
this._isTextarea = nodeName === 'textarea';
288+
this._isInFormField = !!_formField;
285289

286290
if (this._isNativeSelect) {
287291
this.controlType = (element as HTMLSelectElement).multiple ? 'mat-native-select-multiple' :

tools/public_api_guard/material/input.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export declare class MatInput extends _MatInputMixinBase implements MatFormField
88
protected _disabled: boolean;
99
protected _elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>;
1010
protected _id: string;
11+
readonly _isInFormField: boolean;
1112
readonly _isNativeSelect: boolean;
1213
readonly _isServer: boolean;
1314
readonly _isTextarea: boolean;

0 commit comments

Comments
 (0)