diff --git a/src/components-examples/material/icon/icon-harness/icon-harness-example.html b/src/components-examples/material/icon/icon-harness/icon-harness-example.html
index 5769529ec1e6..57af4c464de2 100644
--- a/src/components-examples/material/icon/icon-harness/icon-harness-example.html
+++ b/src/components-examples/material/icon/icon-harness/icon-harness-example.html
@@ -1,3 +1,4 @@
ligature_icon
+
diff --git a/src/components-examples/material/icon/icon-harness/icon-harness-example.spec.ts b/src/components-examples/material/icon/icon-harness/icon-harness-example.spec.ts
index 4b6f1c9d3dd1..c4cd4674cb7a 100644
--- a/src/components-examples/material/icon/icon-harness/icon-harness-example.spec.ts
+++ b/src/components-examples/material/icon/icon-harness/icon-harness-example.spec.ts
@@ -32,24 +32,24 @@ describe('IconHarnessExample', () => {
it('should load all icon harnesses', async () => {
const icons = await loader.getAllHarnesses(MatIconHarness);
- expect(icons.length).toBe(3);
+ expect(icons.length).toBe(4);
});
it('should get the name of an icon', async () => {
const icons = await loader.getAllHarnesses(MatIconHarness);
const names = await parallel(() => icons.map(icon => icon.getName()));
- expect(names).toEqual(['fontIcon', 'svgIcon', 'ligature_icon']);
+ expect(names).toEqual(['fontIcon', 'svgIcon', 'ligature_icon', 'ligature_icon_by_attribute']);
});
it('should get the namespace of an icon', async () => {
const icons = await loader.getAllHarnesses(MatIconHarness);
const namespaces = await parallel(() => icons.map(icon => icon.getNamespace()));
- expect(namespaces).toEqual(['fontIcons', 'svgIcons', null]);
+ expect(namespaces).toEqual(['fontIcons', 'svgIcons', null, null]);
});
it('should get whether an icon is inline', async () => {
const icons = await loader.getAllHarnesses(MatIconHarness);
const inlineStates = await parallel(() => icons.map(icon => icon.isInline()));
- expect(inlineStates).toEqual([false, false, true]);
+ expect(inlineStates).toEqual([false, false, true, true]);
});
});
diff --git a/src/components-examples/material/icon/icon-overview/icon-overview-example.html b/src/components-examples/material/icon/icon-overview/icon-overview-example.html
index ac069beed3fd..3dd06c0c3017 100644
--- a/src/components-examples/material/icon/icon-overview/icon-overview-example.html
+++ b/src/components-examples/material/icon/icon-overview/icon-overview-example.html
@@ -1 +1 @@
-home
+
diff --git a/src/dev-app/icon/icon-demo.html b/src/dev-app/icon/icon-demo.html
index aff0f54360d7..e8cfdd913a13 100644
--- a/src/dev-app/icon/icon-demo.html
+++ b/src/dev-app/icon/icon-demo.html
@@ -38,7 +38,12 @@
- Ligature from Material Icons font:
+ Ligature from Material Icons font by attribute:
+
+
+
+
+ Ligature from Material Icons font by content:
home
diff --git a/src/material/icon/icon.scss b/src/material/icon/icon.scss
index 66a37a418388..da684252489b 100644
--- a/src/material/icon/icon.scss
+++ b/src/material/icon/icon.scss
@@ -21,6 +21,10 @@ $size: 24px !default;
line-height: inherit;
width: inherit;
}
+
+ &[icon]::before {
+ content: attr(icon);
+ }
}
// Icons that will be mirrored in RTL.
diff --git a/src/material/icon/icon.spec.ts b/src/material/icon/icon.spec.ts
index 541644d464b8..8e8b8852d081 100644
--- a/src/material/icon/icon.spec.ts
+++ b/src/material/icon/icon.spec.ts
@@ -1,5 +1,5 @@
-import {inject, waitForAsync, fakeAsync, tick, TestBed} from '@angular/core/testing';
-import {SafeResourceUrl, DomSanitizer, SafeHtml} from '@angular/platform-browser';
+import {fakeAsync, inject, TestBed, tick, waitForAsync} from '@angular/core/testing';
+import {DomSanitizer, SafeHtml, SafeResourceUrl} from '@angular/platform-browser';
import {
HttpClientTestingModule,
HttpTestingController,
@@ -7,7 +7,7 @@ import {
} from '@angular/common/http/testing';
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 {getMatIconNoHttpProviderError, MatIconRegistry} from './icon-registry';
import {FAKE_SVGS} from './fake-svgs';
import {wrappedErrorMessage} from '../../cdk/testing/private';
import {MatIcon} from './icon';
@@ -69,6 +69,7 @@ describe('MatIcon', () => {
declarations: [
IconWithColor,
IconWithLigature,
+ IconWithLigatureByAttribute,
IconWithCustomFontCss,
IconFromSvgName,
IconWithAriaHiddenFalse,
@@ -241,6 +242,58 @@ describe('MatIcon', () => {
});
});
+ describe('Ligature icons by attribute', () => {
+ it('should add material-icons class by default', () => {
+ const fixture = TestBed.createComponent(IconWithLigatureByAttribute);
+
+ const testComponent = fixture.componentInstance;
+ const matIconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
+ testComponent.iconName = 'home';
+ fixture.detectChanges();
+ expect(sortedClassNames(matIconElement)).toEqual([
+ 'mat-icon',
+ 'mat-icon-no-color',
+ 'material-icons',
+ 'notranslate',
+ ]);
+ });
+
+ it('should use alternate icon font if set', () => {
+ iconRegistry.setDefaultFontSetClass('myfont');
+
+ const fixture = TestBed.createComponent(IconWithLigatureByAttribute);
+
+ const testComponent = fixture.componentInstance;
+ const matIconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
+ testComponent.iconName = 'home';
+ fixture.detectChanges();
+ expect(sortedClassNames(matIconElement)).toEqual([
+ 'mat-icon',
+ 'mat-icon-no-color',
+ 'myfont',
+ 'notranslate',
+ ]);
+ });
+
+ it('should be able to provide multiple alternate icon set classes', () => {
+ iconRegistry.setDefaultFontSetClass('myfont', 'myfont-48x48');
+
+ let fixture = TestBed.createComponent(IconWithLigatureByAttribute);
+
+ const testComponent = fixture.componentInstance;
+ const matIconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
+ testComponent.iconName = 'home';
+ fixture.detectChanges();
+ expect(sortedClassNames(matIconElement)).toEqual([
+ 'mat-icon',
+ 'mat-icon-no-color',
+ 'myfont',
+ 'myfont-48x48',
+ 'notranslate',
+ ]);
+ });
+ });
+
describe('Icons from URLs', () => {
it('should register icon URLs by name', fakeAsync(() => {
iconRegistry.addSvgIcon('fluffy', trustUrl('cat.svg'));
@@ -1311,6 +1364,11 @@ class IconWithLigature {
iconName = '';
}
+@Component({template: ``})
+class IconWithLigatureByAttribute {
+ iconName = '';
+}
+
@Component({template: `{{iconName}}`})
class IconWithColor {
iconName = '';
diff --git a/src/material/icon/icon.ts b/src/material/icon/icon.ts
index a7f6a1d8d733..b892fdbb86ad 100644
--- a/src/material/icon/icon.ts
+++ b/src/material/icon/icon.ts
@@ -24,7 +24,7 @@ import {
Optional,
ViewEncapsulation,
} from '@angular/core';
-import {CanColor, ThemePalette, mixinColor} from '@angular/material/core';
+import {CanColor, mixinColor, ThemePalette} from '@angular/material/core';
import {Subscription} from 'rxjs';
import {take} from 'rxjs/operators';
@@ -114,13 +114,16 @@ const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/;
* `
* `
*
- * - Use a font ligature as an icon by putting the ligature text in the content of the ``
- * component. By default the Material icons font is used as described at
+ * - Use a font ligature as an icon by putting the ligature text in the `icon` attribute or the
+ * content of the `` component. It is recommended to use the attribute alternative
+ * to prevent the ligature text to be selectable and to appear in search engine results.
+ * By default the Material icons font is used as described at
* http://google.github.io/material-design-icons/#icon-font-for-the-web. You can specify an
* alternate font by setting the fontSet input to either the CSS class to apply to use the
* desired font, or to an alias previously registered with MatIconRegistry.registerFontClassAlias.
- * Examples:
- * `home
+ * `
+ *
+ * home
* sun`
*
* - Specify a font glyph to be included via CSS rules by setting the fontSet input to specify the
@@ -140,7 +143,7 @@ const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/;
'role': 'img',
'class': 'mat-icon notranslate',
'[attr.data-mat-icon-type]': '_usingFontIcon() ? "font" : "svg"',
- '[attr.data-mat-icon-name]': '_svgName || fontIcon',
+ '[attr.data-mat-icon-name]': '_svgName || fontIcon || icon',
'[attr.data-mat-icon-namespace]': '_svgNamespace || fontSet',
'[class.mat-icon-inline]': 'inline',
'[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"',
@@ -162,6 +165,13 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
}
private _inline: boolean = false;
+ /**
+ * Name of an icon within a font set that use ligatures, such as the
+ * [Material icons font](http://google.github.io/material-design-icons/#icon-font-for-the-web).
+ */
+ @Input()
+ icon: string;
+
/** Name of the icon in the SVG icon set. */
@Input()
get svgIcon(): string {
@@ -194,7 +204,10 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
}
private _fontSet: string;
- /** Name of an icon within a font set. */
+ /**
+ * Name of an icon within a font set that use CSS class for each icon glyph, such as
+ * [FontAwesome](https://fortawesome.github.io/Font-Awesome/examples/).
+ */
@Input()
get fontIcon(): string {
return this._fontIcon;
diff --git a/src/material/icon/testing/shared.spec.ts b/src/material/icon/testing/shared.spec.ts
index 36c6c5ee9874..1a302fe04d61 100644
--- a/src/material/icon/testing/shared.spec.ts
+++ b/src/material/icon/testing/shared.spec.ts
@@ -37,7 +37,7 @@ export function runHarnessTests(
it('should load all icon harnesses', async () => {
const icons = await loader.getAllHarnesses(iconHarness);
- expect(icons.length).toBe(3);
+ expect(icons.length).toBe(4);
});
it('should filter icon harnesses based on their type', async () => {
@@ -47,7 +47,7 @@ export function runHarnessTests(
]);
expect(svgIcons.length).toBe(1);
- expect(fontIcons.length).toBe(2);
+ expect(fontIcons.length).toBe(3);
});
it('should filter icon harnesses based on their name', async () => {
@@ -69,31 +69,31 @@ export function runHarnessTests(
expect(regexFilterResults.length).toBe(1);
expect(stringFilterResults.length).toBe(1);
- expect(nullFilterResults.length).toBe(1);
+ expect(nullFilterResults.length).toBe(2);
});
it('should get the type of each icon', async () => {
const icons = await loader.getAllHarnesses(iconHarness);
const types = await parallel(() => icons.map(icon => icon.getType()));
- expect(types).toEqual([IconType.FONT, IconType.SVG, IconType.FONT]);
+ expect(types).toEqual([IconType.FONT, IconType.SVG, IconType.FONT, IconType.FONT]);
});
it('should get the name of an icon', async () => {
const icons = await loader.getAllHarnesses(iconHarness);
const names = await parallel(() => icons.map(icon => icon.getName()));
- expect(names).toEqual(['fontIcon', 'svgIcon', 'ligature_icon']);
+ expect(names).toEqual(['fontIcon', 'svgIcon', 'ligature_icon', 'ligature_icon_by_attribute']);
});
it('should get the namespace of an icon', async () => {
const icons = await loader.getAllHarnesses(iconHarness);
const namespaces = await parallel(() => icons.map(icon => icon.getNamespace()));
- expect(namespaces).toEqual(['fontIcons', 'svgIcons', null]);
+ expect(namespaces).toEqual(['fontIcons', 'svgIcons', null, null]);
});
it('should get whether an icon is inline', async () => {
const icons = await loader.getAllHarnesses(iconHarness);
const inlineStates = await parallel(() => icons.map(icon => icon.isInline()));
- expect(inlineStates).toEqual([false, false, true]);
+ expect(inlineStates).toEqual([false, false, true, true]);
});
}
@@ -102,6 +102,7 @@ export function runHarnessTests(
ligature_icon
+
`,
})
class IconHarnessTest {}
diff --git a/tools/public_api_guard/material/icon.md b/tools/public_api_guard/material/icon.md
index 2387653cabe6..0ce5ed6fa162 100644
--- a/tools/public_api_guard/material/icon.md
+++ b/tools/public_api_guard/material/icon.md
@@ -71,6 +71,7 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
set fontIcon(value: string);
get fontSet(): string;
set fontSet(value: string);
+ icon: string;
get inline(): boolean;
set inline(inline: BooleanInput);
// (undocumented)
@@ -88,7 +89,7 @@ export class MatIcon extends _MatIconBase implements OnInit, AfterViewChecked, C
// (undocumented)
_usingFontIcon(): boolean;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}