From 61b5f2afca382308bd602de04bd9d8b1fbabf9b1 Mon Sep 17 00:00:00 2001 From: Amy Sorto <8575252+amysorto@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:35:01 +0000 Subject: [PATCH 1/3] feat(material/icon): Change icon font default to Material symbols from Material icons --- guides/getting-started.md | 4 ++-- guides/schematics.md | 11 +++++++---- integration/yarn-pnp-compat/src/index.html | 2 +- src/e2e-app/index.html | 2 +- src/material/icon/icon-registry.ts | 6 +++--- src/material/icon/icon.md | 11 +++++------ src/material/icon/icon.spec.ts | 12 ++++++------ src/material/icon/testing/fake-icon-registry.ts | 2 +- .../schematics/ng-add/fonts/material-fonts.ts | 2 +- src/material/schematics/ng-add/index.spec.ts | 2 +- src/universal-app/index-source.html | 2 +- 11 files changed, 29 insertions(+), 27 deletions(-) diff --git a/guides/getting-started.md b/guides/getting-started.md index 059ff4351e31..45389bf6326c 100644 --- a/guides/getting-started.md +++ b/guides/getting-started.md @@ -35,7 +35,7 @@ The `ng add` command will additionally perform the following actions: * Add project dependencies to `package.json` * Add the Roboto font to your `index.html` -* Add the Material Design icon font to your `index.html` +* Add the Material Symbols icon font to your `index.html` * Add a few global CSS styles to: * Remove margins from `body` * Set `height: 100%` on `html` and `body` @@ -48,7 +48,7 @@ You're done! Angular Material is now configured to be used in your application. Let's display a slide toggle component in your app and verify that everything works. You need to import the `MatSlideToggleModule` that you want to display by adding the following lines to -your standalone component's imports, or otherwise your component's `NgModule`. +your standalone component's imports, or otherwise your component's `NgModule`. ```ts import { MatSlideToggleModule } from '@angular/material/slide-toggle'; diff --git a/guides/schematics.md b/guides/schematics.md index 25d14101e6d9..c5d232075aad 100644 --- a/guides/schematics.md +++ b/guides/schematics.md @@ -22,13 +22,16 @@ In case you just want to install the `@angular/cdk`, there are also schematics f ng add @angular/cdk ``` -The Angular Material `ng add` schematic helps you set up an Angular CLI project that uses Material. Running `ng add` will: +The Angular Material `ng add` schematic helps you set up an Angular CLI project that uses Material. +See the [`getting started guide](./getting-started#install-angular-material) for more information. -- Ensure [project dependencies](./getting-started#step-1-install-angular-material-angular-cdk-and-angular-animations) are placed in `package.json` -- Enable the [BrowserAnimationsModule](./getting-started#step-2-configure-animations) in your app module +Running `ng add` will: + +- Ensure project dependencies are placed in `package.json` +- Enable the BrowserAnimationsModule in your app module - Add either a prebuilt theme or a custom theme - Add Roboto fonts to your `index.html` -- Add the [Material Icon font](./getting-started#step-6-optional-add-material-icons) to your `index.html` +- Add the Material Symbols font to your `index.html` - Add global styles to - Remove margins from `body` - Set `height: 100%` on `html` and `body` diff --git a/integration/yarn-pnp-compat/src/index.html b/integration/yarn-pnp-compat/src/index.html index dc58be14fdd7..65fc76e825e9 100644 --- a/integration/yarn-pnp-compat/src/index.html +++ b/integration/yarn-pnp-compat/src/index.html @@ -8,7 +8,7 @@ - + diff --git a/src/e2e-app/index.html b/src/e2e-app/index.html index 32b525b4f1c0..35e390350bc4 100644 --- a/src/e2e-app/index.html +++ b/src/e2e-app/index.html @@ -7,7 +7,7 @@ - + diff --git a/src/material/icon/icon-registry.ts b/src/material/icon/icon-registry.ts index 004a74060d93..f4f0cc37df94 100644 --- a/src/material/icon/icon-registry.ts +++ b/src/material/icon/icon-registry.ts @@ -145,10 +145,10 @@ export class MatIconRegistry implements OnDestroy { /** * The CSS classes to apply when an `` component has no icon name, url, or font - * specified. The default 'material-icons' value assumes that the material icon font has been - * loaded as described at https://google.github.io/material-design-icons/#icon-font-for-the-web + * specified. The default 'material-symbols-outlined' value assumes that the material icon font + * has been loaded as described at https://google.github.io/material-design-icons/#icon-font-for-the-web */ - private _defaultFontSetClass = ['material-icons', 'mat-ligature-font']; + private _defaultFontSetClass = ['material-symbols-outlined', 'mat-ligature-font']; constructor( @Optional() private _httpClient: HttpClient, diff --git a/src/material/icon/icon.md b/src/material/icon/icon.md index 4063a860a10f..37761136d2a2 100644 --- a/src/material/icon/icon.md +++ b/src/material/icon/icon.md @@ -17,14 +17,13 @@ Some fonts are designed to show icons by using component. By default, `` expects the -[Material icons font](https://google.github.io/material-design-icons/#icon-font-for-the-web). +[Material Symbols](https://developers.google.com/fonts/docs/material_symbols#use_in_web). (You will still need to include the HTML to load the font and its CSS, as described in the link). -You can specify a different font, such as Material's latest icons, -[Material Symbols](https://fonts.google.com/icons), 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`. Alternatively you can set the default for all -your application's icons using `MatIconRegistry.setDefaultFontSetClass`. +You can specify a different font, set the `fontSet` input to either the CSS class to apply to the +desired font, or to an alias previously registered with `MatIconRegistry.registerFontClassAlias`. +Alternatively you can set the default for all your application's icons using +`MatIconRegistry.setDefaultFontSetClass`. ### Font icons with CSS diff --git a/src/material/icon/icon.spec.ts b/src/material/icon/icon.spec.ts index 84f9971285b9..206cd4f232ec 100644 --- a/src/material/icon/icon.spec.ts +++ b/src/material/icon/icon.spec.ts @@ -122,7 +122,7 @@ describe('MatIcon', () => { 'mat-icon', 'mat-ligature-font', 'mat-primary', - 'material-icons', + 'material-symbols-outlined', 'notranslate', ]); }); @@ -141,7 +141,7 @@ describe('MatIcon', () => { 'mat-icon', 'mat-icon-no-color', 'mat-ligature-font', - 'material-icons', + 'material-symbols-outlined', 'notranslate', ]); }); @@ -178,7 +178,7 @@ describe('MatIcon', () => { }); describe('Ligature icons', () => { - it('should add material-icons and mat-ligature-font class by default', () => { + it('should add material-symbols-outlined and mat-ligature-font class by default', () => { const fixture = TestBed.createComponent(IconWithLigature); const testComponent = fixture.componentInstance; @@ -190,7 +190,7 @@ describe('MatIcon', () => { 'mat-icon', 'mat-icon-no-color', 'mat-ligature-font', - 'material-icons', + 'material-symbols-outlined', 'notranslate', ]); }); @@ -265,7 +265,7 @@ describe('MatIcon', () => { expect(icon.getAttribute('fontIcon')).toBe('house'); }); - it('should add material-icons and mat-ligature-font class by default', () => { + it('should add material-symbols-outlined and mat-ligature-font class by default', () => { const fixture = TestBed.createComponent(IconWithLigatureByAttribute); const testComponent = fixture.componentInstance; @@ -277,7 +277,7 @@ describe('MatIcon', () => { 'mat-icon', 'mat-icon-no-color', 'mat-ligature-font', - 'material-icons', + 'material-symbols-outlined', 'notranslate', ]); }); diff --git a/src/material/icon/testing/fake-icon-registry.ts b/src/material/icon/testing/fake-icon-registry.ts index 0045b58c3284..75e7c70c0788 100644 --- a/src/material/icon/testing/fake-icon-registry.ts +++ b/src/material/icon/testing/fake-icon-registry.ts @@ -61,7 +61,7 @@ export class FakeMatIconRegistry implements PublicApi, OnDestro } getDefaultFontSetClass() { - return ['material-icons']; + return ['material-symbols-outlined']; } getSvgIconFromUrl(): Observable { diff --git a/src/material/schematics/ng-add/fonts/material-fonts.ts b/src/material/schematics/ng-add/fonts/material-fonts.ts index 2470c768521e..56c7c988a849 100644 --- a/src/material/schematics/ng-add/fonts/material-fonts.ts +++ b/src/material/schematics/ng-add/fonts/material-fonts.ts @@ -28,7 +28,7 @@ export function addFontsToIndex(options: Schema): Rule { const fonts = [ 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap', - 'https://fonts.googleapis.com/icon?family=Material+Icons', + 'https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined', ]; projectIndexFiles.forEach(indexFilePath => { diff --git a/src/material/schematics/ng-add/index.spec.ts b/src/material/schematics/ng-add/index.spec.ts index 803a9d243604..c1d560a4fca7 100644 --- a/src/material/schematics/ng-add/index.spec.ts +++ b/src/material/schematics/ng-add/index.spec.ts @@ -162,7 +162,7 @@ describe('ng-add schematic', () => { // the created links properly align with the existing HTML. Default CLI projects use an // indentation of two columns. expect(htmlContent).toContain( - ' - + From 0972ab621097870cd52afeafd21b65e9bba34991 Mon Sep 17 00:00:00 2001 From: Amy Sorto <8575252+amysorto@users.noreply.github.com> Date: Mon, 9 Dec 2024 19:32:09 +0000 Subject: [PATCH 2/3] build: set up schematics for v20 --- src/cdk/schematics/migration.json | 8 +- src/cdk/schematics/ng-update/index.ts | 4 +- .../ng-update/migrations/misc-template.ts | 2 +- .../schematics/update-tool/target-version.ts | 2 +- src/material/schematics/migration.json | 8 +- src/material/schematics/ng-update/index.ts | 13 +- .../explicit-system-variable-prefix.ts | 125 ---------- .../ng-update/migrations/mat-core-removal.ts | 101 -------- ...19-explicit-system-variable-prefix.spec.ts | 215 ------------------ .../test-cases/v19-mat-core-removal.spec.ts | 84 ------- 10 files changed, 16 insertions(+), 546 deletions(-) delete mode 100644 src/material/schematics/ng-update/migrations/explicit-system-variable-prefix.ts delete mode 100644 src/material/schematics/ng-update/migrations/mat-core-removal.ts delete mode 100644 src/material/schematics/ng-update/test-cases/v19-explicit-system-variable-prefix.spec.ts delete mode 100644 src/material/schematics/ng-update/test-cases/v19-mat-core-removal.spec.ts diff --git a/src/cdk/schematics/migration.json b/src/cdk/schematics/migration.json index 0bf15e8d6334..b65a92dd0cae 100644 --- a/src/cdk/schematics/migration.json +++ b/src/cdk/schematics/migration.json @@ -1,10 +1,10 @@ { "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { - "migration-v19": { - "version": "19.0.0-0", - "description": "Updates the Angular CDK to v19", - "factory": "./ng-update/index#updateToV19" + "migration-v20": { + "version": "20.0.0-0", + "description": "Updates the Angular CDK to v20", + "factory": "./ng-update/index#updateToV20" }, "ng-post-update": { "description": "Prints out results after ng-update.", diff --git a/src/cdk/schematics/ng-update/index.ts b/src/cdk/schematics/ng-update/index.ts index 046f272d0060..223f12e3bcab 100644 --- a/src/cdk/schematics/ng-update/index.ts +++ b/src/cdk/schematics/ng-update/index.ts @@ -14,9 +14,9 @@ import {createMigrationSchematicRule, NullableDevkitMigration} from './devkit-mi const cdkMigrations: NullableDevkitMigration[] = []; /** Entry point for the migration schematics with target of Angular CDK 18.0.0 */ -export function updateToV19(): Rule { +export function updateToV20(): Rule { return createMigrationSchematicRule( - TargetVersion.V19, + TargetVersion.V20, cdkMigrations, cdkUpgradeData, onMigrationComplete, diff --git a/src/cdk/schematics/ng-update/migrations/misc-template.ts b/src/cdk/schematics/ng-update/migrations/misc-template.ts index 54d2ab086186..25fb7caa02af 100644 --- a/src/cdk/schematics/ng-update/migrations/misc-template.ts +++ b/src/cdk/schematics/ng-update/migrations/misc-template.ts @@ -15,7 +15,7 @@ import {UpgradeData} from '../upgrade-data'; * instances of outdated Angular CDK API that can't be migrated automatically. */ export class MiscTemplateMigration extends Migration { - // There are currently no migrations for V19 deprecations. + // There are currently no migrations for V20 deprecations. enabled = false; override visitTemplate(template: ResolvedResource): void {} diff --git a/src/cdk/schematics/update-tool/target-version.ts b/src/cdk/schematics/update-tool/target-version.ts index 26908bc23800..295939467932 100644 --- a/src/cdk/schematics/update-tool/target-version.ts +++ b/src/cdk/schematics/update-tool/target-version.ts @@ -10,7 +10,7 @@ // tslint:disable-next-line:prefer-const-enum export enum TargetVersion { - V19 = 'version 19', + V20 = 'version 20', } /** diff --git a/src/material/schematics/migration.json b/src/material/schematics/migration.json index 9a667ca12f4e..7675e20d8658 100644 --- a/src/material/schematics/migration.json +++ b/src/material/schematics/migration.json @@ -1,10 +1,10 @@ { "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { - "migration-v19": { - "version": "19.0.0-0", - "description": "Updates Angular Material to v19", - "factory": "./ng-update/index_bundled#updateToV19" + "migration-v20": { + "version": "20.0.0-0", + "description": "Updates Angular Material to v20", + "factory": "./ng-update/index_bundled#updateToV20" } } } diff --git a/src/material/schematics/ng-update/index.ts b/src/material/schematics/ng-update/index.ts index e9f51ee593a6..52ab85b84b7e 100644 --- a/src/material/schematics/ng-update/index.ts +++ b/src/material/schematics/ng-update/index.ts @@ -14,18 +14,13 @@ import { } from '@angular/cdk/schematics'; import {materialUpgradeData} from './upgrade-data'; -import {MatCoreMigration} from './migrations/mat-core-removal'; -import {ExplicitSystemVariablePrefixMigration} from './migrations/explicit-system-variable-prefix'; -const materialMigrations: NullableDevkitMigration[] = [ - MatCoreMigration, - ExplicitSystemVariablePrefixMigration, -]; +const materialMigrations: NullableDevkitMigration[] = []; -/** Entry point for the migration schematics with target of Angular Material v19 */ -export function updateToV19(): Rule { +/** Entry point for the migration schematics with target of Angular Material v20 */ +export function updateToV20(): Rule { return createMigrationSchematicRule( - TargetVersion.V19, + TargetVersion.V20, materialMigrations, materialUpgradeData, onMigrationComplete, diff --git a/src/material/schematics/ng-update/migrations/explicit-system-variable-prefix.ts b/src/material/schematics/ng-update/migrations/explicit-system-variable-prefix.ts deleted file mode 100644 index f49a8962bec1..000000000000 --- a/src/material/schematics/ng-update/migrations/explicit-system-variable-prefix.ts +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DevkitContext, Migration, ResolvedResource, UpgradeData} from '@angular/cdk/schematics'; - -/** - * Migration that adds `system-variables-prefix` to apps that have `use-system-variables` enabled. - */ -export class ExplicitSystemVariablePrefixMigration extends Migration { - override enabled = true; - - override visitStylesheet(stylesheet: ResolvedResource): void { - if (!stylesheet.filePath.endsWith('.scss')) { - return; - } - - const content = this.fileSystem.read(stylesheet.filePath); - if (!content || !content.includes('@angular/material')) { - return; - } - - const changes = this._getChanges(content); - - if (changes.length > 0) { - const update = this.fileSystem.edit(stylesheet.filePath); - - for (let i = changes.length - 1; i > -1; i--) { - update.insertRight(changes[i].start, changes[i].text); - } - - this.fileSystem.commitEdits(); - } - } - - /** Gets the changes that should be applied to a file. */ - private _getChanges(content: string) { - const key = 'use-system-variables'; - const prefixKey = 'system-variables-prefix'; - const changes: {start: number; text: string}[] = []; - let index = content.indexOf(key); - - // Note: this migration is a bit rudimentary, because Sass doesn't expose a proper AST. - while (index > -1) { - const colonIndex = content.indexOf(':', index); - const valueEnd = colonIndex === -1 ? -1 : this._getValueEnd(content, colonIndex); - - if (valueEnd === -1) { - index = content.indexOf(key, index + key.length); - continue; - } - - const value = content.slice(colonIndex + 1, valueEnd + 1).trim(); - if (value.startsWith('true') && !this._hasSystemPrefix(content, index, prefixKey)) { - changes.push({ - start: this._getInsertIndex(content, valueEnd), - text: `${value.endsWith(',') ? '' : ','}\n ${prefixKey}: sys,`, - }); - } - - index = content.indexOf(key, valueEnd); - } - - return changes; - } - - /** - * Gets the end index of a Sass map key. - * @param content Content of the file. - * @param startIndex Index at which to start the search. - */ - private _getValueEnd(content: string, startIndex: number): number { - for (let i = startIndex + 1; i < content.length; i++) { - const char = content[i]; - - if (char === ',' || char === '\n' || char === ')') { - return i; - } - } - - return -1; - } - - /** - * Gets the index at which to insert the migrated content. - * @param content Initial file content. - * @param valueEnd Index at which the value of the system variables opt-in ends. - */ - private _getInsertIndex(content: string, valueEnd: number): number { - for (let i = valueEnd; i < content.length; i++) { - if (content[i] === '\n') { - return i; - } else if (content[i] === ')') { - return i; - } - } - - return valueEnd; - } - - /** - * Determines if a map that enables system variables is using system variables already. - * @param content Full file contents. - * @param keyIndex Index at which the systems variable key is defined. - * @param prefixKey Name of the key that defines the prefix. - */ - private _hasSystemPrefix(content: string, keyIndex: number, prefixKey: string): boolean { - // Note: technically this can break if there are other inline maps, but it should be rare. - const mapEnd = content.indexOf(')', keyIndex); - - if (mapEnd > -1) { - for (let i = keyIndex; i > -1; i--) { - if (content[i] === '(') { - return content.slice(i, mapEnd).includes(prefixKey); - } - } - } - - return false; - } -} diff --git a/src/material/schematics/ng-update/migrations/mat-core-removal.ts b/src/material/schematics/ng-update/migrations/mat-core-removal.ts deleted file mode 100644 index 64c0b84d2808..000000000000 --- a/src/material/schematics/ng-update/migrations/mat-core-removal.ts +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import * as postcss from 'postcss'; -import * as scss from 'postcss-scss'; -import { - DevkitContext, - Migration, - ResolvedResource, - UpgradeData, - WorkspacePath, -} from '@angular/cdk/schematics'; - -const MATERIAL_IMPORT_PATH = '@angular/material'; - -export class MatCoreMigration extends Migration { - override enabled = true; - private _namespace: string | undefined; - - override init() { - // TODO: Check if mat-app-background is used in the application. - } - - override visitStylesheet(stylesheet: ResolvedResource): void { - // Avoid parsing the template Material isn't used. - if (!stylesheet.content.includes(MATERIAL_IMPORT_PATH)) { - return; - } - - try { - const processor = new postcss.Processor([ - { - postcssPlugin: 'mat-core-removal-v19-plugin', - AtRule: { - use: node => this._getNamespace(node), - include: node => this._handleAtInclude(node, stylesheet.filePath), - }, - }, - ]); - processor.process(stylesheet.content, {syntax: scss}).sync(); - } catch (e) { - this.logger.warn( - `Failed to migrate usages of mat.core in ${stylesheet.filePath} due to error:`, - ); - this.logger.warn(e + ''); - } - } - - /** Handles updating the at-include rules of uses of the core mixin. */ - private _handleAtInclude(node: postcss.AtRule, filePath: WorkspacePath): void { - if (!this._namespace || !node.source?.start || !node.source.end) { - return; - } - - if (this._isMatCoreMixin(node)) { - const end = node.source.end.offset; - const start = node.source.start.offset; - - const prefix = '\n' + (node.raws.before?.split('\n').pop() || ''); - const snippet = prefix + node.source.input.css.slice(start, end); - - const elevation = prefix + `@include ${this._namespace}.elevation-classes();`; - const background = prefix + `@include ${this._namespace}.app-background();`; - - this._replaceAt(filePath, node.source.start.offset - prefix.length, { - old: snippet, - new: elevation + background, - }); - } - } - - /** Returns true if the given at-rule is a use of the core mixin. */ - private _isMatCoreMixin(node: postcss.AtRule): boolean { - if (node.params.startsWith(`${this._namespace}.core`)) { - return true; - } - return false; - } - - /** Sets the namespace if the given at-rule if it is importing from @angular/material. */ - private _getNamespace(node: postcss.AtRule): void { - if (!this._namespace && node.params.startsWith(MATERIAL_IMPORT_PATH, 1)) { - this._namespace = node.params.split(/\s+/)[2] || 'material'; - } - } - - /** Updates the source file with the given replacements. */ - private _replaceAt( - filePath: WorkspacePath, - offset: number, - str: {old: string; new: string}, - ): void { - const index = this.fileSystem.read(filePath)!.indexOf(str.old, offset); - this.fileSystem.edit(filePath).remove(index, str.old.length).insertRight(index, str.new); - } -} diff --git a/src/material/schematics/ng-update/test-cases/v19-explicit-system-variable-prefix.spec.ts b/src/material/schematics/ng-update/test-cases/v19-explicit-system-variable-prefix.spec.ts deleted file mode 100644 index 5a7a86afc73f..000000000000 --- a/src/material/schematics/ng-update/test-cases/v19-explicit-system-variable-prefix.spec.ts +++ /dev/null @@ -1,215 +0,0 @@ -import {UnitTestTree} from '@angular-devkit/schematics/testing'; -import {createTestCaseSetup} from '@angular/cdk/schematics/testing'; -import {MIGRATION_PATH} from '../../paths'; - -const THEME_FILE_PATH = '/projects/cdk-testing/src/theme.scss'; - -describe('v19 explicit system variable prefix migration', () => { - let tree: UnitTestTree; - let writeFile: (filename: string, content: string) => void; - let runMigration: () => Promise; - - function stripWhitespace(content: string): string { - return content.replace(/\s/g, ''); - } - - beforeEach(async () => { - const testSetup = await createTestCaseSetup('migration-v19', MIGRATION_PATH, []); - tree = testSetup.appTree; - writeFile = testSetup.writeFile; - runMigration = testSetup.runFixers; - }); - - it('should add an explicit system variables prefix', async () => { - writeFile( - THEME_FILE_PATH, - ` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: ( - theme-type: 'light', - primary: mat.$azure-palette, - tertiary: mat.$red-palette, - use-system-variables: true - ), - typography: ( - use-system-variables: true - ), - density: ( - scale: -1 - ), - )); - - @include mat.all-component-themes($theme); - `, - ); - - await runMigration(); - - expect(stripWhitespace(tree.readText(THEME_FILE_PATH))).toBe( - stripWhitespace(` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: ( - theme-type: 'light', - primary: mat.$azure-palette, - tertiary: mat.$red-palette, - use-system-variables: true, - system-variables-prefix: sys, - ), - typography: ( - use-system-variables: true, - system-variables-prefix: sys, - ), - density: ( - scale: -1 - ), - )); - - @include mat.all-component-themes($theme); - `), - ); - }); - - it('should add an explicit system variables prefix if the value is using trailing commas', async () => { - writeFile( - THEME_FILE_PATH, - ` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: ( - theme-type: 'light', - primary: mat.$azure-palette, - tertiary: mat.$red-palette, - use-system-variables: true, - ), - typography: ( - use-system-variables: true, - ), - density: ( - scale: -1 - ), - )); - - @include mat.all-component-themes($theme); - `, - ); - - await runMigration(); - - expect(stripWhitespace(tree.readText(THEME_FILE_PATH))).toBe( - stripWhitespace(` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: ( - theme-type: 'light', - primary: mat.$azure-palette, - tertiary: mat.$red-palette, - use-system-variables: true, - system-variables-prefix: sys, - ), - typography: ( - use-system-variables: true, - system-variables-prefix: sys, - ), - density: ( - scale: -1 - ), - )); - - @include mat.all-component-themes($theme); - `), - ); - }); - - it('should not add an explicit system variables prefix if the map has one already', async () => { - writeFile( - THEME_FILE_PATH, - ` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: ( - theme-type: 'light', - primary: mat.$azure-palette, - tertiary: mat.$red-palette, - use-system-variables: true - ), - typography: ( - use-system-variables: true, - system-variables-prefix: foo - ), - density: ( - scale: -1 - ), - )); - - @include mat.all-component-themes($theme); - `, - ); - - await runMigration(); - - expect(stripWhitespace(tree.readText(THEME_FILE_PATH))).toBe( - stripWhitespace(` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: ( - theme-type: 'light', - primary: mat.$azure-palette, - tertiary: mat.$red-palette, - use-system-variables: true, - system-variables-prefix: sys, - ), - typography: ( - use-system-variables: true, - system-variables-prefix: foo - ), - density: ( - scale: -1 - ), - )); - - @include mat.all-component-themes($theme); - `), - ); - }); - - it('should handle a single-line map', async () => { - writeFile( - THEME_FILE_PATH, - ` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: (theme-type: 'light', primary: mat.$azure-palette, use-system-variables: true), - typography: (use-system-variables: true), - density: (scale: -1), - )); - - @include mat.all-component-themes($theme); - `, - ); - - await runMigration(); - - expect(stripWhitespace(tree.readText(THEME_FILE_PATH))).toBe( - stripWhitespace(` - @use '@angular/material' as mat; - - $theme: mat.define-theme(( - color: (theme-type: 'light', primary: mat.$azure-palette, use-system-variables: true, system-variables-prefix: sys,), - typography: (use-system-variables: true, system-variables-prefix: sys,), - density: (scale: -1), - )); - - @include mat.all-component-themes($theme); - `), - ); - }); -}); diff --git a/src/material/schematics/ng-update/test-cases/v19-mat-core-removal.spec.ts b/src/material/schematics/ng-update/test-cases/v19-mat-core-removal.spec.ts deleted file mode 100644 index 2a05fac5c73d..000000000000 --- a/src/material/schematics/ng-update/test-cases/v19-mat-core-removal.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {UnitTestTree} from '@angular-devkit/schematics/testing'; -import {createTestCaseSetup} from '@angular/cdk/schematics/testing'; -import {join} from 'path'; -import {MIGRATION_PATH} from '../../paths'; - -const PROJECT_ROOT_DIR = '/projects/cdk-testing'; -const THEME_FILE_PATH = join(PROJECT_ROOT_DIR, 'src/theme.scss'); - -describe('v19 mat.core migration', () => { - let tree: UnitTestTree; - - /** Writes multiple lines to a file. */ - let writeLines: (path: string, lines: string[]) => void; - - /** Reads multiple lines from a file. */ - let readLines: (path: string) => string[]; - - /** Runs the v15 migration on the test application. */ - let runMigration: () => Promise<{logOutput: string}>; - - beforeEach(async () => { - const testSetup = await createTestCaseSetup('migration-v19', MIGRATION_PATH, []); - tree = testSetup.appTree; - runMigration = testSetup.runFixers; - readLines = (path: string) => tree.readContent(path).split('\n'); - writeLines = (path: string, lines: string[]) => testSetup.writeFile(path, lines.join('\n')); - }); - - describe('style migrations', () => { - async function runSassMigrationTest(ctx: string, opts: {old: string[]; new: string[]}) { - writeLines(THEME_FILE_PATH, opts.old); - await runMigration(); - expect(readLines(THEME_FILE_PATH)).withContext(ctx).toEqual(opts.new); - } - - it('should remove uses of the core mixin', async () => { - await runSassMigrationTest('', { - old: [`@use '@angular/material' as mat;`, `@include mat.core();`], - new: [ - `@use '@angular/material' as mat;`, - `@include mat.elevation-classes();`, - `@include mat.app-background();`, - ], - }); - - await runSassMigrationTest('w/ unique namespace', { - old: [`@use '@angular/material' as material;`, `@include material.core();`], - new: [ - `@use '@angular/material' as material;`, - `@include material.elevation-classes();`, - `@include material.app-background();`, - ], - }); - - await runSassMigrationTest('w/ no namespace', { - old: [`@use '@angular/material';`, `@include material.core();`], - new: [ - `@use '@angular/material';`, - `@include material.elevation-classes();`, - `@include material.app-background();`, - ], - }); - - await runSassMigrationTest('w/ unique whitespace', { - old: [ - ` @use '@angular/material' as material ; `, - ` @include material.core( ) ; `, - ], - new: [ - ` @use '@angular/material' as material ; `, - ` @include material.elevation-classes();`, - ` @include material.app-background(); `, - ], - }); - }); - - it('should not break if there is an invalid syntax', async () => { - await runSassMigrationTest('', { - old: [`@use '@angular/material' as mat;`, `.foo { content: '; }`], - new: [`@use '@angular/material' as mat;`, `.foo { content: '; }`], - }); - }); - }); -}); From 54def5aa4ec05bd363f869c3730fc1ab9b6a4c8b Mon Sep 17 00:00:00 2001 From: Amy Sorto <8575252+amysorto@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:21:40 +0000 Subject: [PATCH 3/3] feat(material/schematics): Add Material Symbols icon font schematic --- src/material/schematics/ng-update/index.ts | 61 +++++++++++-- .../v20-mat-symbols-icon-font.spec.ts | 87 +++++++++++++++++++ 2 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts diff --git a/src/material/schematics/ng-update/index.ts b/src/material/schematics/ng-update/index.ts index 52ab85b84b7e..468a25c536f7 100644 --- a/src/material/schematics/ng-update/index.ts +++ b/src/material/schematics/ng-update/index.ts @@ -6,12 +6,17 @@ * found in the LICENSE file at https://angular.dev/license */ -import {Rule, SchematicContext} from '@angular-devkit/schematics'; +import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics'; import { + appendHtmlElementToHead, createMigrationSchematicRule, + getProjectFromWorkspace, + getProjectIndexFiles, + getWorkspaceConfigGracefully, NullableDevkitMigration, TargetVersion, } from '@angular/cdk/schematics'; +import {getWorkspace} from '@schematics/angular/utility/workspace'; import {materialUpgradeData} from './upgrade-data'; @@ -19,12 +24,54 @@ const materialMigrations: NullableDevkitMigration[] = []; /** Entry point for the migration schematics with target of Angular Material v20 */ export function updateToV20(): Rule { - return createMigrationSchematicRule( - TargetVersion.V20, - materialMigrations, - materialUpgradeData, - onMigrationComplete, - ); + return chain([ + createMigrationSchematicRule( + TargetVersion.V20, + materialMigrations, + materialUpgradeData, + onMigrationComplete, + ), + // Updating to the new Material Symbols isn't a migration within materialMigrations since + // the index files are never visited within the migration schematic rule. The + // migrate() function within the update-tool only visits files referenced in + // typescript files which excludes the index template files: + // https://github.com/angular/components/blob/main/src/cdk/schematics/update-tool/index.ts#L71. + updateIconFontToMaterialSymbolsRule(), + ]); +} + +/** + * Finds the index files and adds the import for Material Symbols font if needed. As of v20, + * Material Symbols becomes the default font icon since Material Icons is deprecated. This + * rule ensures the Material Symbols font is imported for existing applications. + * @returns Rule that adds the import for the Material Symbols icon font to the index files + */ +function updateIconFontToMaterialSymbolsRule(): Rule { + return async (tree: Tree, context: SchematicContext) => { + const workspace = await getWorkspaceConfigGracefully(tree); + const projectNames = workspace!.projects.keys(); + + let indexFiles: string[] = []; + for (const projectName of projectNames) { + const project = getProjectFromWorkspace(await getWorkspace(tree), projectName); + indexFiles = [...indexFiles, ...getProjectIndexFiles(project)]; + } + + const materialSymbolsFont = + 'https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined'; + for (const indexFile of indexFiles) { + // Add Material Symbols font if not imported in index file. References to the deprecated + // Material Icons are not removed since some applications may have manual overrides in their + // component styles that still reference it. + if (!tree.read(indexFile)?.includes(materialSymbolsFont)) { + appendHtmlElementToHead( + tree, + indexFile, + ``, + ); + } + } + }; } /** Function that will be called when the migration completed. */ diff --git a/src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts b/src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts new file mode 100644 index 000000000000..4ce196537d38 --- /dev/null +++ b/src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts @@ -0,0 +1,87 @@ +import {UnitTestTree} from '@angular-devkit/schematics/testing'; +import {createTestCaseSetup} from '@angular/cdk/schematics/testing'; +import {MIGRATION_PATH} from '../../paths'; + +const INDEX_HTML_FILE_PATH = '/projects/cdk-testing/src/index.html'; + +describe('v20 material symbols icon font migration', () => { + let tree: UnitTestTree; + let writeFile: (filename: string, content: string) => void; + let runMigration: () => Promise; + + function stripWhitespace(content: string): string { + return content.replace(/\s/g, ''); + } + + beforeEach(async () => { + const testSetup = await createTestCaseSetup('migration-v20', MIGRATION_PATH, []); + tree = testSetup.appTree; + writeFile = testSetup.writeFile; + runMigration = testSetup.runFixers; + }); + + it('should add Material Symbols font to index html file', async () => { + writeFile( + INDEX_HTML_FILE_PATH, + ` + + + + + + + + + `, + ); + + await runMigration(); + + expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe( + stripWhitespace(` + + + + + + + + + + `), + ); + }); + + it('should not add Material Symbols font to index html file if it is already imported', async () => { + writeFile( + INDEX_HTML_FILE_PATH, + ` + + + + + + + + + + `, + ); + + await runMigration(); + + expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe( + stripWhitespace(` + + + + + + + + + + `), + ); + }); +});