Skip to content

Commit 3352a2c

Browse files
committed
feat(material/button): Add support for showing a progress indicator inside the button
Add a new API, testing API, and docs for allowing progress indicators to be projected into a button and have them shown in an accessible manner Fixes #13667
1 parent e78587f commit 3352a2c

File tree

23 files changed

+632
-63
lines changed

23 files changed

+632
-63
lines changed

goldens/material/button/index.api.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class MatButton extends MatButtonBase {
3333
set appearance(value: MatButtonAppearance | '');
3434
setAppearance(appearance: MatButtonAppearance): void;
3535
// (undocumented)
36-
static ɵcmp: i0.ɵɵComponentDeclaration<MatButton, " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", ["matButton", "matAnchor"], { "appearance": { "alias": "matButton"; "required": false; }; }, {}, never, [".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])", "*", ".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]"], true, never>;
36+
static ɵcmp: i0.ɵɵComponentDeclaration<MatButton, " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", ["matButton", "matAnchor"], { "appearance": { "alias": "matButton"; "required": false; }; }, {}, never, [".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])", "*", ".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]", "[progressIndicator]"], true, never>;
3737
// (undocumented)
3838
static ɵfac: i0.ɵɵFactoryDeclaration<MatButton, never>;
3939
}
@@ -74,7 +74,7 @@ export class MatFabButton extends MatButtonBase {
7474
// (undocumented)
7575
static ngAcceptInputType_extended: unknown;
7676
// (undocumented)
77-
static ɵcmp: i0.ɵɵComponentDeclaration<MatFabButton, "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", ["matButton", "matAnchor"], { "extended": { "alias": "extended"; "required": false; }; }, {}, never, [".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])", "*", ".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]"], true, never>;
77+
static ɵcmp: i0.ɵɵComponentDeclaration<MatFabButton, "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", ["matButton", "matAnchor"], { "extended": { "alias": "extended"; "required": false; }; }, {}, never, [".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])", "*", ".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]", "[progressIndicator]"], true, never>;
7878
// (undocumented)
7979
static ɵfac: i0.ɵɵFactoryDeclaration<MatFabButton, never>;
8080
}
@@ -94,7 +94,7 @@ export type MatIconAnchor = MatIconButton;
9494
export class MatIconButton extends MatButtonBase {
9595
constructor(...args: unknown[]);
9696
// (undocumented)
97-
static ɵcmp: i0.ɵɵComponentDeclaration<MatIconButton, "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", ["matButton", "matAnchor"], {}, {}, never, ["*"], true, never>;
97+
static ɵcmp: i0.ɵɵComponentDeclaration<MatIconButton, "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", ["matButton", "matAnchor"], {}, {}, never, ["*", "[progressIndicator]"], true, never>;
9898
// (undocumented)
9999
static ɵfac: i0.ɵɵFactoryDeclaration<MatIconButton, never>;
100100
}
@@ -111,7 +111,7 @@ export class MatMiniFabButton extends MatButtonBase {
111111
// (undocumented)
112112
_isFab: boolean;
113113
// (undocumented)
114-
static ɵcmp: i0.ɵɵComponentDeclaration<MatMiniFabButton, "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", ["matButton", "matAnchor"], {}, {}, never, [".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])", "*", ".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]"], true, never>;
114+
static ɵcmp: i0.ɵɵComponentDeclaration<MatMiniFabButton, "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", ["matButton", "matAnchor"], {}, {}, never, [".material-icons:not([iconPositionEnd]), mat-icon:not([iconPositionEnd]), [matButtonIcon]:not([iconPositionEnd])", "*", ".material-icons[iconPositionEnd], mat-icon[iconPositionEnd], [matButtonIcon][iconPositionEnd]", "[progressIndicator]"], true, never>;
115115
// (undocumented)
116116
static ɵfac: i0.ɵɵFactoryDeclaration<MatMiniFabButton, never>;
117117
}

goldens/material/button/testing/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class MatButtonHarness extends ContentContainerComponentHarness {
4242
static hostSelector: string;
4343
isDisabled(): Promise<boolean>;
4444
isFocused(): Promise<boolean>;
45+
isShowingProgress(): Promise<boolean>;
4546
static with<T extends MatButtonHarness>(this: ComponentHarnessConstructor<T>, options?: ButtonHarnessFilters): HarnessPredicate<T>;
4647
}
4748

src/components-examples/material/button/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ ng_project(
2020
"//src/cdk/testing/testbed",
2121
"//src/material/button",
2222
"//src/material/button/testing",
23+
"//src/material/checkbox",
2324
"//src/material/divider",
2425
"//src/material/icon",
26+
"//src/material/progress-spinner",
2527
"//src/material/tooltip",
2628
],
2729
)

src/components-examples/material/button/button-overview/button-overview-example.html

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
<button matButton>Basic</button>
55
<button matButton disabled>Disabled</button>
66
<a matButton href="https://www.google.com/" target="_blank">Link</a>
7+
<button matButton showProgress="true">
8+
Progress
9+
<mat-progress-spinner
10+
progressIndicator
11+
mode="indeterminate"
12+
diameter="20"
13+
aria-label="Loading"
14+
tabindex=""
15+
/>
16+
</button>
717
</div>
818
</section>
919
<mat-divider/>
@@ -13,6 +23,16 @@
1323
<button matButton="elevated">Basic</button>
1424
<button matButton="elevated" disabled>Disabled</button>
1525
<a matButton="elevated" href="https://www.google.com/" target="_blank">Link</a>
26+
<button matButton="elevated" showProgress="true">
27+
Progress
28+
<mat-progress-spinner
29+
progressIndicator
30+
mode="indeterminate"
31+
diameter="20"
32+
aria-label="Loading"
33+
tabindex=""
34+
/>
35+
</button>
1636
</div>
1737
</section>
1838
<mat-divider/>
@@ -22,6 +42,16 @@
2242
<button matButton="outlined">Basic</button>
2343
<button matButton="outlined" disabled>Disabled</button>
2444
<a matButton="outlined" href="https://www.google.com/" target="_blank">Link</a>
45+
<button matButton="outlined" showProgress="true">
46+
Progress
47+
<mat-progress-spinner
48+
progressIndicator
49+
mode="indeterminate"
50+
diameter="20"
51+
aria-label="Loading"
52+
tabindex=""
53+
/>
54+
</button>
2555
</div>
2656
</section>
2757
<mat-divider/>
@@ -31,6 +61,16 @@
3161
<button matButton="filled">Basic</button>
3262
<button matButton="filled" disabled>Disabled</button>
3363
<a matButton="filled" href="https://www.google.com/" target="_blank">Link</a>
64+
<button matButton="filled" showProgress="true">
65+
Progress
66+
<mat-progress-spinner
67+
progressIndicator
68+
mode="indeterminate"
69+
diameter="20"
70+
aria-label="Loading"
71+
tabindex=""
72+
/>
73+
</button>
3474
</div>
3575
</section>
3676
<mat-divider/>
@@ -40,6 +80,16 @@
4080
<button matButton="tonal" >Basic</button>
4181
<button matButton="tonal" disabled>Disabled</button>
4282
<a matButton="tonal" href="https://www.google.com/" target="_blank">Link</a>
83+
<button matButton="tonal" showProgress="true">
84+
Progress
85+
<mat-progress-spinner
86+
progressIndicator
87+
mode="indeterminate"
88+
diameter="20"
89+
aria-label="Loading"
90+
tabindex=""
91+
/>
92+
</button>
4393
</div>
4494
</section>
4595
<mat-divider/>
@@ -53,6 +103,16 @@
53103
<button matIconButton disabled aria-label="Example icon button with a open in new tab icon">
54104
<mat-icon>open_in_new</mat-icon>
55105
</button>
106+
<button matIconButton showProgress="true" aria-label="Example icon button with a download icon">
107+
<mat-icon>download</mat-icon>
108+
<mat-progress-spinner
109+
progressIndicator
110+
mode="indeterminate"
111+
diameter="20"
112+
aria-label="Loading"
113+
tabindex=""
114+
/>
115+
</button>
56116
</div>
57117
</div>
58118
</section>
@@ -67,6 +127,16 @@
67127
<button matFab disabled aria-label="Example icon button with a heart icon">
68128
<mat-icon>favorite</mat-icon>
69129
</button>
130+
<button matFab showProgress="true" aria-label="Example icon button with a skull icon">
131+
<mat-icon>skull</mat-icon>
132+
<mat-progress-spinner
133+
progressIndicator
134+
mode="indeterminate"
135+
diameter="20"
136+
aria-label="Loading"
137+
tabindex=""
138+
/>
139+
</button>
70140
</div>
71141
</div>
72142
</section>
@@ -81,6 +151,16 @@
81151
<button matMiniFab disabled aria-label="Example icon button with a home icon">
82152
<mat-icon>home</mat-icon>
83153
</button>
154+
<button matMiniFab showProgress="true" aria-label="Example icon button with a wifi icon">
155+
<mat-icon>wifi</mat-icon>
156+
<mat-progress-spinner
157+
progressIndicator
158+
mode="indeterminate"
159+
diameter="20"
160+
aria-label="Loading"
161+
tabindex=""
162+
/>
163+
</button>
84164
</div>
85165
</div>
86166
</section>
@@ -101,6 +181,17 @@
101181
<mat-icon>favorite</mat-icon>
102182
Link
103183
</a>
184+
<button matFab extended showProgress="true">
185+
<mat-icon>favorite</mat-icon>
186+
Progress
187+
<mat-progress-spinner
188+
progressIndicator
189+
mode="indeterminate"
190+
diameter="20"
191+
aria-label="Loading"
192+
tabindex=""
193+
/>
194+
</button>
104195
</div>
105196
</div>
106197
</section>

src/components-examples/material/button/button-overview/button-overview-example.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {Component} from '@angular/core';
22
import {MatIconModule} from '@angular/material/icon';
33
import {MatDividerModule} from '@angular/material/divider';
44
import {MatButtonModule} from '@angular/material/button';
5+
import {MatProgressSpinner} from '@angular/material/progress-spinner';
56

67
/**
78
* @title Button overview
@@ -10,6 +11,6 @@ import {MatButtonModule} from '@angular/material/button';
1011
selector: 'button-overview-example',
1112
templateUrl: 'button-overview-example.html',
1213
styleUrl: 'button-overview-example.css',
13-
imports: [MatButtonModule, MatDividerModule, MatIconModule],
14+
imports: [MatButtonModule, MatDividerModule, MatIconModule, MatProgressSpinner],
1415
})
1516
export class ButtonOverviewExample {}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<section>
2+
<mat-checkbox
3+
(change)="toggleShowProgress()"
4+
>
5+
Show the progress indicator
6+
</mat-checkbox>
7+
</section>
8+
<section>
9+
<h3>Button with a MatProgressSpinner</h3>
10+
<button matButton="outlined" [showProgress]="showProgress()">
11+
Download
12+
<mat-progress-spinner
13+
progressIndicator
14+
mode="indeterminate"
15+
diameter="20"
16+
aria-label="Loading"
17+
tabindex=""
18+
/>
19+
</button>
20+
</section>
21+
<section>
22+
<h3>Button with a custom progress indicator</h3>
23+
<button matButton="outlined" [showProgress]="showProgress()">
24+
Download
25+
<div progressIndicator role="progressbar" aria-valuemin="0" aria-valuemax="100">
26+
Loading...
27+
</div>
28+
</button>
29+
</section>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {Component, signal} from '@angular/core';
2+
import {MatButton} from '@angular/material/button';
3+
import {MatCheckbox} from '@angular/material/checkbox';
4+
import {MatProgressSpinner} from '@angular/material/progress-spinner';
5+
6+
/**
7+
* @title Buttons with progress indicators
8+
*/
9+
@Component({
10+
selector: 'button-progress-indicator-example',
11+
templateUrl: 'button-progress-indicator-example.html',
12+
imports: [MatButton, MatCheckbox, MatProgressSpinner],
13+
})
14+
export class ButtonProgressIndicatorExample {
15+
protected readonly showProgress = signal(false);
16+
17+
protected toggleShowProgress() {
18+
this.showProgress.update(show => !show);
19+
}
20+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export {ButtonOverviewExample} from './button-overview/button-overview-example';
22
export {ButtonDisabledInteractiveExample} from './button-disabled-interactive/button-disabled-interactive-example';
33
export {ButtonHarnessExample} from './button-harness/button-harness-example';
4+
export {ButtonProgressIndicatorExample} from './button-progress-indicator/button-progress-indicator-example';

src/dev-app/button/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ ng_project(
1515
"//src/material/button",
1616
"//src/material/checkbox",
1717
"//src/material/icon",
18+
"//src/material/progress-spinner",
1819
"//src/material/tooltip",
1920
],
2021
)

0 commit comments

Comments
 (0)