Skip to content

Commit 9cdb4db

Browse files
committed
fix(material/schematics): migrate typography hierarchy classes in templates
Adds a migration that will rename usages of the classes coming from the `typography-hierarchy` mixin. (cherry picked from commit 44f3a7a)
1 parent e9d5349 commit 9cdb4db

File tree

5 files changed

+218
-18
lines changed

5 files changed

+218
-18
lines changed

src/material/schematics/ng-generate/mdc-migration/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {RuntimeCodeMigration} from './rules/ts-migration/runtime-migration';
2121
import {Schema} from './schema';
2222
import {TemplateMigration} from './rules/template-migration';
2323
import {ThemingStylesMigration} from './rules/theming-styles';
24+
import {TypographyHierarchyTemplateMigrator} from './rules/components/typography-hierarchy/typography-hierarchy-template';
2425

2526
/** Groups of components that must be migrated together. */
2627
const migrationGroups = [
@@ -44,6 +45,12 @@ const migrationGroups = [
4445
['tooltip'],
4546
];
4647

48+
const TYPOGRAPHY_HIERARCHY_MIGRATOR: ComponentMigrator = {
49+
component: 'typography-hierarchy',
50+
template: new TypographyHierarchyTemplateMigrator(),
51+
styles: null!, // TODO
52+
};
53+
4754
function getComponentsToMigrate(requested: string[]): Set<string> {
4855
const componentsToMigrate = new Set<string>(requested);
4956
if (componentsToMigrate.has('all')) {
@@ -94,7 +101,10 @@ export default function (options: Schema): Rule {
94101
const fileSystem = new DevkitFileSystem(tree);
95102
const analyzedFiles = new Set<WorkspacePath>();
96103
const componentsToMigrate = getComponentsToMigrate(options.components);
97-
const migrators = MIGRATORS.filter(m => componentsToMigrate.has(m.component));
104+
const migrators = MIGRATORS.filter(m => componentsToMigrate.has(m.component)).concat(
105+
// The typography hierarchy should always be migrated.
106+
TYPOGRAPHY_HIERARCHY_MIGRATOR,
107+
);
98108
let success = true;
99109

100110
if (options.directory) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
const mappings: [string, string][] = [
10+
['display-4', 'headline-1'],
11+
['display-3', 'headline-2'],
12+
['display-2', 'headline-3'],
13+
['display-1', 'headline-4'],
14+
['headline', 'headline-5'],
15+
['title', 'headline-6'],
16+
['subheading-2', 'subtitle-1'],
17+
['body-2', 'subtitle-2'],
18+
['subheading-1', 'body-1'],
19+
['body-1', 'body-2'],
20+
];
21+
22+
/**
23+
* Mapping between the renamed legacy typography levels and their new non-legacy names. Based on
24+
* the mappings in `private-typography-to-2018-config` from `core/typography/_typography.scss`.
25+
*/
26+
export const RENAMED_TYPOGRAPHY_LEVELS = new Map(mappings);
27+
28+
/** Mapping between the renamed typography CSS classes and their non-legacy equivalents. */
29+
export const RENAMED_TYPOGRAPHY_CLASSES = new Map(
30+
mappings.map(m => ['mat-' + m[0], 'mat-' + m[1]]),
31+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {createTestApp, patchDevkitTreeToExposeTypeScript} from '@angular/cdk/schematics/testing';
2+
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
3+
import {createNewTestRunner, migrateComponents, TEMPLATE_FILE} from '../test-setup-helper';
4+
5+
describe('typography hierarchy template migrator', () => {
6+
let runner: SchematicTestRunner;
7+
let cliAppTree: UnitTestTree;
8+
9+
async function runMigrationTest(oldFileContent: string, newFileContent: string) {
10+
cliAppTree.overwrite(TEMPLATE_FILE, oldFileContent);
11+
const tree = await migrateComponents([], runner, cliAppTree);
12+
expect(tree.readContent(TEMPLATE_FILE)).toBe(newFileContent);
13+
}
14+
15+
beforeEach(async () => {
16+
runner = createNewTestRunner();
17+
cliAppTree = patchDevkitTreeToExposeTypeScript(await createTestApp(runner));
18+
});
19+
20+
it('should migrate legacy typography levels in static attributes', async () => {
21+
await runMigrationTest(
22+
`
23+
<h1 class="mat-display-4">Hello</h1>
24+
<h1 class="mat-display-3">Hello</h1>
25+
<h1 class="mat-display-2">Hello</h1>
26+
<h1 class="mat-display-1">Hello</h1>
27+
<h1 class="mat-headline">Hello</h1>
28+
<h1 class="mat-title">Hello</h1>
29+
<h1 class="mat-subheading-2">Hello</h1>
30+
<h1 class="mat-body-2">Hello</h1>
31+
<h1 class="mat-subheading-1">Hello</h1>
32+
<h1 class="mat-body-1">Hello</h1>
33+
`,
34+
`
35+
<h1 class="mat-headline-1">Hello</h1>
36+
<h1 class="mat-headline-2">Hello</h1>
37+
<h1 class="mat-headline-3">Hello</h1>
38+
<h1 class="mat-headline-4">Hello</h1>
39+
<h1 class="mat-headline-5">Hello</h1>
40+
<h1 class="mat-headline-6">Hello</h1>
41+
<h1 class="mat-subtitle-1">Hello</h1>
42+
<h1 class="mat-subtitle-2">Hello</h1>
43+
<h1 class="mat-body-1">Hello</h1>
44+
<h1 class="mat-body-2">Hello</h1>
45+
`,
46+
);
47+
});
48+
49+
it('should migrate multiple static class usages of the legacy typography levels in a single file', async () => {
50+
await runMigrationTest(
51+
`
52+
<h1 class="header mat-display-4" other-attr="foo">Hello</h1><div foo="bar"></div>
53+
<h2 class="mat-display-3">Hi</h2>
54+
`,
55+
`
56+
<h1 class="header mat-headline-1" other-attr="foo">Hello</h1><div foo="bar"></div>
57+
<h2 class="mat-headline-2">Hi</h2>
58+
`,
59+
);
60+
});
61+
62+
it('should migrate legacy typography levels in class bindings', async () => {
63+
await runMigrationTest(
64+
`
65+
<h1 [class.mat-display-4]="expr">Hello</h1>
66+
<h1 [class.mat-display-3]="expr">Hello</h1>
67+
<h1 [class.mat-display-2]="expr">Hello</h1>
68+
<h1 [class.mat-display-1]="expr">Hello</h1>
69+
<h1 [class.mat-headline]="expr">Hello</h1>
70+
<h1 [class.mat-title]="expr">Hello</h1>
71+
<h1 [class.mat-subheading-2]="expr">Hello</h1>
72+
<h1 [class.mat-body-2]="expr">Hello</h1>
73+
<h1 [class.mat-subheading-1]="expr">Hello</h1>
74+
<h1 [class.mat-body-1]="expr">Hello</h1>
75+
`,
76+
`
77+
<h1 [class.mat-headline-1]="expr">Hello</h1>
78+
<h1 [class.mat-headline-2]="expr">Hello</h1>
79+
<h1 [class.mat-headline-3]="expr">Hello</h1>
80+
<h1 [class.mat-headline-4]="expr">Hello</h1>
81+
<h1 [class.mat-headline-5]="expr">Hello</h1>
82+
<h1 [class.mat-headline-6]="expr">Hello</h1>
83+
<h1 [class.mat-subtitle-1]="expr">Hello</h1>
84+
<h1 [class.mat-subtitle-2]="expr">Hello</h1>
85+
<h1 [class.mat-body-1]="expr">Hello</h1>
86+
<h1 [class.mat-body-2]="expr">Hello</h1>
87+
`,
88+
);
89+
});
90+
91+
it('should migrate mixed class bindings and static class attribute', async () => {
92+
await runMigrationTest(
93+
`
94+
<h1 [class.mat-display-4]="someExpr" class="foo mat-subheading-2 bar">Hello</h1>
95+
<h1 [class.mat-display-1]="someExpr">Hello</h1>
96+
`,
97+
`
98+
<h1 [class.mat-headline-1]="someExpr" class="foo mat-subtitle-1 bar">Hello</h1>
99+
<h1 [class.mat-headline-4]="someExpr">Hello</h1>
100+
`,
101+
);
102+
});
103+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import * as compiler from '@angular/compiler';
10+
import {TemplateMigrator} from '../../template-migrator';
11+
import {visitElements} from '../../tree-traversal';
12+
import {Update} from '../../../../../migration-utilities';
13+
import {RENAMED_TYPOGRAPHY_CLASSES} from './constants';
14+
15+
export class TypographyHierarchyTemplateMigrator extends TemplateMigrator {
16+
getUpdates(ast: compiler.ParsedTemplate): Update[] {
17+
const updates: Update[] = [];
18+
19+
visitElements(ast.nodes, node => {
20+
this._addStaticClassUpdates(node, updates);
21+
this._addClassBindingUpdates(node, updates);
22+
});
23+
24+
return updates;
25+
}
26+
27+
/** Migrates the legacy typography classes in a static `class` attribute. */
28+
private _addStaticClassUpdates(node: compiler.TmplAstElement, updates: Update[]): void {
29+
const classAttr = node.attributes.find(attr => attr.name === 'class');
30+
31+
if (classAttr && classAttr.keySpan && classAttr.valueSpan && classAttr.value.includes('mat-')) {
32+
const classes = classAttr.value.split(' ');
33+
let hasChanged = false;
34+
35+
classes.forEach((current, index) => {
36+
if (RENAMED_TYPOGRAPHY_CLASSES.has(current)) {
37+
hasChanged = true;
38+
classes[index] = RENAMED_TYPOGRAPHY_CLASSES.get(current)!;
39+
}
40+
});
41+
42+
if (hasChanged) {
43+
updates.push({
44+
offset: classAttr.keySpan.start.offset,
45+
updateFn: html =>
46+
html.slice(0, classAttr.valueSpan!.start.offset) +
47+
classes.join(' ') +
48+
html.slice(classAttr.valueSpan!.end.offset),
49+
});
50+
}
51+
}
52+
}
53+
54+
/** Migrates the legacy typography classes in `[class.x]` bindings. */
55+
private _addClassBindingUpdates(node: compiler.TmplAstElement, updates: Update[]): void {
56+
node.inputs.forEach(input => {
57+
if (input.type === compiler.BindingType.Class && RENAMED_TYPOGRAPHY_CLASSES.has(input.name)) {
58+
updates.push({
59+
offset: input.keySpan.start.offset,
60+
updateFn: html => {
61+
return (
62+
html.slice(0, input.keySpan.start.offset) +
63+
'class.' +
64+
RENAMED_TYPOGRAPHY_CLASSES.get(input.name)! +
65+
html.slice(input.keySpan.end.offset)
66+
);
67+
},
68+
});
69+
}
70+
});
71+
}
72+
}

src/material/schematics/ng-generate/mdc-migration/rules/theming-styles.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,10 @@ import {SchematicContext} from '@angular-devkit/schematics';
1111
import * as postcss from 'postcss';
1212
import * as scss from 'postcss-scss';
1313
import {ComponentMigrator, MIGRATORS} from '.';
14+
import {RENAMED_TYPOGRAPHY_LEVELS} from './components/typography-hierarchy/constants';
1415

1516
const COMPONENTS_MIXIN_NAME = /\.([^(;]*)/;
1617

17-
/**
18-
* Mapping between the renamed legacy typography levels and their new non-legacy names. Based on
19-
* the mappings in `private-typography-to-2018-config` from `core/typography/_typography.scss`.
20-
*/
21-
const RENAMED_TYPOGRAPHY_LEVELS = new Map([
22-
['display-4', 'headline-1'],
23-
['display-3', 'headline-2'],
24-
['display-2', 'headline-3'],
25-
['display-1', 'headline-4'],
26-
['headline', 'headline-5'],
27-
['title', 'headline-6'],
28-
['subheading-2', 'subtitle-1'],
29-
['body-2', 'subtitle-2'],
30-
['subheading-1', 'body-1'],
31-
['body-1', 'body-2'],
32-
]);
33-
3418
export class ThemingStylesMigration extends Migration<ComponentMigrator[], SchematicContext> {
3519
enabled = true;
3620
private _namespace: string;

0 commit comments

Comments
 (0)