From b89aa7698fd096130e02492f455e66f6d452a32b Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sat, 18 Mar 2017 17:53:36 +0100 Subject: [PATCH] fix(progress-spinner): not redrawing when changing modes * Fixes the progress spinner not redrawing when the mode is changed dynamically from `indeterminate` to `determinate`. * Some minor cleanup in the progress spinner. Fixes #3648. --- .../progress-spinner-demo.html | 8 +++-- .../progress-spinner/progress-spinner-demo.ts | 3 +- .../progress-spinner/progress-spinner.spec.ts | 35 ++++++++++++++----- src/lib/progress-spinner/progress-spinner.ts | 27 ++++++++------ 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/demo-app/progress-spinner/progress-spinner-demo.html b/src/demo-app/progress-spinner/progress-spinner-demo.html index 3abea5c81d1a..dfb6f65e5e37 100644 --- a/src/demo-app/progress-spinner/progress-spinner-demo.html +++ b/src/demo-app/progress-spinner/progress-spinner-demo.html @@ -4,11 +4,14 @@

Determinate

Value: {{progressValue}}

+ Is determinate
- - + +

Indeterminate

@@ -24,3 +27,4 @@

Indeterminate

+ diff --git a/src/demo-app/progress-spinner/progress-spinner-demo.ts b/src/demo-app/progress-spinner/progress-spinner-demo.ts index c11cedc5f57a..b67781631c5e 100644 --- a/src/demo-app/progress-spinner/progress-spinner-demo.ts +++ b/src/demo-app/progress-spinner/progress-spinner-demo.ts @@ -8,8 +8,9 @@ import {Component} from '@angular/core'; styleUrls: ['progress-spinner-demo.css'], }) export class ProgressSpinnerDemo { - progressValue: number = 40; + progressValue: number = 60; color: string = 'primary'; + modeToggle: boolean = false; step(val: number) { this.progressValue = Math.max(0, Math.min(100, val + this.progressValue)); diff --git a/src/lib/progress-spinner/progress-spinner.spec.ts b/src/lib/progress-spinner/progress-spinner.spec.ts index 0db59707ec16..ed0a0f267222 100644 --- a/src/lib/progress-spinner/progress-spinner.spec.ts +++ b/src/lib/progress-spinner/progress-spinner.spec.ts @@ -50,11 +50,11 @@ describe('MdProgressSpinner', () => { it('should set the value to undefined when the mode is set to indeterminate', () => { let fixture = TestBed.createComponent(ProgressSpinnerWithValueAndBoundMode); let progressElement = fixture.debugElement.query(By.css('md-progress-spinner')); - fixture.debugElement.componentInstance.mode = 'determinate'; + fixture.componentInstance.mode = 'determinate'; fixture.detectChanges(); expect(progressElement.componentInstance.value).toBe(50); - fixture.debugElement.componentInstance.mode = 'indeterminate'; + fixture.componentInstance.mode = 'indeterminate'; fixture.detectChanges(); expect(progressElement.componentInstance.value).toBe(undefined); }); @@ -89,7 +89,7 @@ describe('MdProgressSpinner', () => { let progressElement = fixture.debugElement.query(By.css('md-progress-spinner')); expect(progressElement.componentInstance.interdeterminateInterval).toBeTruthy(); - fixture.debugElement.componentInstance.isHidden = true; + fixture.componentInstance.isHidden = true; fixture.detectChanges(); expect(progressElement.componentInstance.interdeterminateInterval).toBeFalsy(); }); @@ -102,7 +102,7 @@ describe('MdProgressSpinner', () => { expect(progressElement.componentInstance.interdeterminateInterval).toBeTruthy(); - fixture.debugElement.componentInstance.isHidden = true; + fixture.componentInstance.isHidden = true; fixture.detectChanges(); expect(progressElement.componentInstance.interdeterminateInterval).toBeFalsy(); @@ -116,7 +116,7 @@ describe('MdProgressSpinner', () => { expect(progressElement.nativeElement.classList).toContain('mat-primary'); - fixture.debugElement.componentInstance.color = 'accent'; + fixture.componentInstance.color = 'accent'; fixture.detectChanges(); expect(progressElement.nativeElement.classList).toContain('mat-accent'); @@ -131,13 +131,30 @@ describe('MdProgressSpinner', () => { expect(progressElement.nativeElement.classList).toContain('mat-primary'); - fixture.debugElement.componentInstance.color = 'accent'; + fixture.componentInstance.color = 'accent'; fixture.detectChanges(); expect(progressElement.nativeElement.classList).toContain('mat-accent'); expect(progressElement.nativeElement.classList).not.toContain('mat-primary'); }); + it('should re-render the circle when switching from indeterminate to determinate mode', () => { + let fixture = TestBed.createComponent(ProgressSpinnerWithValueAndBoundMode); + let progressElement = fixture.debugElement.query(By.css('md-progress-spinner')).nativeElement; + + fixture.componentInstance.mode = 'indeterminate'; + fixture.detectChanges(); + + let path = progressElement.querySelector('path'); + let oldDimesions = path.getAttribute('d'); + + fixture.componentInstance.mode = 'determinate'; + fixture.detectChanges(); + + expect(path.getAttribute('d')).not + .toBe(oldDimesions, 'Expected circle dimensions to have changed.'); + }); + }); @@ -148,14 +165,14 @@ class BasicProgressSpinner { } class IndeterminateProgressSpinner { } @Component({template: ''}) -class ProgressSpinnerWithValueAndBoundMode { } +class ProgressSpinnerWithValueAndBoundMode { mode = 'indeterminate'; } @Component({template: ` `}) -class IndeterminateProgressSpinnerWithNgIf { } +class IndeterminateProgressSpinnerWithNgIf { isHidden = false; } @Component({template: ``}) -class SpinnerWithNgIf { } +class SpinnerWithNgIf { isHidden = false; } @Component({template: ``}) class SpinnerWithColor { color: string = 'primary'; } diff --git a/src/lib/progress-spinner/progress-spinner.ts b/src/lib/progress-spinner/progress-spinner.ts index 6cf9d76995df..7248cfd0e857 100644 --- a/src/lib/progress-spinner/progress-spinner.ts +++ b/src/lib/progress-spinner/progress-spinner.ts @@ -134,7 +134,7 @@ export class MdProgressSpinner implements OnDestroy { set value(v: number) { if (v != null && this.mode == 'determinate') { let newValue = clamp(v); - this._animateCircle((this.value || 0), newValue, linearEase, DURATION_DETERMINATE, 0); + this._animateCircle(this.value || 0, newValue); this._value = newValue; } } @@ -150,13 +150,13 @@ export class MdProgressSpinner implements OnDestroy { get mode() { return this._mode; } - set mode(m: ProgressSpinnerMode) { - if (m == 'indeterminate') { + set mode(mode: ProgressSpinnerMode) { + if (mode === 'indeterminate') { this._startIndeterminateAnimation(); } else { - this._cleanupIndeterminateAnimation(); + this._cleanupIndeterminateAnimation(true); } - this._mode = m; + this._mode = mode; } constructor( @@ -176,8 +176,8 @@ export class MdProgressSpinner implements OnDestroy { * @param rotation The starting angle of the circle fill, with 0° represented at the top center * of the circle. */ - private _animateCircle(animateFrom: number, animateTo: number, ease: EasingFn, - duration: number, rotation: number) { + private _animateCircle(animateFrom: number, animateTo: number, ease: EasingFn = linearEase, + duration = DURATION_DETERMINATE, rotation = 0) { let id = ++this._lastAnimationId; let startTime = Date.now(); @@ -237,16 +237,23 @@ export class MdProgressSpinner implements OnDestroy { /** * Removes interval, ending the animation. + * @param redraw Whether to redraw the circle after the animation is cleared. */ - private _cleanupIndeterminateAnimation() { - this.interdeterminateInterval = null; + private _cleanupIndeterminateAnimation(redraw = false) { + if (this.interdeterminateInterval) { + this.interdeterminateInterval = null; + + if (redraw) { + this._animateCircle(0, this._value); + } + } } /** * Renders the arc onto the SVG element. Proxies `getArc` while setting the proper * DOM attribute on the ``. */ - private _renderArc(currentValue: number, rotation: number) { + private _renderArc(currentValue: number, rotation = 0) { // Caches the path reference so it doesn't have to be looked up every time. let path = this._path = this._path || this._elementRef.nativeElement.querySelector('path');