Skip to content

Commit ca45c69

Browse files
committed
fix(material/autocomplete): apply theme of parent form field to panel
Apply the theme of the autocomplete's parent form field to its panel. Fix issue where theme color only applies to the input, and does not affect the panel.
1 parent cd52117 commit ca45c69

File tree

6 files changed

+76
-17
lines changed

6 files changed

+76
-17
lines changed

src/dev-app/autocomplete/autocomplete-demo.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<div>Reactive value: {{ stateCtrl.value | json }}</div>
77
<div>Reactive dirty: {{ stateCtrl.dirty }}</div>
88

9-
<mat-form-field>
9+
<mat-form-field [color]="reactiveStatesTheme">
1010
<mat-label>State</mat-label>
1111
<input matInput [matAutocomplete]="reactiveAuto" [formControl]="stateCtrl">
1212
<mat-autocomplete #reactiveAuto="matAutocomplete" [displayWith]="displayFn">
@@ -23,6 +23,11 @@
2323
<button mat-button (click)="stateCtrl.enabled ? stateCtrl.disable() : stateCtrl.enable()">
2424
TOGGLE DISABLED
2525
</button>
26+
<select [(ngModel)]="reactiveStatesTheme">
27+
<option *ngFor="let theme of availableThemes" [value]="theme.value">
28+
{{theme.name}}
29+
</option>
30+
</select>
2631
</mat-card-actions>
2732

2833
</mat-card>
@@ -33,7 +38,7 @@
3338
<div>Template-driven dirty: {{ modelDir ? modelDir.dirty : false }}</div>
3439

3540
<!-- Added an ngIf below to test that autocomplete works with ngIf -->
36-
<mat-form-field *ngIf="true">
41+
<mat-form-field *ngIf="true" [color]="templateStatesTheme">
3742
<mat-label>State</mat-label>
3843
<input matInput [matAutocomplete]="tdAuto" [(ngModel)]="currentState"
3944
(ngModelChange)="tdStates = filterStates(currentState)" [disabled]="tdDisabled">
@@ -50,6 +55,11 @@
5055
<button mat-button (click)="tdDisabled=!tdDisabled">
5156
TOGGLE DISABLED
5257
</button>
58+
<select [(ngModel)]="templateStatesTheme">
59+
<option *ngFor="let theme of availableThemes" [value]="theme.value">
60+
{{theme.name}}
61+
</option>
62+
</select>
5363
</mat-card-actions>
5464

5565
</mat-card>

src/dev-app/autocomplete/autocomplete-demo.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {MatCardModule} from '@angular/material/card';
1515
import {MatInputModule} from '@angular/material/input';
1616
import {Observable} from 'rxjs';
1717
import {map, startWith} from 'rxjs/operators';
18+
import {ThemePalette} from '@angular/material/core';
1819

1920
export interface State {
2021
code: string;
@@ -52,6 +53,15 @@ export class AutocompleteDemo {
5253

5354
tdDisabled = false;
5455

56+
reactiveStatesTheme: ThemePalette = 'primary';
57+
templateStatesTheme: ThemePalette = 'primary';
58+
59+
availableThemes = [
60+
{value: 'primary', name: 'Primary'},
61+
{value: 'accent', name: 'Accent'},
62+
{value: 'warn', name: 'Warn'},
63+
];
64+
5565
@ViewChild(NgModel) modelDir: NgModel;
5666

5767
groupedStates: StateGroup[];

src/material/autocomplete/autocomplete.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class="mat-mdc-autocomplete-panel mdc-menu-surface mdc-menu-surface--open"
44
role="listbox"
55
[id]="id"
6+
[class]="_getPanelTheme()"
67
[ngClass]="_classList"
78
[attr.aria-label]="ariaLabel || null"
89
[attr.aria-labelledby]="_getPanelAriaLabelledby(formFieldId)"

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,28 @@ describe('MDC-based MatAutocomplete', () => {
945945
});
946946
});
947947

948+
describe('with theming', () => {
949+
let fixture: ComponentFixture<SimpleAutocomplete>;
950+
951+
beforeEach(() => {
952+
fixture = createComponent(SimpleAutocomplete);
953+
fixture.detectChanges();
954+
});
955+
956+
it('should transfer the theme to the autocomplete panel', () => {
957+
fixture.componentInstance.theme = 'warn';
958+
fixture.detectChanges();
959+
960+
fixture.componentInstance.trigger.openPanel();
961+
fixture.detectChanges();
962+
963+
const panel = overlayContainerElement.querySelector(
964+
'.mat-mdc-autocomplete-panel',
965+
)! as HTMLElement;
966+
expect(panel.classList).toContain('mat-warn');
967+
});
968+
});
969+
948970
describe('keyboard events', () => {
949971
let fixture: ComponentFixture<SimpleAutocomplete>;
950972
let input: HTMLInputElement;
@@ -3393,7 +3415,7 @@ describe('MDC-based MatAutocomplete', () => {
33933415
});
33943416

33953417
const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
3396-
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
3418+
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width" [color]="theme">
33973419
<mat-label *ngIf="hasLabel">State</mat-label>
33983420
<input
33993421
matInput
@@ -3402,19 +3424,18 @@ const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
34023424
[matAutocompletePosition]="position"
34033425
[matAutocompleteDisabled]="autocompleteDisabled"
34043426
[formControl]="stateCtrl">
3427+
<mat-autocomplete [class]="panelClass" #auto="matAutocomplete" [displayWith]="displayFn"
3428+
[disableRipple]="disableRipple" [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby"
3429+
(opened)="openedSpy()" (closed)="closedSpy()">
3430+
<mat-option
3431+
*ngFor="let state of filteredStates"
3432+
[value]="state"
3433+
[style.height.px]="state.height"
3434+
[disabled]="state.disabled">
3435+
<span>{{ state.code }}: {{ state.name }}</span>
3436+
</mat-option>
3437+
</mat-autocomplete>
34053438
</mat-form-field>
3406-
3407-
<mat-autocomplete [class]="panelClass" #auto="matAutocomplete" [displayWith]="displayFn"
3408-
[disableRipple]="disableRipple" [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby"
3409-
(opened)="openedSpy()" (closed)="closedSpy()">
3410-
<mat-option
3411-
*ngFor="let state of filteredStates"
3412-
[value]="state"
3413-
[style.height.px]="state.height"
3414-
[disabled]="state.disabled">
3415-
<span>{{ state.code }}: {{ state.name }}</span>
3416-
</mat-option>
3417-
</mat-autocomplete>
34183439
`;
34193440

34203441
@Component({template: SIMPLE_AUTOCOMPLETE_TEMPLATE})
@@ -3431,6 +3452,7 @@ class SimpleAutocomplete implements OnDestroy {
34313452
ariaLabel: string;
34323453
ariaLabelledby: string;
34333454
panelClass = 'class-one class-two';
3455+
theme: string;
34343456
openedSpy = jasmine.createSpy('autocomplete opened spy');
34353457
closedSpy = jasmine.createSpy('autocomplete closed spy');
34363458

src/material/autocomplete/autocomplete.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Directive,
1616
ElementRef,
1717
EventEmitter,
18+
inject,
1819
Inject,
1920
InjectionToken,
2021
Input,
@@ -35,6 +36,7 @@ import {
3536
_MatOptionBase,
3637
_MatOptgroupBase,
3738
} from '@angular/material/core';
39+
import {MatFormField, MAT_FORM_FIELD} from '@angular/material/form-field';
3840
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
3941
import {BooleanInput, coerceBooleanProperty, coerceStringArray} from '@angular/cdk/coercion';
4042
import {Platform} from '@angular/cdk/platform';
@@ -291,6 +293,13 @@ export abstract class _MatAutocompleteBase
291293
return this.ariaLabelledby ? labelExpression + this.ariaLabelledby : labelId;
292294
}
293295

296+
/** Returns the theme to be used on the panel. */
297+
_getPanelTheme() {
298+
return this._parentFormField ? `mat-${this._parentFormField.color}` : '';
299+
}
300+
301+
protected _parentFormField: MatFormField | null = inject(MAT_FORM_FIELD, {optional: true});
302+
294303
/** Sets the autocomplete visibility classes on a classlist based on the panel is visible. */
295304
private _setVisibilityClasses(classList: {[key: string]: boolean}) {
296305
classList[this._visibleClass] = this.showPanel;

tools/public_api_guard/material/autocomplete.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
```ts
66

77
import { _AbstractConstructor } from '@angular/material/core';
8+
import { AbstractControlDirective } from '@angular/forms';
89
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
10+
import { AfterContentChecked } from '@angular/core';
911
import { AfterContentInit } from '@angular/core';
1012
import { AfterViewInit } from '@angular/core';
1113
import { BooleanInput } from '@angular/cdk/coercion';
@@ -22,12 +24,13 @@ import * as i5 from '@angular/material/core';
2224
import * as i6 from '@angular/common';
2325
import * as i7 from '@angular/cdk/scrolling';
2426
import { InjectionToken } from '@angular/core';
25-
import { MatFormField } from '@angular/material/form-field';
27+
import { MatFormField as MatFormField_2 } from '@angular/material/form-field';
2628
import { MatOptgroup } from '@angular/material/core';
2729
import { _MatOptgroupBase } from '@angular/material/core';
2830
import { MatOption } from '@angular/material/core';
2931
import { _MatOptionBase } from '@angular/material/core';
3032
import { MatOptionSelectionChange } from '@angular/material/core';
33+
import { NgControl } from '@angular/forms';
3134
import { NgZone } from '@angular/core';
3235
import { Observable } from 'rxjs';
3336
import { OnChanges } from '@angular/core';
@@ -38,6 +41,7 @@ import { QueryList } from '@angular/core';
3841
import { ScrollStrategy } from '@angular/cdk/overlay';
3942
import { SimpleChanges } from '@angular/core';
4043
import { TemplateRef } from '@angular/core';
44+
import { ThemePalette } from '@angular/material/core';
4145
import { ViewContainerRef } from '@angular/core';
4246
import { ViewportRuler } from '@angular/cdk/scrolling';
4347

@@ -104,6 +108,7 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp
104108
displayWith: ((value: any) => string) | null;
105109
_emitSelectEvent(option: _MatOptionBase): void;
106110
_getPanelAriaLabelledby(labelId: string | null): string | null;
111+
_getPanelTheme(): string;
107112
_getScrollTop(): number;
108113
protected abstract _hiddenClass: string;
109114
id: string;
@@ -123,6 +128,8 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp
123128
readonly optionSelected: EventEmitter<MatAutocompleteSelectedEvent>;
124129
panel: ElementRef;
125130
panelWidth: string | number;
131+
// (undocumented)
132+
protected _parentFormField: MatFormField | null;
126133
_setScrollTop(scrollTop: number): void;
127134
_setVisibility(): void;
128135
showPanel: boolean;
@@ -191,7 +198,7 @@ export class MatAutocompleteTrigger extends _MatAutocompleteTriggerBase {
191198

192199
// @public
193200
export abstract class _MatAutocompleteTriggerBase implements ControlValueAccessor, AfterViewInit, OnChanges, OnDestroy {
194-
constructor(_element: ElementRef<HTMLInputElement>, _overlay: Overlay, _viewContainerRef: ViewContainerRef, _zone: NgZone, _changeDetectorRef: ChangeDetectorRef, scrollStrategy: any, _dir: Directionality, _formField: MatFormField, _document: any, _viewportRuler: ViewportRuler, _defaults?: MatAutocompleteDefaultOptions | undefined);
201+
constructor(_element: ElementRef<HTMLInputElement>, _overlay: Overlay, _viewContainerRef: ViewContainerRef, _zone: NgZone, _changeDetectorRef: ChangeDetectorRef, scrollStrategy: any, _dir: Directionality, _formField: MatFormField_2, _document: any, _viewportRuler: ViewportRuler, _defaults?: MatAutocompleteDefaultOptions | undefined);
195202
protected abstract _aboveClass: string;
196203
get activeOption(): _MatOptionBase | null;
197204
autocomplete: _MatAutocompleteBase;

0 commit comments

Comments
 (0)