Skip to content

Commit 41c5a2d

Browse files
committed
fix(progress-spinner): coerce string values
Coerces any string values that are passed to `value`, `diameter` and `strokeWidth` to numbers. Fixes #7790.
1 parent cf11ff2 commit 41c5a2d

File tree

2 files changed

+60
-22
lines changed

2 files changed

+60
-22
lines changed

src/lib/progress-spinner/progress-spinner.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {TestBed, async} from '@angular/core/testing';
22
import {Component} from '@angular/core';
33
import {By} from '@angular/platform-browser';
4-
import {MatProgressSpinnerModule} from './index';
4+
import {MatProgressSpinnerModule, MatProgressSpinner} from './index';
55

66

77
describe('MatProgressSpinner', () => {
@@ -17,6 +17,7 @@ describe('MatProgressSpinner', () => {
1717
ProgressSpinnerCustomStrokeWidth,
1818
ProgressSpinnerCustomDiameter,
1919
SpinnerWithColor,
20+
ProgressSpinnerWithStringValues,
2021
],
2122
}).compileComponents();
2223
}));
@@ -174,6 +175,24 @@ describe('MatProgressSpinner', () => {
174175
expect(fixture.nativeElement.querySelector('svg').getAttribute('focusable')).toBe('false');
175176
});
176177

178+
it('should handle the number inputs being passed in as strings', () => {
179+
const fixture = TestBed.createComponent(ProgressSpinnerWithStringValues);
180+
const spinner = fixture.debugElement.query(By.directive(MatProgressSpinner));
181+
const svgElement = spinner.nativeElement.querySelector('svg');
182+
183+
fixture.detectChanges();
184+
185+
expect(spinner.componentInstance.diameter).toBe(37);
186+
expect(spinner.componentInstance.strokeWidth).toBe(11);
187+
expect(spinner.componentInstance.value).toBe(25);
188+
189+
expect(spinner.nativeElement.style.width).toBe('38px');
190+
expect(spinner.nativeElement.style.height).toBe('38px');
191+
expect(svgElement.style.width).toBe('38px');
192+
expect(svgElement.style.height).toBe('38px');
193+
expect(svgElement.getAttribute('viewBox')).toBe('0 0 38 38');
194+
});
195+
177196
});
178197

179198

@@ -201,3 +220,10 @@ class SpinnerWithColor { color: string = 'primary'; }
201220

202221
@Component({template: `<mat-progress-spinner value="50" [color]="color"></mat-progress-spinner>`})
203222
class ProgressSpinnerWithColor { color: string = 'primary'; }
223+
224+
@Component({
225+
template: `
226+
<mat-progress-spinner value="25" diameter="37" strokeWidth="11"></mat-progress-spinner>
227+
`
228+
})
229+
class ProgressSpinnerWithStringValues { }

src/lib/progress-spinner/progress-spinner.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,23 @@ import {
2121
import {CanColor, mixinColor} from '@angular/material/core';
2222
import {Platform} from '@angular/cdk/platform';
2323
import {DOCUMENT} from '@angular/common';
24+
import {coerceNumberProperty} from '@angular/cdk/coercion';
2425

2526
/** Possible mode for a progress spinner. */
2627
export type ProgressSpinnerMode = 'determinate' | 'indeterminate';
2728

29+
/**
30+
* Base reference size of the spinner.
31+
* @docs-private
32+
*/
33+
const BASE_SIZE = 100;
34+
35+
/**
36+
* Base reference stroke width of the spinner.
37+
* @docs-private
38+
*/
39+
const BASE_STROKE_WIDTH = 10;
40+
2841
// Boilerplate for applying mixins to MatProgressSpinner.
2942
/** @docs-private */
3043
export class MatProgressSpinnerBase {
@@ -83,16 +96,15 @@ const INDETERMINATE_ANIMATION_TEMPLATE = `
8396
export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements CanColor,
8497
OnChanges {
8598

86-
private _value: number = 0;
87-
private readonly _baseSize = 100;
88-
private readonly _baseStrokeWidth = 10;
99+
private _value = 0;
100+
private _strokeWidth = 10;
89101
private _fallbackAnimation = false;
90102

91103
/** The width and height of the host element. Will grow with stroke width. **/
92-
_elementSize = this._baseSize;
104+
_elementSize = BASE_SIZE;
93105

94106
/** Tracks diameters of existing instances to de-dupe generated styles (default d = 100) */
95-
private static diameters = new Set<number>([100]);
107+
private static diameters = new Set<number>([BASE_SIZE]);
96108

97109
/**
98110
* Used for storing all of the generated keyframe animations.
@@ -105,14 +117,23 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
105117
get diameter(): number {
106118
return this._diameter;
107119
}
108-
109120
set diameter(size: number) {
110-
this._setDiameterAndInitStyles(size);
121+
this._diameter = coerceNumberProperty(size);
122+
123+
if (!this._fallbackAnimation && !MatProgressSpinner.diameters.has(this._diameter)) {
124+
this._attachStyleNode();
125+
}
111126
}
112-
_diameter = this._baseSize;
127+
private _diameter = BASE_SIZE;
113128

114129
/** Stroke width of the progress spinner. */
115-
@Input() strokeWidth: number = 10;
130+
@Input()
131+
get strokeWidth(): number {
132+
return this._strokeWidth;
133+
}
134+
set strokeWidth(value: number) {
135+
this._strokeWidth = coerceNumberProperty(value);
136+
}
116137

117138
/** Mode of the progress circle */
118139
@Input() mode: ProgressSpinnerMode = 'determinate';
@@ -124,7 +145,7 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
124145
}
125146
set value(newValue: number) {
126147
if (newValue != null && this.mode === 'determinate') {
127-
this._value = Math.max(0, Math.min(100, newValue));
148+
this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));
128149
}
129150
}
130151

@@ -144,14 +165,13 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
144165

145166
ngOnChanges(changes: SimpleChanges) {
146167
if (changes.strokeWidth || changes.diameter) {
147-
this._elementSize =
148-
this._diameter + Math.max(this.strokeWidth - this._baseStrokeWidth, 0);
168+
this._elementSize = this._diameter + Math.max(this.strokeWidth - BASE_STROKE_WIDTH, 0);
149169
}
150170
}
151171

152172
/** The radius of the spinner, adjusted for stroke width. */
153173
get _circleRadius() {
154-
return (this.diameter - this._baseStrokeWidth) / 2;
174+
return (this.diameter - BASE_STROKE_WIDTH) / 2;
155175
}
156176

157177
/** The view box of the spinner's svg element. */
@@ -184,14 +204,6 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
184204
return this.strokeWidth / this._elementSize * 100;
185205
}
186206

187-
/** Sets the diameter and adds diameter-specific styles if necessary. */
188-
private _setDiameterAndInitStyles(size: number): void {
189-
this._diameter = size;
190-
if (!MatProgressSpinner.diameters.has(this.diameter) && !this._fallbackAnimation) {
191-
this._attachStyleNode();
192-
}
193-
}
194-
195207
/** Dynamically generates a style tag containing the correct animation for this diameter. */
196208
private _attachStyleNode(): void {
197209
let styleTag = MatProgressSpinner.styleTag;

0 commit comments

Comments
 (0)