Skip to content

Commit

Permalink
fix(material-experimental/mdc-input): only apply styling when inside …
Browse files Browse the repository at this point in the history
…a form field (angular#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 angular#21871.
  • Loading branch information
crisbeto authored May 4, 2021
1 parent 1371a85 commit 21ab17f
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/material-experimental/mdc-chips/chip-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
Expand Down
13 changes: 10 additions & 3 deletions src/material-experimental/mdc-chips/chip-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -127,9 +129,14 @@ export class MatChipInput implements MatChipTextControl, AfterContentInit, OnCha

constructor(
protected _elementRef: ElementRef<HTMLInputElement>,
@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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand Down
23 changes: 23 additions & 0 deletions src/material-experimental/mdc-input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -1800,3 +1811,15 @@ class CustomMatInputAccessor {
class MatInputWithColor {
color: ThemePalette;
}


@Component({
template: `
<mat-form-field>
<input class="inside" matNativeControl>
</mat-form-field>
<input class="outside" matNativeControl>
`
})
class MatInputInsideOutsideFormField {}
10 changes: 7 additions & 3 deletions src/material-experimental/mdc-input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/material/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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' :
Expand Down
1 change: 1 addition & 0 deletions tools/public_api_guard/material/input.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export declare class MatInput extends _MatInputMixinBase implements MatFormField
protected _disabled: boolean;
protected _elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>;
protected _id: string;
readonly _isInFormField: boolean;
readonly _isNativeSelect: boolean;
readonly _isServer: boolean;
readonly _isTextarea: boolean;
Expand Down

0 comments on commit 21ab17f

Please sign in to comment.