Skip to content

Commit bd52b35

Browse files
committed
fix(button): focus styles not applied to programmatically focused buttons
Currently the tint that is added on top of focused buttons won't show up if the button is focused programmatically, which means that the button won't appear focused in cases like the dialog closing and restoring focus to its trigger. This seems to have been introduced by 5d6920d. Fixes #7510.
1 parent 9b07712 commit bd52b35

File tree

8 files changed

+43
-23
lines changed

8 files changed

+43
-23
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@
7373
<button mat-raised-button (click)="button1.focus()">Focus 1</button>
7474
<button mat-raised-button (click)="button2.focus()">Focus 2</button>
7575
<button mat-raised-button (click)="button3.focus()">Focus 3</button>
76+
<button mat-raised-button (click)="button4.focus()">Focus 4</button>
7677
</div>
7778
<button mat-button #button1 [disabled]="isDisabled" (click)="clickCounter=clickCounter+1">off</button>
78-
<button mat-button color="primary" [disabled]="isDisabled">off</button>
79-
<a href="http://www.google.com" #button2 mat-button color="accent" [disabled]="isDisabled">off</a>
80-
<button mat-raised-button #button3 color="primary" [disabled]="isDisabled">off</button>
79+
<button mat-button #button2 color="primary" [disabled]="isDisabled">off</button>
80+
<a href="http://www.google.com" #button3 mat-button color="accent" [disabled]="isDisabled">off</a>
81+
<button mat-raised-button #button4 color="primary" [disabled]="isDisabled">off</button>
8182
<button mat-mini-fab [disabled]="isDisabled">
8283
<mat-icon>check</mat-icon>
8384
</button>

src/demo-app/demo-app/demo-app.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</mat-sidenav>
2525
<div>
2626
<mat-toolbar color="primary">
27-
<button mat-icon-button (click)="start.open()">
27+
<button mat-icon-button (click)="start.open('mouse')">
2828
<mat-icon>menu</mat-icon>
2929
</button>
3030
<div class="demo-toolbar">

src/demo-app/sidenav/sidenav-demo.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
[fixedInViewport]="fixed" [fixedTopGap]="fixedTop" [fixedBottomGap]="fixedBottom">
1010
Start Side Sidenav
1111
<br>
12-
<button mat-button (click)="start.close()">Close</button>
12+
<button mat-button (click)="start.close('mouse')">Close</button>
1313
<br>
14-
<button mat-button (click)="end.open()">Open End Side</button>
14+
<button mat-button (click)="end.open('keyboard')">Open End Side</button>
1515
<br>
1616
<button mat-button (click)="modeIndex = (modeIndex + 1) % 3">Toggle Mode</button>
1717
<div>Mode: {{start.mode}}</div>
@@ -24,7 +24,7 @@
2424
[fixedInViewport]="fixed" [fixedTopGap]="fixedTop" [fixedBottomGap]="fixedBottom">
2525
End Side Sidenav
2626
<br>
27-
<button mat-button (click)="end.close()">Close</button>
27+
<button mat-button (click)="end.close('mouse')">Close</button>
2828
<div class="demo-filler-content" *ngFor="let c of fillerContent">Filler Content</div>
2929
</mat-sidenav>
3030

@@ -39,8 +39,8 @@
3939

4040
<div>
4141
<h3>Sidenav</h3>
42-
<button mat-button (click)="start.toggle()">Toggle Start Side Sidenav</button>
43-
<button mat-button (click)="end.toggle()">Toggle End Side Sidenav</button>
42+
<button mat-button (click)="start.toggle(undefined, 'mouse')">Toggle Start Side Sidenav</button>
43+
<button mat-button (click)="end.toggle(undefined, 'mouse')">Toggle End Side Sidenav</button>
4444
<mat-checkbox [(ngModel)]="fixed">Fixed mode</mat-checkbox>
4545
<mat-checkbox [(ngModel)]="coverHeader">Sidenav covers header/footer</mat-checkbox>
4646
</div>

src/lib/button-toggle/button-toggle.scss

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,13 @@ $mat-button-toggle-border-radius: 2px !default;
3838
.mat-button-toggle {
3939
white-space: nowrap;
4040
position: relative;
41-
}
4241

43-
.mat-button-toggle.cdk-keyboard-focused .mat-button-toggle-focus-overlay {
44-
opacity: 1;
42+
&.cdk-keyboard-focused,
43+
&.cdk-program-focused {
44+
.mat-button-toggle-focus-overlay {
45+
opacity: 1;
46+
}
47+
}
4548
}
4649

4750
.mat-button-toggle-label-content {

src/lib/button/_button-base.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ $mat-mini-fab-padding: 8px !default;
5151
cursor: default;
5252
}
5353

54-
&.cdk-keyboard-focused {
54+
&.cdk-keyboard-focused, &.cdk-program-focused {
5555
.mat-button-focus-overlay {
5656
opacity: 1;
5757
}

src/lib/datepicker/_datepicker-theme.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ $mat-calendar-weekday-table-font-size: 11px !default;
5252
}
5353

5454
:not(.mat-calendar-body-disabled):hover,
55-
.cdk-keyboard-focused .mat-calendar-body-active {
55+
.cdk-keyboard-focused .mat-calendar-body-active,
56+
.cdk-program-focused .mat-calendar-body-active {
5657
& > .mat-calendar-body-cell-content:not(.mat-calendar-body-selected) {
5758
background-color: mat-color($background, hover);
5859
}

src/lib/sidenav/drawer.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {animate, AnimationEvent, state, style, transition, trigger} from '@angular/animations';
10-
import {FocusTrap, FocusTrapFactory} from '@angular/cdk/a11y';
10+
import {FocusTrap, FocusTrapFactory, FocusMonitor, FocusOrigin} from '@angular/cdk/a11y';
1111
import {Directionality} from '@angular/cdk/bidi';
1212
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1313
import {ESCAPE} from '@angular/cdk/keycodes';
@@ -172,6 +172,9 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
172172
/** Whether the drawer is opened. */
173173
private _opened: boolean = false;
174174

175+
/** How the sidenav was opened (keypress, mouse click etc.) */
176+
private _openedVia: FocusOrigin | null;
177+
175178
/** Emits whenever the drawer has started animating. */
176179
_animationStarted = new EventEmitter<AnimationEvent>();
177180

@@ -230,6 +233,7 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
230233

231234
constructor(private _elementRef: ElementRef,
232235
private _focusTrapFactory: FocusTrapFactory,
236+
private _focusMonitor: FocusMonitor,
233237
@Optional() @Inject(DOCUMENT) private _doc: any) {
234238
this.openedChange.subscribe((opened: boolean) => {
235239
if (opened) {
@@ -251,16 +255,18 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
251255
* opened.
252256
*/
253257
private _restoreFocus() {
254-
let activeEl = this._doc && this._doc.activeElement;
258+
const activeEl = this._doc && this._doc.activeElement;
259+
255260
if (activeEl && this._elementRef.nativeElement.contains(activeEl)) {
256261
if (this._elementFocusedBeforeDrawerWasOpened instanceof HTMLElement) {
257-
this._elementFocusedBeforeDrawerWasOpened.focus();
262+
this._focusMonitor.focusVia(this._elementFocusedBeforeDrawerWasOpened, this._openedVia);
258263
} else {
259264
this._elementRef.nativeElement.blur();
260265
}
261266
}
262267

263268
this._elementFocusedBeforeDrawerWasOpened = null;
269+
this._openedVia = null;
264270
}
265271

266272
ngAfterContentInit() {
@@ -285,10 +291,13 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
285291
this.toggle(coerceBooleanProperty(v));
286292
}
287293

288-
289-
/** Open the drawer. */
290-
open(): Promise<MatDrawerToggleResult> {
291-
return this.toggle(true);
294+
/**
295+
* Open the drawer.
296+
* @param openedVia Whether the drawer was opened by a key press, mouse click or programmatically.
297+
* Used for focus management after the sidenav is closed.
298+
*/
299+
open(openedVia?: FocusOrigin): Promise<MatDrawerToggleResult> {
300+
return this.toggle(true, openedVia);
292301
}
293302

294303
/** Close the drawer. */
@@ -299,12 +308,17 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
299308
/**
300309
* Toggle this drawer.
301310
* @param isOpen Whether the drawer should be open.
311+
* @param openedVia Whether the drawer was opened by a key press, mouse click or programmatically.
312+
* Used for focus management after the sidenav is closed.
302313
*/
303-
toggle(isOpen: boolean = !this.opened): Promise<MatDrawerToggleResult> {
314+
toggle(isOpen: boolean = !this.opened, openedVia: FocusOrigin = 'program'):
315+
Promise<MatDrawerToggleResult> {
316+
304317
this._opened = isOpen;
305318

306319
if (isOpen) {
307320
this._animationState = this._enableAnimations ? 'open' : 'open-instant';
321+
this._openedVia = openedVia;
308322
} else {
309323
this._animationState = 'void';
310324
}

src/lib/slider/slider.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ $mat-slider-focus-ring-size: 30px !default;
9393
background-color $swift-ease-out-duration $swift-ease-out-timing-function,
9494
opacity $swift-ease-out-duration $swift-ease-out-timing-function;
9595

96-
.cdk-keyboard-focused & {
96+
.cdk-keyboard-focused &,
97+
.cdk-program-focused & {
9798
transform: scale(1);
9899
opacity: 1;
99100
}

0 commit comments

Comments
 (0)