Skip to content

Commit

Permalink
fix(material/autocomplete): apply theme of parent form field to panel (
Browse files Browse the repository at this point in the history
…#25983)

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.
  • Loading branch information
zarend authored Nov 18, 2022
1 parent 7640acd commit 44657e2
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 10 deletions.
26 changes: 18 additions & 8 deletions src/dev-app/autocomplete/autocomplete-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,28 @@
<div>Reactive value: {{ stateCtrl.value | json }}</div>
<div>Reactive dirty: {{ stateCtrl.dirty }}</div>

<mat-form-field>
<mat-form-field [color]="reactiveStatesTheme">
<mat-label>State</mat-label>
<input matInput [matAutocomplete]="reactiveAuto" [formControl]="stateCtrl">
<mat-autocomplete #reactiveAuto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let state of tempStates" [value]="state">
<span>{{ state.name }}</span>
<span class="demo-secondary-text"> ({{ state.code }}) </span>
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-autocomplete #reactiveAuto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let state of tempStates" [value]="state">
<span>{{ state.name }}</span>
<span class="demo-secondary-text"> ({{ state.code }}) </span>
</mat-option>
</mat-autocomplete>

<mat-card-actions>
<button mat-button (click)="stateCtrl.reset()">RESET</button>
<button mat-button (click)="stateCtrl.setValue(states[10])">SET VALUE</button>
<button mat-button (click)="stateCtrl.enabled ? stateCtrl.disable() : stateCtrl.enable()">
TOGGLE DISABLED
</button>
<select [(ngModel)]="reactiveStatesTheme">
<option *ngFor="let theme of availableThemes" [value]="theme.value">
{{theme.name}}
</option>
</select>
</mat-card-actions>

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

<!-- Added an ngIf below to test that autocomplete works with ngIf -->
<mat-form-field *ngIf="true">
<mat-form-field *ngIf="true" [color]="templateStatesTheme">
<mat-label>State</mat-label>
<input matInput [matAutocomplete]="tdAuto" [(ngModel)]="currentState"
(ngModelChange)="tdStates = filterStates(currentState)" [disabled]="tdDisabled">
Expand All @@ -50,6 +55,11 @@
<button mat-button (click)="tdDisabled=!tdDisabled">
TOGGLE DISABLED
</button>
<select [(ngModel)]="templateStatesTheme">
<option *ngFor="let theme of availableThemes" [value]="theme.value">
{{theme.name}}
</option>
</select>
</mat-card-actions>

</mat-card>
Expand Down
10 changes: 10 additions & 0 deletions src/dev-app/autocomplete/autocomplete-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {MatCardModule} from '@angular/material/card';
import {MatInputModule} from '@angular/material/input';
import {Observable} from 'rxjs';
import {map, startWith} from 'rxjs/operators';
import {ThemePalette} from '@angular/material/core';

export interface State {
code: string;
Expand Down Expand Up @@ -52,6 +53,15 @@ export class AutocompleteDemo {

tdDisabled = false;

reactiveStatesTheme: ThemePalette = 'primary';
templateStatesTheme: ThemePalette = 'primary';

availableThemes = [
{value: 'primary', name: 'Primary'},
{value: 'accent', name: 'Accent'},
{value: 'warn', name: 'Warn'},
];

@ViewChild(NgModel) modelDir: NgModel;

groupedStates: StateGroup[];
Expand Down
1 change: 1 addition & 0 deletions src/material/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ export abstract class _MatAutocompleteTriggerBase

this.autocomplete._setVisibility();
this.autocomplete._isOpen = this._overlayAttached = true;
this.autocomplete._setColor(this._formField?.color);

// We need to do an extra `panelOpen` check in here, because the
// autocomplete won't be shown if there are no options.
Expand Down
26 changes: 24 additions & 2 deletions src/material/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,28 @@ describe('MDC-based MatAutocomplete', () => {
});
});

describe('with theming', () => {
let fixture: ComponentFixture<SimpleAutocomplete>;

beforeEach(() => {
fixture = createComponent(SimpleAutocomplete);
fixture.detectChanges();
});

it('should transfer the theme to the autocomplete panel', () => {
fixture.componentInstance.theme = 'warn';
fixture.detectChanges();

fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();

const panel = overlayContainerElement.querySelector(
'.mat-mdc-autocomplete-panel',
)! as HTMLElement;
expect(panel.classList).toContain('mat-warn');
});
});

describe('keyboard events', () => {
let fixture: ComponentFixture<SimpleAutocomplete>;
let input: HTMLInputElement;
Expand Down Expand Up @@ -3393,7 +3415,7 @@ describe('MDC-based MatAutocomplete', () => {
});

const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width" [color]="theme">
<mat-label *ngIf="hasLabel">State</mat-label>
<input
matInput
Expand All @@ -3403,7 +3425,6 @@ const SIMPLE_AUTOCOMPLETE_TEMPLATE = `
[matAutocompleteDisabled]="autocompleteDisabled"
[formControl]="stateCtrl">
</mat-form-field>
<mat-autocomplete [class]="panelClass" #auto="matAutocomplete" [displayWith]="displayFn"
[disableRipple]="disableRipple" [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby"
(opened)="openedSpy()" (closed)="closedSpy()">
Expand Down Expand Up @@ -3431,6 +3452,7 @@ class SimpleAutocomplete implements OnDestroy {
ariaLabel: string;
ariaLabelledby: string;
panelClass = 'class-one class-two';
theme: string;
openedSpy = jasmine.createSpy('autocomplete opened spy');
closedSpy = jasmine.createSpy('autocomplete closed spy');

Expand Down
17 changes: 17 additions & 0 deletions src/material/autocomplete/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
CanDisableRipple,
_MatOptionBase,
_MatOptgroupBase,
ThemePalette,
} from '@angular/material/core';
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
import {BooleanInput, coerceBooleanProperty, coerceStringArray} from '@angular/cdk/coercion';
Expand Down Expand Up @@ -122,6 +123,14 @@ export abstract class _MatAutocompleteBase
}
_isOpen: boolean = false;

/** @docs-private Sets the theme color of the panel. */
_setColor(value: ThemePalette) {
this._color = value;
this._setThemeClasses(this._classList);
}
/** @docs-private theme color of the panel */
private _color: ThemePalette;

// The @ViewChild query for TemplateRef here needs to be static because some code paths
// lead to the overlay being created before change detection has finished for this component.
// Notably, another component may trigger `focus` on the autocomplete-trigger.
Expand Down Expand Up @@ -206,6 +215,7 @@ export abstract class _MatAutocompleteBase
}

this._setVisibilityClasses(this._classList);
this._setThemeClasses(this._classList);
this._elementRef.nativeElement.className = '';
}
_classList: {[key: string]: boolean} = {};
Expand Down Expand Up @@ -296,6 +306,13 @@ export abstract class _MatAutocompleteBase
classList[this._visibleClass] = this.showPanel;
classList[this._hiddenClass] = !this.showPanel;
}

/** Sets the theming classes on a classlist based on the theme of the panel. */
private _setThemeClasses(classList: {[key: string]: boolean}) {
classList['mat-primary'] = this._color === 'primary';
classList['mat-warn'] = this._color === 'warn';
classList['mat-accent'] = this._color === 'accent';
}
}

@Component({
Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/material/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { QueryList } from '@angular/core';
import { ScrollStrategy } from '@angular/cdk/overlay';
import { SimpleChanges } from '@angular/core';
import { TemplateRef } from '@angular/core';
import { ThemePalette } from '@angular/material/core';
import { ViewContainerRef } from '@angular/core';
import { ViewportRuler } from '@angular/cdk/scrolling';

Expand Down Expand Up @@ -123,6 +124,7 @@ export abstract class _MatAutocompleteBase extends _MatAutocompleteMixinBase imp
readonly optionSelected: EventEmitter<MatAutocompleteSelectedEvent>;
panel: ElementRef;
panelWidth: string | number;
_setColor(value: ThemePalette): void;
_setScrollTop(scrollTop: number): void;
_setVisibility(): void;
showPanel: boolean;
Expand Down

0 comments on commit 44657e2

Please sign in to comment.