Skip to content

Commit 0500652

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 38e26e9 commit 0500652

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
@@ -80,7 +80,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt
8080
$dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.54);
8181
$disabled-dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.38);
8282

83-
select.mat-mdc-form-field-input-control {
83+
select.mat-mdc-form-field-input-control:not(.mat-mdc-native-select-inline) {
8484
// On dark themes we set the native `select` color to some shade of white,
8585
// however the color propagates to all of the `option` elements, which are
8686
// 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
@@ -659,6 +659,37 @@ describe('MatMdcInput without forms', () => {
659659
expect(labelEl.classList).toContain('mdc-floating-label--float-above');
660660
}));
661661

662+
it('should mark a multi-select as being inline', fakeAsync(() => {
663+
const fixture = createComponent(MatInputSelect);
664+
fixture.detectChanges();
665+
666+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
667+
668+
expect(select.classList).not.toContain('mat-mdc-native-select-inline');
669+
670+
select.multiple = true;
671+
fixture.detectChanges();
672+
673+
expect(select.classList).toContain('mat-mdc-native-select-inline');
674+
}));
675+
676+
it('should mark a select with a size as being inline', fakeAsync(() => {
677+
const fixture = createComponent(MatInputSelect);
678+
fixture.detectChanges();
679+
680+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
681+
682+
expect(select.classList).not.toContain('mat-mdc-native-select-inline');
683+
684+
select.size = 3;
685+
fixture.detectChanges();
686+
expect(select.classList).toContain('mat-mdc-native-select-inline');
687+
688+
select.size = 1;
689+
fixture.detectChanges();
690+
expect(select.classList).not.toContain('mat-mdc-native-select-inline');
691+
}));
692+
662693
it('should not float the label if the selectedIndex is negative', fakeAsync(() => {
663694
const fixture = createComponent(MatInputSelect);
664695
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
@@ -732,6 +732,37 @@ describe('MatInput without forms', () => {
732732
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
733733
}));
734734

735+
it('should mark a multi-select as being inline', fakeAsync(() => {
736+
const fixture = createComponent(MatInputSelect);
737+
fixture.detectChanges();
738+
739+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
740+
741+
expect(select.classList).not.toContain('mat-native-select-inline');
742+
743+
select.multiple = true;
744+
fixture.detectChanges();
745+
746+
expect(select.classList).toContain('mat-native-select-inline');
747+
}));
748+
749+
it('should mark a select with a size as being inline', fakeAsync(() => {
750+
const fixture = createComponent(MatInputSelect);
751+
fixture.detectChanges();
752+
753+
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select');
754+
755+
expect(select.classList).not.toContain('mat-native-select-inline');
756+
757+
select.size = 3;
758+
fixture.detectChanges();
759+
expect(select.classList).toContain('mat-native-select-inline');
760+
761+
select.size = 1;
762+
fixture.detectChanges();
763+
expect(select.classList).not.toContain('mat-native-select-inline');
764+
}));
765+
735766
it('should not float the label if the selectedIndex is negative', fakeAsync(() => {
736767
const fixture = createComponent(MatInputSelect);
737768
fixture.detectChanges();

src/material/input/input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const _MatInputBase = mixinErrorState(
8080
'[disabled]': 'disabled',
8181
'[required]': 'required',
8282
'[attr.readonly]': 'readonly && !_isNativeSelect || null',
83+
'[class.mat-native-select-inline]': '_isInlineSelect()',
8384
// Only mark the input as invalid for assistive technology if it has a value since the
8485
// state usually overlaps with `aria-required` when the input is empty and can be redundant.
8586
'[attr.aria-invalid]': '(empty && required) ? null : errorState',
@@ -507,6 +508,12 @@ export class MatInput
507508
}
508509
}
509510

511+
/** Whether the form control is a native select that is displayed inline. */
512+
_isInlineSelect(): boolean {
513+
const element = this._elementRef.nativeElement as HTMLSelectElement;
514+
return this._isNativeSelect && (element.multiple || element.size > 1);
515+
}
516+
510517
static ngAcceptInputType_disabled: BooleanInput;
511518
static ngAcceptInputType_readonly: BooleanInput;
512519
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
@@ -60,6 +60,7 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
6060
protected _id: string;
6161
protected _isBadInput(): boolean;
6262
readonly _isInFormField: boolean;
63+
_isInlineSelect(): boolean;
6364
readonly _isNativeSelect: boolean;
6465
protected _isNeverEmpty(): boolean;
6566
readonly _isServer: boolean;

0 commit comments

Comments
 (0)