Skip to content

Commit 7086e44

Browse files
crisbetoandrewseguin
authored andcommitted
fix(material/input): incorrect color for select using the size attribute (#23734)
Currently we don't apply the theme colors to native `select` elements, because it can the option text to blend in with the background for Windows users. Our current approach seems to break down for inline selects (with `multiple` or `size` attributes), because they don't have a dropdown, but are instead rendered inline. These changes make it so the exclusion is only applied to `select` elements that have a dropdown. (cherry picked from commit 032cb06)
1 parent 1ac871f commit 7086e44

File tree

7 files changed

+81
-7
lines changed

7 files changed

+81
-7
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt
105105
$dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.54);
106106
$disabled-dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.38);
107107

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

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,37 @@ describe('MatMdcInput without forms', () => {
622622
expect(labelEl.classList).toContain('mdc-floating-label--float-above');
623623
}));
624624

625+
it('should mark a multi-select as being inline', fakeAsync(() => {
626+
const fixture = createComponent(MatInputSelect);
627+
fixture.detectChanges();
628+
629+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
630+
631+
expect(select.classList).not.toContain('mat-mdc-native-select-inline');
632+
633+
select.multiple = true;
634+
fixture.detectChanges();
635+
636+
expect(select.classList).toContain('mat-mdc-native-select-inline');
637+
}));
638+
639+
it('should mark a select with a size as being inline', fakeAsync(() => {
640+
const fixture = createComponent(MatInputSelect);
641+
fixture.detectChanges();
642+
643+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
644+
645+
expect(select.classList).not.toContain('mat-mdc-native-select-inline');
646+
647+
select.size = 3;
648+
fixture.detectChanges();
649+
expect(select.classList).toContain('mat-mdc-native-select-inline');
650+
651+
select.size = 1;
652+
fixture.detectChanges();
653+
expect(select.classList).not.toContain('mat-mdc-native-select-inline');
654+
}));
655+
625656
it('should not float the label if the selectedIndex is negative', fakeAsync(() => {
626657
const fixture = createComponent(MatInputSelect);
627658
fixture.detectChanges();

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ import {MatInput as BaseMatInput} from '@angular/material/input';
2626
'[class.mat-form-field-autofill-control]': 'false',
2727
'[class.mat-input-element]': 'false',
2828
'[class.mat-form-field-control]': 'false',
29+
'[class.mat-native-select-inline]': 'false',
2930
'[class.mat-input-server]': '_isServer',
3031
'[class.mat-mdc-form-field-textarea-control]': '_isInFormField && _isTextarea',
3132
'[class.mat-mdc-form-field-input-control]': '_isInFormField',
3233
'[class.mdc-text-field__input]': '_isInFormField',
34+
'[class.mat-mdc-native-select-inline]': '_isInlineSelect()',
3335
// Native input properties that are overwritten by Angular inputs need to be synced with
3436
// the native input element. Otherwise property bindings for those don't work.
3537
'[id]': 'id',

src/material/input/_input-theme.scss

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@
3737
// Since we can't change background of the dropdown, we need to explicitly
3838
// reset the color of the options to something dark.
3939
@if (map.get($config, is-dark)) {
40-
option {
41-
color: palette.$dark-primary-text;
42-
}
43-
44-
option:disabled {
45-
color: palette.$dark-disabled-text;
40+
&:not(.mat-native-select-inline) {
41+
option {
42+
color: palette.$dark-primary-text;
43+
}
44+
45+
option:disabled {
46+
color: palette.$dark-disabled-text;
47+
}
4648
}
4749
}
4850
}

src/material/input/input.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,37 @@ describe('MatInput without forms', () => {
709709
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
710710
}));
711711

712+
it('should mark a multi-select as being inline', fakeAsync(() => {
713+
const fixture = createComponent(MatInputSelect);
714+
fixture.detectChanges();
715+
716+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
717+
718+
expect(select.classList).not.toContain('mat-native-select-inline');
719+
720+
select.multiple = true;
721+
fixture.detectChanges();
722+
723+
expect(select.classList).toContain('mat-native-select-inline');
724+
}));
725+
726+
it('should mark a select with a size as being inline', fakeAsync(() => {
727+
const fixture = createComponent(MatInputSelect);
728+
fixture.detectChanges();
729+
730+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
731+
732+
expect(select.classList).not.toContain('mat-native-select-inline');
733+
734+
select.size = 3;
735+
fixture.detectChanges();
736+
expect(select.classList).toContain('mat-native-select-inline');
737+
738+
select.size = 1;
739+
fixture.detectChanges();
740+
expect(select.classList).not.toContain('mat-native-select-inline');
741+
}));
742+
712743
it('should not float the label if the selectedIndex is negative', fakeAsync(() => {
713744
const fixture = createComponent(MatInputSelect);
714745
fixture.detectChanges();

src/material/input/input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const _MatInputBase = mixinErrorState(class {
8181
'[disabled]': 'disabled',
8282
'[required]': 'required',
8383
'[attr.readonly]': 'readonly && !_isNativeSelect || null',
84+
'[class.mat-native-select-inline]': '_isInlineSelect()',
8485
// Only mark the input as invalid for assistive technology if it has a value since the
8586
// state usually overlaps with `aria-required` when the input is empty and can be redundant.
8687
'[attr.aria-invalid]': '(empty && required) ? null : errorState',
@@ -472,6 +473,12 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
472473
}
473474
}
474475

476+
/** Whether the form control is a native select that is displayed inline. */
477+
_isInlineSelect(): boolean {
478+
const element = this._elementRef.nativeElement as HTMLSelectElement;
479+
return this._isNativeSelect && (element.multiple || element.size > 1);
480+
}
481+
475482
static ngAcceptInputType_disabled: BooleanInput;
476483
static ngAcceptInputType_readonly: BooleanInput;
477484
static ngAcceptInputType_required: BooleanInput;

tools/public_api_guard/material/input.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
6161
protected _id: string;
6262
protected _isBadInput(): boolean;
6363
readonly _isInFormField: boolean;
64+
_isInlineSelect(): boolean;
6465
readonly _isNativeSelect: boolean;
6566
protected _isNeverEmpty(): boolean;
6667
readonly _isServer: boolean;

0 commit comments

Comments
 (0)