From 07992e3330b6e98447d56fdf6e7eb2f5b9049f6c Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Thu, 19 Jul 2018 12:09:39 +0200 Subject: [PATCH 1/2] update(ripple): remove deprecated speed factor option. * Removes the deprecated `[matRippleSpeedFactor]` and global `baseSpeedFactor` option. * Adds update rules that can automatically switch to the new API (as much as possible) and even calculate the new durations based on the previously specified speed factor. BREAKING CHANGE: deprecated `[matRippleSpeedFactor]` and `baseSpeedFactor` for the ripples have been removed. Use the new animation config instead. --- src/lib/chips/chip.ts | 3 +- src/lib/core/ripple/ripple-renderer.ts | 7 +- src/lib/core/ripple/ripple.spec.ts | 56 ++----- src/lib/core/ripple/ripple.ts | 19 --- .../misc-ripples-v7/ripple-speed-factor.ts | 26 +++ .../switchRippleSpeedFactorRule.ts | 149 ++++++++++++++++++ .../switchRippleSpeedFactorTemplateRule.ts | 90 +++++++++++ src/lib/tabs/tab-nav-bar/tab-nav-bar.ts | 3 +- 8 files changed, 278 insertions(+), 75 deletions(-) create mode 100644 src/lib/schematics/update/rules/misc-ripples-v7/ripple-speed-factor.ts create mode 100644 src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorRule.ts create mode 100644 src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorTemplateRule.ts diff --git a/src/lib/chips/chip.ts b/src/lib/chips/chip.ts index c39a39c3e10e..99f9d0e04fea 100644 --- a/src/lib/chips/chip.ts +++ b/src/lib/chips/chip.ts @@ -234,10 +234,9 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes this._chipRipple.setupTriggerEvents(_elementRef.nativeElement); if (globalOptions) { + // TODO(paul): Do not copy each option manually. Allow dynamic global option changes: #9729 this._ripplesGloballyDisabled = !!globalOptions.disabled; - // TODO(paul): Once the speedFactor is removed, we no longer need to copy each single option. this.rippleConfig = { - speedFactor: globalOptions.baseSpeedFactor, animation: globalOptions.animation, terminateOnPointerUp: globalOptions.terminateOnPointerUp, }; diff --git a/src/lib/core/ripple/ripple-renderer.ts b/src/lib/core/ripple/ripple-renderer.ts index fcbc66846043..a5f82245cf68 100644 --- a/src/lib/core/ripple/ripple-renderer.ts +++ b/src/lib/core/ripple/ripple-renderer.ts @@ -17,11 +17,6 @@ export type RippleConfig = { persistent?: boolean; animation?: RippleAnimationConfig; terminateOnPointerUp?: boolean; - /** - * @deprecated Use the `animation` property instead. - * @breaking-change 7.0.0 - */ - speedFactor?: number; }; /** @@ -140,7 +135,7 @@ export class RippleRenderer { const radius = config.radius || distanceToFurthestCorner(x, y, containerRect); const offsetX = x - containerRect.left; const offsetY = y - containerRect.top; - const duration = animationConfig.enterDuration / (config.speedFactor || 1); + const duration = animationConfig.enterDuration; const ripple = document.createElement('div'); ripple.classList.add('mat-ripple-element'); diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts index 85bc3fb884e5..04a661395b2b 100644 --- a/src/lib/core/ripple/ripple.spec.ts +++ b/src/lib/core/ripple/ripple.spec.ts @@ -62,7 +62,7 @@ describe('MatRipple', () => { fixture = TestBed.createComponent(BasicRippleContainer); fixture.detectChanges(); - rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]'); + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); rippleDirective = fixture.componentInstance.ripple; }); @@ -262,7 +262,7 @@ describe('MatRipple', () => { fixture = TestBed.createComponent(RippleContainerWithNgIf); fixture.detectChanges(); - rippleTarget = fixture.debugElement.nativeElement.querySelector('[mat-ripple]'); + rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple'); fixture.componentInstance.isDestroyed = true; fixture.detectChanges(); @@ -377,7 +377,7 @@ describe('MatRipple', () => { fixture = TestBed.createComponent(BasicRippleContainer); fixture.detectChanges(); - rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]'); + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); rippleDirective = fixture.componentInstance.ripple; }); @@ -492,7 +492,7 @@ describe('MatRipple', () => { fixture = TestBed.createComponent(testComponent); fixture.detectChanges(); - rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]'); + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); rippleDirective = fixture.componentInstance.ripple; } @@ -534,41 +534,6 @@ describe('MatRipple', () => { expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); }); - it('should support changing the baseSpeedFactor', fakeAsync(() => { - createTestComponent({ baseSpeedFactor: 0.5 }); - - dispatchMouseEvent(rippleTarget, 'mousedown'); - dispatchMouseEvent(rippleTarget, 'mouseup'); - - expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); - - // Calculates the speedFactor for the duration. Those factors needs to be inverted, because - // a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration. - let fadeInFactor = 1 / 0.5; - - // Calculates the duration for fading-in and fading-out the ripple. - tick(enterDuration * fadeInFactor + exitDuration); - - expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); - })); - - it('should combine individual speed factor with baseSpeedFactor', fakeAsync(() => { - createTestComponent({ baseSpeedFactor: 0.5 }); - - rippleDirective.launch(0, 0, { speedFactor: 1.5 }); - - expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); - - // Calculates the speedFactor for the duration. Those factors needs to be inverted, because - // a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration. - let fadeInFactor = 1 / (0.5 * 1.5); - - // Calculates the duration for fading-in and fading-out the ripple. - tick(enterDuration * fadeInFactor + exitDuration); - - expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); - })); - it('should support changing the animation duration', fakeAsync(() => { createTestComponent({ animation: {enterDuration: 100, exitDuration: 100} @@ -619,7 +584,7 @@ describe('MatRipple', () => { fixture = TestBed.createComponent(BasicRippleContainer); fixture.detectChanges(); - rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]'); + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); rippleDirective = fixture.componentInstance.ripple; }); @@ -637,7 +602,7 @@ describe('MatRipple', () => { fixture.detectChanges(); controller = fixture.debugElement.componentInstance; - rippleTarget = fixture.debugElement.nativeElement.querySelector('[mat-ripple]'); + rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple'); }); it('sets ripple color', () => { @@ -757,7 +722,7 @@ describe('MatRipple', () => { @Component({ template: ` -
`, @@ -769,8 +734,7 @@ class BasicRippleContainer { @Component({ template: `
`, + template: `
`, }) class RippleContainerWithoutBindings {} -@Component({ template: `
` }) class RippleContainerWithNgIf { @ViewChild(MatRipple) ripple: MatRipple; diff --git a/src/lib/core/ripple/ripple.ts b/src/lib/core/ripple/ripple.ts index 526529d70a6b..2dd3053d5a18 100644 --- a/src/lib/core/ripple/ripple.ts +++ b/src/lib/core/ripple/ripple.ts @@ -37,15 +37,6 @@ export interface RippleGlobalOptions { */ animation?: RippleAnimationConfig; - /** - * If set, the default duration of the fade-in animation is divided by this value. For example, - * setting it to 0.5 will cause the ripple fade-in animation to take twice as long. - * A changed speedFactor will not affect the fade-out duration of the ripples. - * @deprecated Use the `animation` global option instead. - * @breaking-change 7.0.0 - */ - baseSpeedFactor?: number; - /** * Whether ripples should start fading out immediately after the mouse our touch is released. By * default, ripples will wait for the enter animation to complete and for mouse or touch release. @@ -86,15 +77,6 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget { */ @Input('matRippleRadius') radius: number = 0; - /** - * If set, the normal duration of ripple animations is divided by this value. For example, - * setting it to 0.5 will cause the animations to take twice as long. - * A changed speedFactor will not modify the fade-out duration of the ripples. - * @deprecated Use the [matRippleAnimation] binding instead. - * @breaking-change 7.0.0 - */ - @Input('matRippleSpeedFactor') speedFactor: number = 1; - /** * Configuration for the ripple animation. Allows modifying the enter and exit animation * duration of the ripples. The animation durations will be overwritten if the @@ -174,7 +156,6 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget { color: this.color, animation: {...this._globalOptions.animation, ...this.animation}, terminateOnPointerUp: this._globalOptions.terminateOnPointerUp, - speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1), }; } diff --git a/src/lib/schematics/update/rules/misc-ripples-v7/ripple-speed-factor.ts b/src/lib/schematics/update/rules/misc-ripples-v7/ripple-speed-factor.ts new file mode 100644 index 000000000000..e72cf48c1671 --- /dev/null +++ b/src/lib/schematics/update/rules/misc-ripples-v7/ripple-speed-factor.ts @@ -0,0 +1,26 @@ +/** + * @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.io/license + */ + +/** Converts the specified speed factor into the exact static enter duration. */ +export function convertSpeedFactorToDuration(factor: number) { + // Based on the numeric speed factor value that only affected the `enterDuration` we can + // now calculate the exact `enterDuration`. 450ms is the enter duration without factor. + return 450 / (factor || 1); +} + +/** + * Creates a runtime TypeScript expression that can be used in order to calculate the duration + * from the speed factor expression that couldn't be statically analyzed. + * + * @param speedFactorValue Speed factor expression that couldn't be statically analyzed. + */ +export function createSpeedFactorConvertExpression(speedFactorValue: string): string { + // To be sure that the speed factor value expression is calculated properly, we need to add + // the according parenthesis. + return `450 / (${speedFactorValue})`; +} diff --git a/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorRule.ts b/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorRule.ts new file mode 100644 index 000000000000..b920d9746e82 --- /dev/null +++ b/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorRule.ts @@ -0,0 +1,149 @@ +/** + * @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.io/license + */ + +import {bold, red} from 'chalk'; +import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint'; +import * as ts from 'typescript'; +import { + convertSpeedFactorToDuration, + createSpeedFactorConvertExpression, +} from './ripple-speed-factor'; + +/** + * Note that will be added whenever a speed factor expression has been converted to calculate + * the according duration. This note should encourage people to clean up their code by switching + * away from the speed factors to explicit durations. + */ +const removeNote = `TODO: Cleanup duration calculation.`; + +/** + * Rule that walks through every property assignment and switches the global `baseSpeedFactor` + * ripple option to the new global animation config. Also updates every class member assignment + * that refers to MatRipple#speedFactor. + */ +export class Rule extends Rules.TypedRule { + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] { + return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program)); + } +} + +export class Walker extends ProgramAwareRuleWalker { + + /** Switches binary expressions (e.g. myRipple.speedFactor = 0.5) to the new animation config. */ + visitBinaryExpression(expression: ts.BinaryExpression) { + if (!ts.isPropertyAccessExpression(expression.left)) { + return; + } + + // Left side expression consists of target object and property name (e.g. myInstance.val) + const leftExpression = expression.left as ts.PropertyAccessExpression; + const targetTypeNode = this.getTypeChecker().getTypeAtLocation(leftExpression.expression); + + if (!targetTypeNode.symbol) { + return; + } + + const targetTypeName = targetTypeNode.symbol.getName(); + const propertyName = leftExpression.name.getText(); + + if (targetTypeName === 'MatRipple' && propertyName === 'speedFactor') { + if (ts.isNumericLiteral(expression.right)) { + const numericValue = parseFloat(expression.right.text); + const newEnterDurationValue = convertSpeedFactorToDuration(numericValue); + + // Replace the `speedFactor` property name with `animation`. + const propertyNameReplacement = this.createReplacement(leftExpression.name.getStart(), + leftExpression.name.getWidth(), 'animation'); + + // Replace the value assignment with the new animation config. + const rightExpressionReplacement = this.createReplacement(expression.right.getStart(), + expression.right.getWidth(), `{enterDuration: ${newEnterDurationValue}}`); + + this.addFailureAtNode(expression, + `Found deprecated variable assignment for "${bold('MatRipple')}#${red('speedFactor')}"`, + [propertyNameReplacement, rightExpressionReplacement]); + } else { + // Handle the right expression differently if the previous speed factor value can't + // be resolved statically. In that case, we just create a TypeScript expression that + // calculates the explicit duration based on the non-static speed factor expression. + const newExpression = createSpeedFactorConvertExpression(expression.right.getText()); + + // Replace the `speedFactor` property name with `animation`. + const propertyNameReplacement = this.createReplacement(leftExpression.name.getStart(), + leftExpression.name.getWidth(), 'animation'); + + // Replace the value assignment with the new animation config and remove TODO. + const rightExpressionReplacement = this.createReplacement(expression.right.getStart(), + expression.right.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`); + + this.addFailureAtNode(expression, + `Found deprecated variable assignment for "${bold('MatRipple')}#${red('speedFactor')}"`, + [propertyNameReplacement, rightExpressionReplacement]); + } + } + } + + /** + * Switches a potential global option `baseSpeedFactor` to the new animation config. For this + * we assume that the `baseSpeedFactor` is not used in combination with individual speed factors. + */ + visitPropertyAssignment(assignment: ts.PropertyAssignment) { + // For switching the `baseSpeedFactor` global option we expect the property assignment + // to be inside of a normal object literal. Custom ripple global options cannot be switched + // automatically. + if (!ts.isObjectLiteralExpression(assignment.parent)) { + return; + } + + // The assignment consists of a name (key) and initializer (value). + if (assignment.name.getText() !== 'baseSpeedFactor') { + return; + } + + // We could technically lazily check for the MAT_RIPPLE_GLOBAL_OPTIONS injection token to + // be present, but it's not right to assume that everyone sets the ripple global options + // immediately in the provider object (e.g. it can happen that someone just imports the + // config from a separate file). + + const {initializer, name} = assignment; + + if (ts.isNumericLiteral(initializer)) { + const numericValue = parseFloat(initializer.text); + const newEnterDurationValue = convertSpeedFactorToDuration(numericValue); + + const keyNameReplacement = this.createReplacement(name.getStart(), + assignment.name.getWidth(), `animation`); + + const initializerReplacement = this.createReplacement(initializer.getStart(), + initializer.getWidth(), `{enterDuration: ${newEnterDurationValue}}`); + + this.addFailureAtNode(assignment, + `Found deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')}:` + + `${red('baseSpeedFactor')}"`, + [keyNameReplacement, initializerReplacement]); + } else { + // Handle the right expression differently if the previous speed factor value can't + // be resolved statically. In that case, we just create a TypeScript expression that + // calculates the explicit duration based on the non-static speed factor expression. + const newExpression = createSpeedFactorConvertExpression(initializer.getText()); + + // Replace the `baseSpeedFactor` property name with `animation`. + const propertyNameReplacement = this.createReplacement(name.getStart(), + name.getWidth(), 'animation'); + + // Replace the value assignment with the new animation config and remove TODO. + const rightExpressionReplacement = this.createReplacement(initializer.getStart(), + initializer.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`); + + this.addFailureAtNode(assignment, + `Found a deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')}:` + + `${red('baseSpeedFactor')}.`, + [propertyNameReplacement, rightExpressionReplacement]); + } + } +} diff --git a/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorTemplateRule.ts b/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorTemplateRule.ts new file mode 100644 index 000000000000..84a3fa0ddca6 --- /dev/null +++ b/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorTemplateRule.ts @@ -0,0 +1,90 @@ +/** + * @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.io/license + */ + +import {Replacement, RuleFailure, Rules} from 'tslint'; +import * as ts from 'typescript'; +import {ExternalResource} from '../../tslint/component-file'; +import {ComponentWalker} from '../../tslint/component-walker'; +import { + addFailureAtReplacement, + createExternalReplacementFailure, +} from '../../tslint/rule-failures'; +import { + convertSpeedFactorToDuration, + createSpeedFactorConvertExpression, +} from './ripple-speed-factor'; + +/** Regular expression that matches [matRippleSpeedFactor]="$NUMBER" in templates. */ +const speedFactorNumberRegex = /\[matRippleSpeedFactor]="(\d+(?:\.\d+)?)"/g; + +/** Regular expression that matches [matRippleSpeedFactor]="$NOT_A_NUMBER" in templates. */ +const speedFactorNotParseable = /\[matRippleSpeedFactor]="(?!\d+(?:\.\d+)?")(.*)"/g; + +/** Failure message that will be shown if a speed factor is set to a readable number. */ +const failureMessageReadableNumber = + 'Detected deprecated [matRippleSpeedFactor] input binding with readable number.'; + +/** Failure message that will be shown if a speed factor is set to a non-parseable value. */ +const failureMessageNonParseableValue = + 'Detected deprecated [matRippleSpeedFactor] input binding with non-parseable value.'; + +/** + * Rule that walks through every inline or external template and updates the deprecated + * [matRippleSpeedFactor] to [matRippleAnimation]. + */ +export class Rule extends Rules.AbstractRule { + apply(sourceFile: ts.SourceFile): RuleFailure[] { + return this.applyWithWalker(new Walker(sourceFile, this.getOptions())); + } +} + +export class Walker extends ComponentWalker { + + visitInlineTemplate(template: ts.StringLiteralLike) { + this._createReplacementsForContent(template, template.getText()) + .forEach(data => addFailureAtReplacement(this, data.failureMessage, data.replacement)); + } + + visitExternalTemplate(template: ExternalResource) { + this._createReplacementsForContent(template, template.getFullText()) + .map(data => createExternalReplacementFailure(template, data.failureMessage, + this.getRuleName(), data.replacement)) + .forEach(failure => this.addFailure(failure)); + } + + private _createReplacementsForContent(node: ts.Node, templateText: string) { + const replacements: {failureMessage: string, replacement: Replacement}[] = []; + const startPos = node.getStart(); + + let match: RegExpMatchArray | null; + + while ((match = speedFactorNumberRegex.exec(templateText)) !== null) { + const newEnterDuration = convertSpeedFactorToDuration(parseFloat(match[1])); + const fix = this.createReplacement(startPos + match.index!, match[0].length, + `[matRippleAnimation]="{enterDuration: ${newEnterDuration}}"`); + + replacements.push({ + replacement: fix, + failureMessage: failureMessageReadableNumber, + }); + } + + while ((match = speedFactorNotParseable.exec(templateText)) !== null) { + const newDurationExpression = createSpeedFactorConvertExpression(match[1]); + const fix = this.createReplacement(startPos + match.index!, match[0].length, + `[matRippleAnimation]="{enterDuration: (${newDurationExpression})}"`); + + replacements.push({ + replacement: fix, + failureMessage: failureMessageNonParseableValue, + }); + } + + return replacements; + } +} diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts index 449b318248b9..c35a0ac6c118 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts @@ -239,11 +239,10 @@ export class MatTabLink extends _MatTabLinkMixinBase this.tabIndex = parseInt(tabIndex) || 0; if (globalOptions) { + // TODO(paul): Do not copy each option manually. Allow dynamic global option changes: #9729 this._ripplesGloballyDisabled = !!globalOptions.disabled; - // TODO(paul): Once the speedFactor is removed, we no longer need to copy each single option. this.rippleConfig = { terminateOnPointerUp: globalOptions.terminateOnPointerUp, - speedFactor: globalOptions.baseSpeedFactor, animation: globalOptions.animation, }; } From 1d8e392d3faa023ca336f7756d246c26f6efa6ff Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 19 Sep 2018 14:14:15 +0200 Subject: [PATCH 2/2] Add test case; fix NodeJS version target circular dependency; Support for rule version constraints --- src/lib/schematics/update/index.ts | 7 +--- .../material/data/attribute-selectors.ts | 2 +- .../update/material/data/class-names.ts | 2 +- .../material/data/constructor-checks.ts | 2 +- .../update/material/data/css-selectors.ts | 2 +- .../update/material/data/element-selectors.ts | 2 +- .../update/material/data/input-names.ts | 2 +- .../material/data/method-call-checks.ts | 2 +- .../update/material/data/output-names.ts | 2 +- .../update/material/data/property-names.ts | 2 +- .../update/material/transform-change-data.ts | 3 +- ....ts => rippleSpeedFactorAssignmentRule.ts} | 0 ...le.ts => rippleSpeedFactorTemplateRule.ts} | 0 src/lib/schematics/update/target-version.ts | 13 ++++++++ .../update/test-cases/v7-test-cases.spec.ts | 3 +- .../v7/ripple-speed-factor_expected_output.ts | 33 +++++++++++++++++++ .../v7/ripple-speed-factor_input.ts | 33 +++++++++++++++++++ src/lib/schematics/update/tslint-config.ts | 16 +++++++-- src/lib/schematics/update/update.ts | 2 +- 19 files changed, 107 insertions(+), 21 deletions(-) rename src/lib/schematics/update/rules/misc-ripples-v7/{switchRippleSpeedFactorRule.ts => rippleSpeedFactorAssignmentRule.ts} (100%) rename src/lib/schematics/update/rules/misc-ripples-v7/{switchRippleSpeedFactorTemplateRule.ts => rippleSpeedFactorTemplateRule.ts} (100%) create mode 100644 src/lib/schematics/update/target-version.ts create mode 100644 src/lib/schematics/update/test-cases/v7/ripple-speed-factor_expected_output.ts create mode 100644 src/lib/schematics/update/test-cases/v7/ripple-speed-factor_input.ts diff --git a/src/lib/schematics/update/index.ts b/src/lib/schematics/update/index.ts index 2fb8858b610e..322e3331464b 100644 --- a/src/lib/schematics/update/index.ts +++ b/src/lib/schematics/update/index.ts @@ -7,14 +7,9 @@ */ import {Rule} from '@angular-devkit/schematics'; +import {TargetVersion} from './target-version'; import {createUpdateRule} from './update'; -/** Possible versions that can be automatically migrated by `ng update`. */ -export enum TargetVersion { - V6, - V7, -} - /** Entry point for the migration schematics with target of Angular Material 6.0.0 */ export function updateToV6(): Rule { return createUpdateRule(TargetVersion.V6); diff --git a/src/lib/schematics/update/material/data/attribute-selectors.ts b/src/lib/schematics/update/material/data/attribute-selectors.ts index 22adcbe4164c..dcf48aaf6793 100644 --- a/src/lib/schematics/update/material/data/attribute-selectors.ts +++ b/src/lib/schematics/update/material/data/attribute-selectors.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; -import {TargetVersion} from '../../index'; export interface MaterialAttributeSelectorData { /** The attribute name to replace. */ diff --git a/src/lib/schematics/update/material/data/class-names.ts b/src/lib/schematics/update/material/data/class-names.ts index 74b9d5690cc0..e2c60a790523 100644 --- a/src/lib/schematics/update/material/data/class-names.ts +++ b/src/lib/schematics/update/material/data/class-names.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialClassNameData { diff --git a/src/lib/schematics/update/material/data/constructor-checks.ts b/src/lib/schematics/update/material/data/constructor-checks.ts index 06e4190fa8db..f7e6a4e978e0 100644 --- a/src/lib/schematics/update/material/data/constructor-checks.ts +++ b/src/lib/schematics/update/material/data/constructor-checks.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; /** diff --git a/src/lib/schematics/update/material/data/css-selectors.ts b/src/lib/schematics/update/material/data/css-selectors.ts index 600b6262d54e..8b22a7e718f5 100644 --- a/src/lib/schematics/update/material/data/css-selectors.ts +++ b/src/lib/schematics/update/material/data/css-selectors.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialCssSelectorData { diff --git a/src/lib/schematics/update/material/data/element-selectors.ts b/src/lib/schematics/update/material/data/element-selectors.ts index e2f834d11e5e..cb16727419d0 100644 --- a/src/lib/schematics/update/material/data/element-selectors.ts +++ b/src/lib/schematics/update/material/data/element-selectors.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialElementSelectorData { diff --git a/src/lib/schematics/update/material/data/input-names.ts b/src/lib/schematics/update/material/data/input-names.ts index 4813c9ef61da..17cf52d49e64 100644 --- a/src/lib/schematics/update/material/data/input-names.ts +++ b/src/lib/schematics/update/material/data/input-names.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialInputNameData { diff --git a/src/lib/schematics/update/material/data/method-call-checks.ts b/src/lib/schematics/update/material/data/method-call-checks.ts index 5322ae05f48d..5c2bb9a7ccc9 100644 --- a/src/lib/schematics/update/material/data/method-call-checks.ts +++ b/src/lib/schematics/update/material/data/method-call-checks.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialMethodCallData { diff --git a/src/lib/schematics/update/material/data/output-names.ts b/src/lib/schematics/update/material/data/output-names.ts index 7be76533937c..3f10e6d4ec00 100644 --- a/src/lib/schematics/update/material/data/output-names.ts +++ b/src/lib/schematics/update/material/data/output-names.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialOutputNameData { diff --git a/src/lib/schematics/update/material/data/property-names.ts b/src/lib/schematics/update/material/data/property-names.ts index 22026d665141..7c8aec7971fc 100644 --- a/src/lib/schematics/update/material/data/property-names.ts +++ b/src/lib/schematics/update/material/data/property-names.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../../index'; +import {TargetVersion} from '../../target-version'; import {VersionChanges} from '../transform-change-data'; export interface MaterialPropertyNameData { diff --git a/src/lib/schematics/update/material/transform-change-data.ts b/src/lib/schematics/update/material/transform-change-data.ts index 52f7ab4d8b37..4192aad5f2e7 100644 --- a/src/lib/schematics/update/material/transform-change-data.ts +++ b/src/lib/schematics/update/material/transform-change-data.ts @@ -5,7 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {TargetVersion} from '../index'; + +import {TargetVersion} from '../target-version'; export type VersionChanges = { [target in TargetVersion]?: ReadableChange[]; diff --git a/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorRule.ts b/src/lib/schematics/update/rules/misc-ripples-v7/rippleSpeedFactorAssignmentRule.ts similarity index 100% rename from src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorRule.ts rename to src/lib/schematics/update/rules/misc-ripples-v7/rippleSpeedFactorAssignmentRule.ts diff --git a/src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorTemplateRule.ts b/src/lib/schematics/update/rules/misc-ripples-v7/rippleSpeedFactorTemplateRule.ts similarity index 100% rename from src/lib/schematics/update/rules/misc-ripples-v7/switchRippleSpeedFactorTemplateRule.ts rename to src/lib/schematics/update/rules/misc-ripples-v7/rippleSpeedFactorTemplateRule.ts diff --git a/src/lib/schematics/update/target-version.ts b/src/lib/schematics/update/target-version.ts new file mode 100644 index 000000000000..9d6b25164f1f --- /dev/null +++ b/src/lib/schematics/update/target-version.ts @@ -0,0 +1,13 @@ +/** + * @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.io/license + */ + +/** Possible versions that can be automatically migrated by `ng update`. */ +export enum TargetVersion { + V6, + V7, +} diff --git a/src/lib/schematics/update/test-cases/v7-test-cases.spec.ts b/src/lib/schematics/update/test-cases/v7-test-cases.spec.ts index 4310bfa10256..7bf50ad6775e 100644 --- a/src/lib/schematics/update/test-cases/v7-test-cases.spec.ts +++ b/src/lib/schematics/update/test-cases/v7-test-cases.spec.ts @@ -10,7 +10,8 @@ describe('v7 upgrade test cases', () => { * a developers application. */ const testCases = [ - 'v7/property-names' + 'v7/property-names', + 'v7/ripple-speed-factor', ]; // Iterates through every test case directory and generates a jasmine test block that will diff --git a/src/lib/schematics/update/test-cases/v7/ripple-speed-factor_expected_output.ts b/src/lib/schematics/update/test-cases/v7/ripple-speed-factor_expected_output.ts new file mode 100644 index 000000000000..19fb4f814463 --- /dev/null +++ b/src/lib/schematics/update/test-cases/v7/ripple-speed-factor_expected_output.ts @@ -0,0 +1,33 @@ +import {Component} from '@angular/core'; + +class MatRipple { + speedFactor: number; +} + +class A { + self = {me: this.ripple}; + + constructor(protected ripple: MatRipple) {} + + onClick() { + this.ripple.animation = {enterDuration: 900}; + this.self.me.animation = {enterDuration: 300}; + } +} + +const b = new MatRipple(); +const myConstant = 1; + +b.animation = /** TODO: Cleanup duration calculation. */ {enterDuration: 450 / (0.5 + myConstant)}; + +@Component({ + template: `
` +}) +class C {} + +@Component({ + template: `
` +}) +class D { + myValue = 1.5; +} diff --git a/src/lib/schematics/update/test-cases/v7/ripple-speed-factor_input.ts b/src/lib/schematics/update/test-cases/v7/ripple-speed-factor_input.ts new file mode 100644 index 000000000000..b7f9ae25a407 --- /dev/null +++ b/src/lib/schematics/update/test-cases/v7/ripple-speed-factor_input.ts @@ -0,0 +1,33 @@ +import {Component} from '@angular/core'; + +class MatRipple { + speedFactor: number; +} + +class A { + self = {me: this.ripple}; + + constructor(protected ripple: MatRipple) {} + + onClick() { + this.ripple.speedFactor = 0.5; + this.self.me.speedFactor = 1.5; + } +} + +const b = new MatRipple(); +const myConstant = 1; + +b.speedFactor = 0.5 + myConstant; + +@Component({ + template: `
` +}) +class C {} + +@Component({ + template: `
` +}) +class D { + myValue = 1.5; +} diff --git a/src/lib/schematics/update/tslint-config.ts b/src/lib/schematics/update/tslint-config.ts index cf93588d6725..1dde114423d4 100644 --- a/src/lib/schematics/update/tslint-config.ts +++ b/src/lib/schematics/update/tslint-config.ts @@ -7,7 +7,7 @@ */ import {sync as globSync} from 'glob'; -import {TargetVersion} from './index'; +import {TargetVersion} from './target-version'; /** List of rules that need to be enabled when running the TSLint fix task. */ const upgradeRules = [ @@ -52,6 +52,10 @@ const upgradeRules = [ // Additional misc rules. 'check-import-misc', 'check-template-misc', + + // Ripple misc V7 + ['ripple-speed-factor-assignment', TargetVersion.V7], + ['ripple-speed-factor-template', TargetVersion.V7], ]; /** List of absolute paths that refer to directories that contain the upgrade rules. */ @@ -66,8 +70,14 @@ const rulesDirectory = globSync('rules/**/', {cwd: __dirname, absolute: true}); * walker is not able to detect stylesheets which are not referenced by Angular. */ export function createTslintConfig(target: TargetVersion, extraStyleFiles: string[]) { - const rules = upgradeRules.reduce((result, ruleName) => { - result[ruleName] = [true, target, extraStyleFiles]; + const rules = upgradeRules.reduce((result, data) => { + const ruleName = data instanceof Array ? data[0] : data; + const versionConstraints = data instanceof Array ? data.slice(1) : null; + + if (!versionConstraints || versionConstraints.includes(target)) { + result[ruleName] = [true, target, extraStyleFiles]; + } + return result; }, {}); diff --git a/src/lib/schematics/update/update.ts b/src/lib/schematics/update/update.ts index e9288d846ffc..92481591471f 100644 --- a/src/lib/schematics/update/update.ts +++ b/src/lib/schematics/update/update.ts @@ -9,8 +9,8 @@ import {Rule, SchematicContext, TaskId, Tree} from '@angular-devkit/schematics'; import {RunSchematicTask, TslintFixTask} from '@angular-devkit/schematics/tasks'; import {sync as globSync} from 'glob'; -import {TargetVersion} from './index'; import {getProjectTsConfigPaths} from './project-tsconfig-paths'; +import {TargetVersion} from './target-version'; import {createTslintConfig} from './tslint-config'; /** Entry point for `ng update` from Angular CLI. */