Skip to content

Commit 068006d

Browse files
committed
feat(material/icon): add default options
closes #23548
1 parent 19b8934 commit 068006d

File tree

3 files changed

+155
-5
lines changed

3 files changed

+155
-5
lines changed

src/material/icon/icon.spec.ts

+115-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
HttpTestingController,
66
TestRequest,
77
} from '@angular/common/http/testing';
8-
import {Component, ErrorHandler, ViewChild} from '@angular/core';
9-
import {MatIconModule, MAT_ICON_LOCATION} from './index';
8+
import {Component, ErrorHandler, Provider, Type, ViewChild} from '@angular/core';
9+
import {MAT_ICON_DEFAULT_OPTIONS, MAT_ICON_LOCATION, MatIconModule} from './index';
1010
import {MatIconRegistry, getMatIconNoHttpProviderError} from './icon-registry';
1111
import {FAKE_SVGS} from './fake-svgs';
1212
import {wrappedErrorMessage} from '../../cdk/testing/private';
@@ -41,6 +41,19 @@ function verifyPathChildElement(element: Element, attributeValue: string): void
4141
expect(pathElement.getAttribute('name')).toBe(attributeValue);
4242
}
4343

44+
/** Creates a test component fixture. */
45+
function createComponent<T>(component: Type<T>, providers: Provider[] = []) {
46+
TestBed.configureTestingModule({
47+
imports: [MatIconModule],
48+
declarations: [component],
49+
providers: [...providers],
50+
});
51+
52+
TestBed.compileComponents();
53+
54+
return TestBed.createComponent<T>(component);
55+
}
56+
4457
describe('MatIcon', () => {
4558
let fakePath: string;
4659
let errorHandler: jasmine.SpyObj<ErrorHandler>;
@@ -1237,6 +1250,106 @@ describe('MatIcon without HttpClientModule', () => {
12371250
});
12381251
});
12391252

1253+
describe('MatIcon with default options', () => {
1254+
it('should be able to configure color globally', fakeAsync(() => {
1255+
const fixture = createComponent(IconWithLigature, [
1256+
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {color: 'accent'}},
1257+
]);
1258+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
1259+
fixture.detectChanges();
1260+
1261+
expect(iconElement.classList.contains('mat-icon-no-color'))
1262+
.withContext('Expected the mat-icon element to not include the mat-icon-no-color class')
1263+
.toBeFalsy();
1264+
1265+
expect(iconElement.classList.contains('mat-accent'))
1266+
.withContext('Expected the mat-icon element to include the default accent styling class')
1267+
.toBeTruthy();
1268+
}));
1269+
1270+
it('should use passed color rather then color provided', fakeAsync(() => {
1271+
const fixture = createComponent(IconWithColor, [
1272+
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {color: 'warn'}},
1273+
]);
1274+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
1275+
fixture.detectChanges();
1276+
1277+
expect(iconElement.classList.contains('mat-warn'))
1278+
.withContext('Expected the mat-icon element to not include the default warn styling class')
1279+
.toBeFalsy();
1280+
1281+
expect(iconElement.classList.contains('mat-primary'))
1282+
.withContext('Expected the mat-icon element to include the passed primary styling class')
1283+
.toBeTruthy();
1284+
}));
1285+
1286+
it('should use default color if no color passed', fakeAsync(() => {
1287+
const fixture = createComponent(IconWithColor, [
1288+
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {color: 'accent'}},
1289+
]);
1290+
const component = fixture.componentInstance;
1291+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
1292+
component.iconColor = '';
1293+
fixture.detectChanges();
1294+
1295+
expect(iconElement.classList.contains('mat-icon-no-color'))
1296+
.withContext('Expected the mat-icon element to not include the mat-icon-no-color class')
1297+
.toBeFalsy();
1298+
1299+
expect(iconElement.classList.contains('mat-primary'))
1300+
.withContext('Expected the mat-icon element to not include the primary styling class')
1301+
.toBeFalsy();
1302+
1303+
expect(iconElement.classList.contains('mat-accent'))
1304+
.withContext('Expected the mat-icon element to include the default accent styling class')
1305+
.toBeTruthy();
1306+
}));
1307+
1308+
it('should be able to configure font set globally', fakeAsync(() => {
1309+
const fixture = createComponent(IconWithLigature, [
1310+
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {fontSet: 'custom-font-set'}},
1311+
]);
1312+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
1313+
fixture.detectChanges();
1314+
expect(iconElement.classList.contains('custom-font-set'))
1315+
.withContext('Expected the mat-icon element to include the default custom-font-set font set')
1316+
.toBeTruthy();
1317+
}));
1318+
1319+
it('should use passed fontSet rather then default one', fakeAsync(() => {
1320+
const fixture = createComponent(IconWithCustomFontCss, [
1321+
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {fontSet: 'default-font-set'}},
1322+
]);
1323+
const component = fixture.componentInstance;
1324+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
1325+
component.fontSet = 'custom-font-set';
1326+
fixture.detectChanges();
1327+
1328+
expect(iconElement.classList.contains('default-font-set'))
1329+
.withContext(
1330+
'Expected the mat-icon element to not include the default default-font-set font set',
1331+
)
1332+
.toBeFalsy();
1333+
1334+
expect(iconElement.classList.contains('custom-font-set'))
1335+
.withContext('Expected the mat-icon element to include the passed custom-font-set font set')
1336+
.toBeTruthy();
1337+
}));
1338+
1339+
it('should use passed empty fontSet rather then default one', fakeAsync(() => {
1340+
const fixture = createComponent(IconWithCustomFontCss, [
1341+
{provide: MAT_ICON_DEFAULT_OPTIONS, useValue: {fontSet: 'default-font-set'}},
1342+
]);
1343+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
1344+
fixture.detectChanges();
1345+
expect(iconElement.classList.contains('default-font-set'))
1346+
.withContext(
1347+
'Expected the mat-icon element to not include the default default-font-set font set',
1348+
)
1349+
.toBeFalsy();
1350+
}));
1351+
});
1352+
12401353
@Component({template: `<mat-icon>{{iconName}}</mat-icon>`})
12411354
class IconWithLigature {
12421355
iconName = '';

src/material/icon/icon.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import {
2121
Input,
2222
OnDestroy,
2323
OnInit,
24+
Optional,
2425
ViewEncapsulation,
2526
} from '@angular/core';
26-
import {CanColor, mixinColor} from '@angular/material/core';
27+
import {CanColor, ThemePalette, mixinColor} from '@angular/material/core';
2728
import {Subscription} from 'rxjs';
2829
import {take} from 'rxjs/operators';
2930

@@ -37,6 +38,19 @@ const _MatIconBase = mixinColor(
3738
},
3839
);
3940

41+
/** Default options for `mat-icon`. */
42+
export interface MatIconDefaultOptions {
43+
/** Default color of the icon. */
44+
color?: ThemePalette;
45+
/** Font set that the icon is a part of. */
46+
fontSet?: string;
47+
}
48+
49+
/** Injection token to be used to override the default options for `mat-icon`. */
50+
export const MAT_ICON_DEFAULT_OPTIONS = new InjectionToken<MatIconDefaultOptions>(
51+
'MAT_ICON_DEFAULT_OPTIONS',
52+
);
53+
4054
/**
4155
* Injection token used to provide the current location to `MatIcon`.
4256
* Used to handle server-side rendering and to stub out during unit tests.
@@ -216,9 +230,22 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
216230
@Attribute('aria-hidden') ariaHidden: string,
217231
@Inject(MAT_ICON_LOCATION) private _location: MatIconLocation,
218232
private readonly _errorHandler: ErrorHandler,
233+
@Optional()
234+
@Inject(MAT_ICON_DEFAULT_OPTIONS)
235+
defaults?: MatIconDefaultOptions,
219236
) {
220237
super(elementRef);
221238

239+
if (defaults) {
240+
if (defaults.color) {
241+
this.color = this.defaultColor = defaults.color;
242+
}
243+
244+
if (defaults.fontSet) {
245+
this.fontSet = defaults.fontSet;
246+
}
247+
}
248+
222249
// If the user has not explicitly set aria-hidden, mark the icon as hidden, as this is
223250
// the right thing to do for the majority of icon use-cases.
224251
if (!ariaHidden) {

tools/public_api_guard/material/icon.md

+12-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { OnInit } from '@angular/core';
2222
import { Optional } from '@angular/core';
2323
import { SafeHtml } from '@angular/platform-browser';
2424
import { SafeResourceUrl } from '@angular/platform-browser';
25+
import { ThemePalette } from '@angular/material/core';
2526

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

58+
// @public
59+
export const MAT_ICON_DEFAULT_OPTIONS: InjectionToken<MatIconDefaultOptions>;
60+
5761
// @public
5862
export const MAT_ICON_LOCATION: InjectionToken<MatIconLocation>;
5963

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

6367
// @public
6468
export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, CanColor, OnDestroy {
65-
constructor(elementRef: ElementRef<HTMLElement>, _iconRegistry: MatIconRegistry, ariaHidden: string, _location: MatIconLocation, _errorHandler: ErrorHandler);
69+
constructor(elementRef: ElementRef<HTMLElement>, _iconRegistry: MatIconRegistry, ariaHidden: string, _location: MatIconLocation, _errorHandler: ErrorHandler, defaults?: MatIconDefaultOptions);
6670
get fontIcon(): string;
6771
set fontIcon(value: string);
6872
get fontSet(): string;
@@ -86,7 +90,13 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
8690
// (undocumented)
8791
static ɵcmp: i0.ɵɵComponentDeclaration<MatIcon, "mat-icon", ["matIcon"], { "color": "color"; "inline": "inline"; "svgIcon": "svgIcon"; "fontSet": "fontSet"; "fontIcon": "fontIcon"; }, {}, never, ["*"]>;
8892
// (undocumented)
89-
static ɵfac: i0.ɵɵFactoryDeclaration<MatIcon, [null, null, { attribute: "aria-hidden"; }, null, null]>;
93+
static ɵfac: i0.ɵɵFactoryDeclaration<MatIcon, [null, null, { attribute: "aria-hidden"; }, null, null, { optional: true; }]>;
94+
}
95+
96+
// @public
97+
export interface MatIconDefaultOptions {
98+
color?: ThemePalette;
99+
fontSet?: string;
90100
}
91101

92102
// @public

0 commit comments

Comments
 (0)