Skip to content

Commit 7480e3b

Browse files
authored
fix(material/progress-spinner): unable to change mode on spinner directive (#14514)
Currently we have the `mat-spinner` directive which is a shortcut to a `mat-progress-spinner` with `mode="indeterminate"`. Since the spinner inherits all of the inputs from the progress spinner, there's nothing stoping people from changing the mode back to `determinate`, however the element will look half-broken because the host bindings assume that the mode won't change. These changes update the host bindings to allow switching between modes. Fixes #14511.
1 parent 229dd6e commit 7480e3b

File tree

6 files changed

+48
-70
lines changed

6 files changed

+48
-70
lines changed

src/material-experimental/mdc-progress-spinner/progress-spinner.spec.ts

+12
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ describe('MDC-based MatProgressSpinner', () => {
2424
ProgressSpinnerWithStringValues,
2525
IndeterminateSpinnerInShadowDom,
2626
IndeterminateSpinnerInShadowDomWithNgIf,
27+
SpinnerWithMode,
2728
],
2829
}).compileComponents();
2930
}),
@@ -397,6 +398,14 @@ describe('MDC-based MatProgressSpinner', () => {
397398
expect(children.length).toBeGreaterThan(0);
398399
expect(children.every(child => child.getAttribute('aria-hidden') === 'true')).toBe(true);
399400
});
401+
402+
it('should be able to change the mode on a mat-spinner', () => {
403+
const fixture = TestBed.createComponent(SpinnerWithMode);
404+
fixture.detectChanges();
405+
406+
const progressElement = fixture.debugElement.query(By.css('mat-spinner')).nativeElement;
407+
expect(progressElement.getAttribute('mode')).toBe('determinate');
408+
});
400409
});
401410

402411
@Component({template: '<mat-progress-spinner></mat-progress-spinner>'})
@@ -470,3 +479,6 @@ class IndeterminateSpinnerInShadowDomWithNgIf {
470479

471480
diameter: number;
472481
}
482+
483+
@Component({template: '<mat-spinner mode="determinate"></mat-spinner>'})
484+
class SpinnerWithMode {}

src/material/progress-spinner/progress-spinner-module.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
import {NgModule} from '@angular/core';
99
import {CommonModule} from '@angular/common';
1010
import {MatCommonModule} from '@angular/material/core';
11-
import {MatProgressSpinner, MatSpinner} from './progress-spinner';
11+
import {MatProgressSpinner} from './progress-spinner';
1212

1313
@NgModule({
1414
imports: [MatCommonModule, CommonModule],
15-
exports: [MatProgressSpinner, MatSpinner, MatCommonModule],
16-
declarations: [MatProgressSpinner, MatSpinner],
15+
exports: [MatProgressSpinner, MatCommonModule],
16+
declarations: [MatProgressSpinner],
1717
})
1818
export class MatProgressSpinnerModule {}

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

+12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('MatProgressSpinner', () => {
2626
ProgressSpinnerWithStringValues,
2727
IndeterminateSpinnerInShadowDom,
2828
IndeterminateSpinnerInShadowDomWithNgIf,
29+
SpinnerWithMode,
2930
],
3031
}).compileComponents();
3132
}),
@@ -540,6 +541,14 @@ describe('MatProgressSpinner', () => {
540541
expect(children.length).toBeGreaterThan(0);
541542
expect(children.every(child => child.getAttribute('aria-hidden') === 'true')).toBe(true);
542543
});
544+
545+
it('should be able to change the mode on a mat-spinner', () => {
546+
const fixture = TestBed.createComponent(SpinnerWithMode);
547+
fixture.detectChanges();
548+
549+
const progressElement = fixture.debugElement.query(By.css('mat-spinner')).nativeElement;
550+
expect(progressElement.getAttribute('mode')).toBe('determinate');
551+
});
543552
});
544553

545554
@Component({template: '<mat-progress-spinner></mat-progress-spinner>'})
@@ -616,3 +625,6 @@ class IndeterminateSpinnerInShadowDomWithNgIf {
616625

617626
diameter: number;
618627
}
628+
629+
@Component({template: '<mat-spinner mode="determinate"></mat-spinner>'})
630+
class SpinnerWithMode {}

src/material/progress-spinner/progress-spinner.ts

+7-55
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,12 @@ const INDETERMINATE_ANIMATION_TEMPLATE = `
111111
* `<mat-progress-spinner>` component.
112112
*/
113113
@Component({
114-
selector: 'mat-progress-spinner',
114+
selector: 'mat-progress-spinner, mat-spinner',
115115
exportAs: 'matProgressSpinner',
116116
host: {
117117
'role': 'progressbar',
118-
'class': 'mat-progress-spinner',
118+
// `mat-spinner` is here for backward compatibility.
119+
'class': 'mat-progress-spinner mat-spinner',
119120
// set tab index to -1 so screen readers will read the aria-label
120121
// Note: there is a known issue with JAWS that does not read progressbar aria labels on FireFox
121122
'tabindex': '-1',
@@ -229,6 +230,10 @@ export class MatProgressSpinner
229230
this._noopAnimations =
230231
animationMode === 'NoopAnimations' && !!defaults && !defaults._forceAnimations;
231232

233+
if (elementRef.nativeElement.nodeName.toLowerCase() === 'mat-spinner') {
234+
this.mode = 'indeterminate';
235+
}
236+
232237
if (defaults) {
233238
if (defaults.color) {
234239
this.color = this.defaultColor = defaults.color;
@@ -356,56 +361,3 @@ export class MatProgressSpinner
356361
return this.diameter.toString().replace('.', '_');
357362
}
358363
}
359-
360-
/**
361-
* `<mat-spinner>` component.
362-
*
363-
* This is a component definition to be used as a convenience reference to create an
364-
* indeterminate `<mat-progress-spinner>` instance.
365-
*/
366-
@Component({
367-
selector: 'mat-spinner',
368-
host: {
369-
'role': 'progressbar',
370-
'mode': 'indeterminate',
371-
'class': 'mat-spinner mat-progress-spinner',
372-
'[class._mat-animation-noopable]': `_noopAnimations`,
373-
'[style.width.px]': 'diameter',
374-
'[style.height.px]': 'diameter',
375-
},
376-
inputs: ['color'],
377-
templateUrl: 'progress-spinner.html',
378-
styleUrls: ['progress-spinner.css'],
379-
changeDetection: ChangeDetectionStrategy.OnPush,
380-
encapsulation: ViewEncapsulation.None,
381-
})
382-
export class MatSpinner extends MatProgressSpinner {
383-
constructor(
384-
elementRef: ElementRef<HTMLElement>,
385-
platform: Platform,
386-
@Optional() @Inject(DOCUMENT) document: any,
387-
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode: string,
388-
@Inject(MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS)
389-
defaults?: MatProgressSpinnerDefaultOptions,
390-
/**
391-
* @deprecated `changeDetectorRef`, `viewportRuler` and `ngZone`
392-
* parameters to become required.
393-
* @breaking-change 14.0.0
394-
*/
395-
changeDetectorRef?: ChangeDetectorRef,
396-
viewportRuler?: ViewportRuler,
397-
ngZone?: NgZone,
398-
) {
399-
super(
400-
elementRef,
401-
platform,
402-
document,
403-
animationMode,
404-
defaults,
405-
changeDetectorRef,
406-
viewportRuler,
407-
ngZone,
408-
);
409-
this.mode = 'indeterminate';
410-
}
411-
}

src/material/progress-spinner/public-api.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {MatProgressSpinner} from './progress-spinner';
10+
911
export * from './progress-spinner-module';
1012
export {
1113
MatProgressSpinner,
12-
MatSpinner,
1314
MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS,
1415
ProgressSpinnerMode,
1516
MatProgressSpinnerDefaultOptions,
1617
MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS_FACTORY,
1718
} from './progress-spinner';
19+
20+
/**
21+
* @deprecated Import `MatProgressSpinner` instead. Note that the
22+
* `mat-spinner` selector isn't deprecated.
23+
* @breaking-change 8.0.0
24+
*/
25+
// tslint:disable-next-line:variable-name
26+
export const MatSpinner = MatProgressSpinner;

tools/public_api_guard/material/progress-spinner.md

+4-11
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class MatProgressSpinner extends _MatProgressSpinnerBase implements OnIni
5151
get value(): number;
5252
set value(newValue: NumberInput);
5353
// (undocumented)
54-
static ɵcmp: i0.ɵɵComponentDeclaration<MatProgressSpinner, "mat-progress-spinner", ["matProgressSpinner"], { "color": "color"; "diameter": "diameter"; "strokeWidth": "strokeWidth"; "mode": "mode"; "value": "value"; }, {}, never, never>;
54+
static ɵcmp: i0.ɵɵComponentDeclaration<MatProgressSpinner, "mat-progress-spinner, mat-spinner", ["matProgressSpinner"], { "color": "color"; "diameter": "diameter"; "strokeWidth": "strokeWidth"; "mode": "mode"; "value": "value"; }, {}, never, never>;
5555
// (undocumented)
5656
static ɵfac: i0.ɵɵFactoryDeclaration<MatProgressSpinner, [null, null, { optional: true; }, { optional: true; }, null, null, null, null]>;
5757
}
@@ -71,18 +71,11 @@ export class MatProgressSpinnerModule {
7171
// (undocumented)
7272
static ɵinj: i0.ɵɵInjectorDeclaration<MatProgressSpinnerModule>;
7373
// (undocumented)
74-
static ɵmod: i0.ɵɵNgModuleDeclaration<MatProgressSpinnerModule, [typeof i1.MatProgressSpinner, typeof i1.MatSpinner], [typeof i2.MatCommonModule, typeof i3.CommonModule], [typeof i1.MatProgressSpinner, typeof i1.MatSpinner, typeof i2.MatCommonModule]>;
74+
static ɵmod: i0.ɵɵNgModuleDeclaration<MatProgressSpinnerModule, [typeof i1.MatProgressSpinner], [typeof i2.MatCommonModule, typeof i3.CommonModule], [typeof i1.MatProgressSpinner, typeof i2.MatCommonModule]>;
7575
}
7676

77-
// @public
78-
export class MatSpinner extends MatProgressSpinner {
79-
constructor(elementRef: ElementRef<HTMLElement>, platform: Platform, document: any, animationMode: string, defaults?: MatProgressSpinnerDefaultOptions,
80-
changeDetectorRef?: ChangeDetectorRef, viewportRuler?: ViewportRuler, ngZone?: NgZone);
81-
// (undocumented)
82-
static ɵcmp: i0.ɵɵComponentDeclaration<MatSpinner, "mat-spinner", never, { "color": "color"; }, {}, never, never>;
83-
// (undocumented)
84-
static ɵfac: i0.ɵɵFactoryDeclaration<MatSpinner, [null, null, { optional: true; }, { optional: true; }, null, null, null, null]>;
85-
}
77+
// @public @deprecated (undocumented)
78+
export const MatSpinner: typeof MatProgressSpinner;
8679

8780
// @public
8881
export type ProgressSpinnerMode = 'determinate' | 'indeterminate';

0 commit comments

Comments
 (0)