Skip to content

feat(material/icon): add default options #23638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 80 additions & 2 deletions src/material/icon/icon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
HttpTestingController,
TestRequest,
} from '@angular/common/http/testing';
import {Component, ErrorHandler, ViewChild} from '@angular/core';
import {MatIconModule, MAT_ICON_LOCATION} from './index';
import {Component, ErrorHandler, Provider, Type, ViewChild} from '@angular/core';
import {MAT_ICON_DEFAULT_OPTIONS, MAT_ICON_LOCATION, MatIconModule} from './index';
import {MatIconRegistry, getMatIconNoHttpProviderError} from './icon-registry';
import {FAKE_SVGS} from './fake-svgs';
import {wrappedErrorMessage} from '../../cdk/testing/private';
Expand Down Expand Up @@ -41,6 +41,19 @@ function verifyPathChildElement(element: Element, attributeValue: string): void
expect(pathElement.getAttribute('name')).toBe(attributeValue);
}

/** Creates a test component fixture. */
function createComponent<T>(component: Type<T>, providers: Provider[] = []) {
TestBed.configureTestingModule({
imports: [MatIconModule],
declarations: [component],
providers: [...providers],
});

TestBed.compileComponents();

return TestBed.createComponent<T>(component);
}

describe('MatIcon', () => {
let fakePath: string;
let errorHandler: jasmine.SpyObj<ErrorHandler>;
Expand Down Expand Up @@ -1237,6 +1250,71 @@ describe('MatIcon without HttpClientModule', () => {
});
});

describe('MatIcon with default options', () => {
it('should be able to configure color globally', fakeAsync(() => {
const fixture = createComponent(IconWithLigature, [
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {color: 'accent'}},
]);
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
fixture.detectChanges();
expect(iconElement.classList).not.toContain('mat-icon-no-color');
expect(iconElement.classList).toContain('mat-accent');
}));

it('should use passed color rather then color provided', fakeAsync(() => {
const fixture = createComponent(IconWithColor, [
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {color: 'warn'}},
]);
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
fixture.detectChanges();
expect(iconElement.classList).not.toContain('mat-warn');
expect(iconElement.classList).toContain('mat-primary');
}));

it('should use default color if no color passed', fakeAsync(() => {
const fixture = createComponent(IconWithColor, [
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {color: 'accent'}},
]);
const component = fixture.componentInstance;
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
component.iconColor = '';
fixture.detectChanges();
expect(iconElement.classList).not.toContain('mat-icon-no-color');
expect(iconElement.classList).not.toContain('mat-primary');
expect(iconElement.classList).toContain('mat-accent');
}));

it('should be able to configure font set globally', fakeAsync(() => {
const fixture = createComponent(IconWithLigature, [
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {fontSet: 'custom-font-set'}},
]);
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
fixture.detectChanges();
expect(iconElement.classList).toContain('custom-font-set');
}));

it('should use passed fontSet rather then default one', fakeAsync(() => {
const fixture = createComponent(IconWithCustomFontCss, [
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {fontSet: 'default-font-set'}},
]);
const component = fixture.componentInstance;
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
component.fontSet = 'custom-font-set';
fixture.detectChanges();
expect(iconElement.classList).not.toContain('default-font-set');
expect(iconElement.classList).toContain('custom-font-set');
}));

it('should use passed empty fontSet rather then default one', fakeAsync(() => {
const fixture = createComponent(IconWithCustomFontCss, [
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {fontSet: 'default-font-set'}},
]);
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
fixture.detectChanges();
expect(iconElement.classList).not.toContain('default-font-set');
}));
});

@Component({template: `<mat-icon>{{iconName}}</mat-icon>`})
class IconWithLigature {
iconName = '';
Expand Down
29 changes: 28 additions & 1 deletion src/material/icon/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import {
Input,
OnDestroy,
OnInit,
Optional,
ViewEncapsulation,
} from '@angular/core';
import {CanColor, mixinColor} from '@angular/material/core';
import {CanColor, ThemePalette, mixinColor} from '@angular/material/core';
import {Subscription} from 'rxjs';
import {take} from 'rxjs/operators';

Expand All @@ -37,6 +38,19 @@ const _MatIconBase = mixinColor(
},
);

/** Default options for `mat-icon`. */
export interface MatIconDefaultOptions {
/** Default color of the icon. */
color?: ThemePalette;
/** Font set that the icon is a part of. */
fontSet?: string;
}

/** Injection token to be used to override the default options for `mat-icon`. */
export const MAT_ICON_DEFAULT_OPTIONS = new InjectionToken<MatIconDefaultOptions>(
'MAT_ICON_DEFAULT_OPTIONS',
);

/**
* Injection token used to provide the current location to `MatIcon`.
* Used to handle server-side rendering and to stub out during unit tests.
Expand Down Expand Up @@ -216,9 +230,22 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
@Attribute('aria-hidden') ariaHidden: string,
@Inject(MAT_ICON_LOCATION) private _location: MatIconLocation,
private readonly _errorHandler: ErrorHandler,
@Optional()
@Inject(MAT_ICON_DEFAULT_OPTIONS)
defaults?: MatIconDefaultOptions,
) {
super(elementRef);

if (defaults) {
if (defaults.color) {
this.color = this.defaultColor = defaults.color;
}

if (defaults.fontSet) {
this.fontSet = defaults.fontSet;
}
}

// If the user has not explicitly set aria-hidden, mark the icon as hidden, as this is
// the right thing to do for the majority of icon use-cases.
if (!ariaHidden) {
Expand Down
14 changes: 12 additions & 2 deletions tools/public_api_guard/material/icon.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { OnInit } from '@angular/core';
import { Optional } from '@angular/core';
import { SafeHtml } from '@angular/platform-browser';
import { SafeResourceUrl } from '@angular/platform-browser';
import { ThemePalette } from '@angular/material/core';

// @public
export function getMatIconFailedToSanitizeLiteralError(literal: SafeHtml): Error;
Expand Down Expand Up @@ -54,6 +55,9 @@ export interface IconOptions {
// @public
export type IconResolver = (name: string, namespace: string) => SafeResourceUrl | SafeResourceUrlWithIconOptions | null;

// @public
export const MAT_ICON_DEFAULT_OPTIONS: InjectionToken<MatIconDefaultOptions>;

// @public
export const MAT_ICON_LOCATION: InjectionToken<MatIconLocation>;

Expand All @@ -62,7 +66,7 @@ export function MAT_ICON_LOCATION_FACTORY(): MatIconLocation;

// @public
export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, CanColor, OnDestroy {
constructor(elementRef: ElementRef<HTMLElement>, _iconRegistry: MatIconRegistry, ariaHidden: string, _location: MatIconLocation, _errorHandler: ErrorHandler);
constructor(elementRef: ElementRef<HTMLElement>, _iconRegistry: MatIconRegistry, ariaHidden: string, _location: MatIconLocation, _errorHandler: ErrorHandler, defaults?: MatIconDefaultOptions);
get fontIcon(): string;
set fontIcon(value: string);
get fontSet(): string;
Expand All @@ -86,7 +90,13 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<MatIcon, "mat-icon", ["matIcon"], { "color": "color"; "inline": "inline"; "svgIcon": "svgIcon"; "fontSet": "fontSet"; "fontIcon": "fontIcon"; }, {}, never, ["*"]>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatIcon, [null, null, { attribute: "aria-hidden"; }, null, null]>;
static ɵfac: i0.ɵɵFactoryDeclaration<MatIcon, [null, null, { attribute: "aria-hidden"; }, null, null, { optional: true; }]>;
}

// @public
export interface MatIconDefaultOptions {
color?: ThemePalette;
fontSet?: string;
}

// @public
Expand Down