Skip to content

Commit 54def5a

Browse files
committed
feat(material/schematics): Add Material Symbols icon font schematic
1 parent 0972ab6 commit 54def5a

File tree

2 files changed

+141
-7
lines changed

2 files changed

+141
-7
lines changed

src/material/schematics/ng-update/index.ts

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,72 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {Rule, SchematicContext} from '@angular-devkit/schematics';
9+
import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
1010
import {
11+
appendHtmlElementToHead,
1112
createMigrationSchematicRule,
13+
getProjectFromWorkspace,
14+
getProjectIndexFiles,
15+
getWorkspaceConfigGracefully,
1216
NullableDevkitMigration,
1317
TargetVersion,
1418
} from '@angular/cdk/schematics';
19+
import {getWorkspace} from '@schematics/angular/utility/workspace';
1520

1621
import {materialUpgradeData} from './upgrade-data';
1722

1823
const materialMigrations: NullableDevkitMigration[] = [];
1924

2025
/** Entry point for the migration schematics with target of Angular Material v20 */
2126
export function updateToV20(): Rule {
22-
return createMigrationSchematicRule(
23-
TargetVersion.V20,
24-
materialMigrations,
25-
materialUpgradeData,
26-
onMigrationComplete,
27-
);
27+
return chain([
28+
createMigrationSchematicRule(
29+
TargetVersion.V20,
30+
materialMigrations,
31+
materialUpgradeData,
32+
onMigrationComplete,
33+
),
34+
// Updating to the new Material Symbols isn't a migration within materialMigrations since
35+
// the index files are never visited within the migration schematic rule. The
36+
// migrate() function within the update-tool only visits files referenced in
37+
// typescript files which excludes the index template files:
38+
// https://github.com/angular/components/blob/main/src/cdk/schematics/update-tool/index.ts#L71.
39+
updateIconFontToMaterialSymbolsRule(),
40+
]);
41+
}
42+
43+
/**
44+
* Finds the index files and adds the import for Material Symbols font if needed. As of v20,
45+
* Material Symbols becomes the default font icon since Material Icons is deprecated. This
46+
* rule ensures the Material Symbols font is imported for existing applications.
47+
* @returns Rule that adds the import for the Material Symbols icon font to the index files
48+
*/
49+
function updateIconFontToMaterialSymbolsRule(): Rule {
50+
return async (tree: Tree, context: SchematicContext) => {
51+
const workspace = await getWorkspaceConfigGracefully(tree);
52+
const projectNames = workspace!.projects.keys();
53+
54+
let indexFiles: string[] = [];
55+
for (const projectName of projectNames) {
56+
const project = getProjectFromWorkspace(await getWorkspace(tree), projectName);
57+
indexFiles = [...indexFiles, ...getProjectIndexFiles(project)];
58+
}
59+
60+
const materialSymbolsFont =
61+
'https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined';
62+
for (const indexFile of indexFiles) {
63+
// Add Material Symbols font if not imported in index file. References to the deprecated
64+
// Material Icons are not removed since some applications may have manual overrides in their
65+
// component styles that still reference it.
66+
if (!tree.read(indexFile)?.includes(materialSymbolsFont)) {
67+
appendHtmlElementToHead(
68+
tree,
69+
indexFile,
70+
`<link href="${materialSymbolsFont}" rel="stylesheet">`,
71+
);
72+
}
73+
}
74+
};
2875
}
2976

3077
/** Function that will be called when the migration completed. */
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {UnitTestTree} from '@angular-devkit/schematics/testing';
2+
import {createTestCaseSetup} from '@angular/cdk/schematics/testing';
3+
import {MIGRATION_PATH} from '../../paths';
4+
5+
const INDEX_HTML_FILE_PATH = '/projects/cdk-testing/src/index.html';
6+
7+
describe('v20 material symbols icon font migration', () => {
8+
let tree: UnitTestTree;
9+
let writeFile: (filename: string, content: string) => void;
10+
let runMigration: () => Promise<unknown>;
11+
12+
function stripWhitespace(content: string): string {
13+
return content.replace(/\s/g, '');
14+
}
15+
16+
beforeEach(async () => {
17+
const testSetup = await createTestCaseSetup('migration-v20', MIGRATION_PATH, []);
18+
tree = testSetup.appTree;
19+
writeFile = testSetup.writeFile;
20+
runMigration = testSetup.runFixers;
21+
});
22+
23+
it('should add Material Symbols font to index html file', async () => {
24+
writeFile(
25+
INDEX_HTML_FILE_PATH,
26+
`
27+
<!doctype html>
28+
<html lang="en">
29+
<head>
30+
<link rel="preconnect" href="https://fonts.gstatic.com">
31+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
32+
</head>
33+
<body></body>
34+
</html>
35+
`,
36+
);
37+
38+
await runMigration();
39+
40+
expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe(
41+
stripWhitespace(`
42+
<!doctype html>
43+
<html lang="en">
44+
<head>
45+
<link rel="preconnect" href="https://fonts.gstatic.com">
46+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
47+
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
48+
</head>
49+
<body></body>
50+
</html>
51+
`),
52+
);
53+
});
54+
55+
it('should not add Material Symbols font to index html file if it is already imported', async () => {
56+
writeFile(
57+
INDEX_HTML_FILE_PATH,
58+
`
59+
<!doctype html>
60+
<html lang="en">
61+
<head>
62+
<link rel="preconnect" href="https://fonts.gstatic.com">
63+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
64+
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
65+
</head>
66+
<body></body>
67+
</html>
68+
`,
69+
);
70+
71+
await runMigration();
72+
73+
expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe(
74+
stripWhitespace(`
75+
<!doctype html>
76+
<html lang="en">
77+
<head>
78+
<link rel="preconnect" href="https://fonts.gstatic.com">
79+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
80+
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
81+
</head>
82+
<body></body>
83+
</html>
84+
`),
85+
);
86+
});
87+
});

0 commit comments

Comments
 (0)