Skip to content

Commit 5c26124

Browse files
committed
feat(material/button): add the ability to interact with disabled buttons
Native disabled buttons don't allow focus and prevent the button from dispatching events. In some cases this can be problematic, because it prevents the app from showing to the user why the button is disabled. These changes introduce a new opt-in input that will style buttons as disabled and set `aria-disabled="true"`, but not set the native `disabled` attribute, allowing them to be interactive.
1 parent 597b822 commit 5c26124

22 files changed

+399
-70
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
button {
2+
margin-right: 8px;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<button
2+
mat-raised-button
3+
disabled
4+
disabledInteractive
5+
matTooltip="This is a tooltip!">Disabled button allowing interactivity</button>
6+
7+
<button
8+
mat-raised-button
9+
disabled
10+
matTooltip="This is a tooltip!">Default disabled button</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {Component} from '@angular/core';
2+
import {MatButton} from '@angular/material/button';
3+
import {MatTooltip} from '@angular/material/tooltip';
4+
5+
/**
6+
* @title Interactive disabled buttons
7+
*/
8+
@Component({
9+
selector: 'button-disabled-interactive-example',
10+
templateUrl: 'button-disabled-interactive-example.html',
11+
styleUrls: ['button-disabled-interactive-example.css'],
12+
standalone: true,
13+
imports: [MatButton, MatTooltip],
14+
})
15+
export class ButtonDisabledInteractiveExample {}
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 {ButtonTypesExample} from './button-types/button-types-example';
3+
export {ButtonDisabledInteractiveExample} from './button-disabled-interactive/button-disabled-interactive-example';
34
export {ButtonHarnessExample} from './button-harness/button-harness-example';

src/dev-app/button/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ ng_module(
1111
],
1212
deps = [
1313
"//src/material/button",
14+
"//src/material/checkbox",
1415
"//src/material/icon",
16+
"//src/material/tooltip",
1517
],
1618
)
1719

src/dev-app/button/button-demo.html

Lines changed: 141 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,52 @@ <h4 class="demo-section-header">Buttons</h4>
1919
</button>
2020
</section>
2121
<section>
22-
<button mat-button disabled>normal</button>
23-
<button mat-raised-button disabled>raised</button>
24-
<button mat-stroked-button disabled>stroked</button>
25-
<button mat-flat-button disabled>flat</button>
26-
<button mat-fab disabled>
22+
<button
23+
mat-button
24+
disabled
25+
[disabledInteractive]="disabledInteractive"
26+
[matTooltip]="tooltipText">normal</button>
27+
<button
28+
mat-raised-button
29+
disabled
30+
[disabledInteractive]="disabledInteractive"
31+
[matTooltip]="tooltipText">raised</button>
32+
<button
33+
mat-stroked-button
34+
disabled
35+
[disabledInteractive]="disabledInteractive"
36+
[matTooltip]="tooltipText">stroked</button>
37+
<button
38+
mat-flat-button
39+
disabled
40+
[disabledInteractive]="disabledInteractive"
41+
[matTooltip]="tooltipText">flat</button>
42+
<button
43+
mat-fab
44+
disabled
45+
[disabledInteractive]="disabledInteractive"
46+
[matTooltip]="tooltipText">
2747
<mat-icon>check</mat-icon>
2848
</button>
29-
<button mat-mini-fab disabled>
49+
<button
50+
mat-mini-fab
51+
disabled
52+
[disabledInteractive]="disabledInteractive"
53+
[matTooltip]="tooltipText">
3054
<mat-icon>check</mat-icon>
3155
</button>
32-
<button mat-fab extended disabled>Search</button>
33-
<button mat-fab extended disabled>
56+
<button
57+
mat-fab
58+
extended
59+
disabled
60+
[disabledInteractive]="disabledInteractive"
61+
[matTooltip]="tooltipText">Search</button>
62+
<button
63+
mat-fab
64+
extended
65+
disabled
66+
[disabledInteractive]="disabledInteractive"
67+
[matTooltip]="tooltipText">
3468
<mat-icon>check</mat-icon>
3569
Search
3670
<mat-icon iconPositionEnd>check</mat-icon>
@@ -57,18 +91,61 @@ <h4 class="demo-section-header">Anchors</h4>
5791
</a>
5892
</section>
5993
<section>
60-
<a href="//www.google.com" disabled mat-button color="primary">SEARCH</a>
61-
<a href="//www.google.com" disabled mat-raised-button>SEARCH</a>
62-
<a href="//www.google.com" disabled mat-stroked-button color="primary">SEARCH</a>
63-
<a href="//www.google.com" disabled mat-flat-button>SEARCH</a>
64-
<a href="//www.google.com" disabled mat-fab>
94+
<a
95+
href="//www.google.com"
96+
disabled
97+
[disabledInteractive]="disabledInteractive"
98+
[matTooltip]="tooltipText"
99+
mat-button
100+
color="primary">SEARCH</a>
101+
<a
102+
href="//www.google.com"
103+
disabled
104+
[disabledInteractive]="disabledInteractive"
105+
[matTooltip]="tooltipText"
106+
mat-raised-button>SEARCH</a>
107+
<a
108+
href="//www.google.com"
109+
disabled
110+
[disabledInteractive]="disabledInteractive"
111+
[matTooltip]="tooltipText"
112+
mat-stroked-button color="primary">SEARCH</a>
113+
<a
114+
href="//www.google.com"
115+
disabled
116+
[disabledInteractive]="disabledInteractive"
117+
[matTooltip]="tooltipText"
118+
mat-flat-button>SEARCH</a>
119+
<a
120+
href="//www.google.com"
121+
disabled
122+
[disabledInteractive]="disabledInteractive"
123+
[matTooltip]="tooltipText"
124+
mat-fab>
65125
<mat-icon>check</mat-icon>
66126
</a>
67-
<a href="//www.google.com" disabled mat-mini-fab>
127+
<a
128+
href="//www.google.com"
129+
disabled
130+
[disabledInteractive]="disabledInteractive"
131+
[matTooltip]="tooltipText"
132+
mat-mini-fab>
68133
<mat-icon>check</mat-icon>
69134
</a>
70-
<a href="//www.google.com" disabled mat-fab extended>Search</a>
71-
<a href="//www.google.com" disabled mat-fab extended>
135+
<a
136+
href="//www.google.com"
137+
disabled
138+
[disabledInteractive]="disabledInteractive"
139+
[matTooltip]="tooltipText"
140+
mat-fab
141+
extended>Search</a>
142+
<a
143+
href="//www.google.com"
144+
disabled
145+
[disabledInteractive]="disabledInteractive"
146+
[matTooltip]="tooltipText"
147+
mat-fab
148+
extended>
72149
<mat-icon>check</mat-icon>
73150
Search
74151
<mat-icon iconPositionEnd>check</mat-icon>
@@ -81,7 +158,11 @@ <h4 class="demo-section-header">Text Buttons [mat-button]</h4>
81158
<button mat-button color="primary">primary</button>
82159
<button mat-button color="accent">accent</button>
83160
<button mat-button color="warn">warn</button>
84-
<button mat-button disabled>disabled</button>
161+
<button
162+
mat-button
163+
disabled
164+
[disabledInteractive]="disabledInteractive"
165+
[matTooltip]="tooltipText">disabled</button>
85166
<button mat-button>
86167
<mat-icon>home</mat-icon>
87168
with icons
@@ -95,7 +176,11 @@ <h4 class="demo-section-header">Raised Buttons [mat-raised-button]</h4>
95176
<button mat-raised-button color="primary">primary</button>
96177
<button mat-raised-button color="accent">accent</button>
97178
<button mat-raised-button color="warn">warn</button>
98-
<button mat-raised-button disabled>disabled</button>
179+
<button
180+
mat-raised-button
181+
disabled
182+
[disabledInteractive]="disabledInteractive"
183+
[matTooltip]="tooltipText">disabled</button>
99184
<button mat-raised-button>
100185
<mat-icon>home</mat-icon>
101186
with icons
@@ -109,7 +194,11 @@ <h4 class="demo-section-header">Stroked Buttons [mat-stroked-button]</h4>
109194
<button mat-stroked-button color="primary">primary</button>
110195
<button mat-stroked-button color="accent">accent</button>
111196
<button mat-stroked-button color="warn">warn</button>
112-
<button mat-stroked-button disabled>disabled</button>
197+
<button
198+
mat-stroked-button
199+
disabled
200+
[disabledInteractive]="disabledInteractive"
201+
[matTooltip]="tooltipText">disabled</button>
113202
<button mat-stroked-button>
114203
<mat-icon>home</mat-icon>
115204
with icons
@@ -123,7 +212,11 @@ <h4 class="demo-section-header">Flat Buttons [mat-flat-button]</h4>
123212
<button mat-flat-button color="primary">primary</button>
124213
<button mat-flat-button color="accent">accent</button>
125214
<button mat-flat-button color="warn">warn</button>
126-
<button mat-flat-button disabled>disabled</button>
215+
<button
216+
mat-flat-button
217+
disabled
218+
[disabledInteractive]="disabledInteractive"
219+
[matTooltip]="tooltipText">disabled</button>
127220
<button mat-flat-button>
128221
<mat-icon>home</mat-icon>
129222
with icons
@@ -145,12 +238,16 @@ <h4 class="demo-section-header"> Icon Buttons [mat-icon-button]</h4>
145238
<button mat-icon-button color="warn">
146239
<mat-icon>trending_up</mat-icon>
147240
</button>
148-
<button mat-icon-button disabled>
241+
<button
242+
mat-icon-button
243+
disabled
244+
[disabledInteractive]="disabledInteractive"
245+
[matTooltip]="tooltipText">
149246
<mat-icon>visibility</mat-icon>
150247
</button>
151248
</section>
152249

153-
<h4 class="demo-section-header"> Icon Button Anchors [mat-icon-button]</h4>
250+
<h4 class="demo-section-header">Icon Button Anchors [mat-icon-button]</h4>
154251
<section>
155252
<a href="#" mat-icon-button>
156253
<mat-icon>cached</mat-icon>
@@ -164,7 +261,12 @@ <h4 class="demo-section-header"> Icon Button Anchors [mat-icon-button]</h4>
164261
<a href="#" mat-icon-button color="warn">
165262
<mat-icon>trending_up</mat-icon>
166263
</a>
167-
<a href="#" mat-icon-button disabled>
264+
<a
265+
href="#"
266+
mat-icon-button
267+
disabled
268+
[disabledInteractive]="disabledInteractive"
269+
[matTooltip]="tooltipText">
168270
<mat-icon>visibility</mat-icon>
169271
</a>
170272
</section>
@@ -183,7 +285,11 @@ <h4 class="demo-section-header">Fab Buttons [mat-fab]</h4>
183285
<button mat-fab color="warn">
184286
<mat-icon>home</mat-icon>
185287
</button>
186-
<button mat-fab disabled>
288+
<button
289+
mat-fab
290+
disabled
291+
[disabledInteractive]="disabledInteractive"
292+
[matTooltip]="tooltipText">
187293
<mat-icon>favorite</mat-icon>
188294
</button>
189295
</section>
@@ -202,7 +308,11 @@ <h4 class="demo-section-header"> Mini Fab Buttons [mat-mini-fab]</h4>
202308
<button mat-mini-fab color="warn">
203309
<mat-icon>filter_list</mat-icon>
204310
</button>
205-
<button mat-mini-fab disabled>
311+
<button
312+
mat-mini-fab
313+
disabled
314+
[disabledInteractive]="disabledInteractive"
315+
[matTooltip]="tooltipText">
206316
<mat-icon>home</mat-icon>
207317
</button>
208318
</section>
@@ -212,9 +322,12 @@ <h4 class="demo-section-header">Interaction/State</h4>
212322
<div>
213323
<p>isDisabled: {{isDisabled}}</p>
214324
<p>Button 1 as been clicked {{clickCounter}} times</p>
215-
<button mat-flat-button (click)="isDisabled=!isDisabled">
216-
{{isDisabled ? 'Enable All' : 'Disable All'}}
217-
</button>
325+
<p>
326+
<mat-checkbox [(ngModel)]="disabledInteractive">Allow disabled button interactivity</mat-checkbox>
327+
</p>
328+
<p>
329+
<mat-checkbox [(ngModel)]="isDisabled">All disabled</mat-checkbox>
330+
</p>
218331
<button mat-flat-button (click)="button1.focus()">Focus 1</button>
219332
<button mat-flat-button (click)="button2.focus()">Focus 2</button>
220333
<button mat-flat-button (click)="button3.focus()">Focus 3</button>

src/dev-app/button/button-demo.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,45 @@
77
*/
88

99
import {Component} from '@angular/core';
10-
import {MatButtonModule} from '@angular/material/button';
11-
import {MatIconModule} from '@angular/material/icon';
10+
import {FormsModule} from '@angular/forms';
11+
import {
12+
MatButton,
13+
MatAnchor,
14+
MatFabButton,
15+
MatFabAnchor,
16+
MatIconButton,
17+
MatIconAnchor,
18+
MatMiniFabButton,
19+
MatMiniFabAnchor,
20+
} from '@angular/material/button';
21+
import {MatIcon} from '@angular/material/icon';
22+
import {MatTooltip} from '@angular/material/tooltip';
23+
import {MatCheckbox} from '@angular/material/checkbox';
1224

1325
@Component({
1426
selector: 'button-demo',
1527
templateUrl: 'button-demo.html',
1628
styleUrls: ['button-demo.css'],
1729
standalone: true,
18-
imports: [MatButtonModule, MatIconModule],
30+
imports: [
31+
MatButton,
32+
MatAnchor,
33+
MatFabButton,
34+
MatFabAnchor,
35+
MatMiniFabButton,
36+
MatMiniFabAnchor,
37+
MatIconButton,
38+
MatIconAnchor,
39+
MatIcon,
40+
MatTooltip,
41+
MatCheckbox,
42+
FormsModule,
43+
],
1944
})
2045
export class ButtonDemo {
21-
isDisabled: boolean = false;
22-
clickCounter: number = 0;
23-
toggleDisable: boolean = false;
46+
isDisabled = false;
47+
clickCounter = 0;
48+
toggleDisable = false;
49+
tooltipText = 'This is a button tooltip!';
50+
disabledInteractive = false;
2451
}

src/material/button/_button-base.scss

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,17 @@
6868
@include token-utils.create-token-slot(background-color, state-layer-color);
6969
}
7070

71+
&.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before {
72+
@include token-utils.create-token-slot(background-color, disabled-state-layer-color);
73+
}
74+
7175
&:hover .mat-mdc-button-persistent-ripple::before {
7276
@include token-utils.create-token-slot(opacity, hover-state-layer-opacity);
7377
}
7478

7579
&.cdk-program-focused,
76-
&.cdk-keyboard-focused {
80+
&.cdk-keyboard-focused,
81+
&.mat-mdc-button-disabled-interactive:focus {
7782
.mat-mdc-button-persistent-ripple::before {
7883
@include token-utils.create-token-slot(opacity, focus-state-layer-opacity);
7984
}
@@ -91,11 +96,18 @@
9196
// and note that having pointer-events may have unintended side-effects, e.g. allowing the user
9297
// to click the target underneath the button.
9398
@mixin mat-private-button-disabled() {
94-
&[disabled] {
99+
// `[disabled]` shouldn't be necessary, but we keep it to maintain
100+
// compatibility with apps setting it through host bindings.
101+
&[disabled],
102+
&.mat-mdc-button-disabled {
95103
cursor: default;
96104
pointer-events: none;
97105
@content;
98106
}
107+
108+
&.mat-mdc-button-disabled-interactive {
109+
pointer-events: auto;
110+
}
99111
}
100112

101113
// Hides the touch target on lower densities.

0 commit comments

Comments
 (0)