Skip to content

Commit 5cc2417

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 4d97271 commit 5cc2417

File tree

8 files changed

+44
-23
lines changed

8 files changed

+44
-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: 23 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';
@@ -171,6 +171,9 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
171171
/** Whether the drawer is opened. */
172172
private _opened: boolean = false;
173173

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

@@ -202,7 +205,9 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
202205

203206
constructor(private _elementRef: ElementRef,
204207
private _focusTrapFactory: FocusTrapFactory,
208+
private _focusMonitor: FocusMonitor,
205209
@Optional() @Inject(DOCUMENT) private _doc: any) {
210+
206211
this.onOpen.subscribe(() => {
207212
if (this._doc) {
208213
this._elementFocusedBeforeDrawerWasOpened = this._doc.activeElement as HTMLElement;
@@ -221,16 +226,18 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
221226
* opened.
222227
*/
223228
private _restoreFocus() {
224-
let activeEl = this._doc && this._doc.activeElement;
229+
const activeEl = this._doc && this._doc.activeElement;
230+
225231
if (activeEl && this._elementRef.nativeElement.contains(activeEl)) {
226232
if (this._elementFocusedBeforeDrawerWasOpened instanceof HTMLElement) {
227-
this._elementFocusedBeforeDrawerWasOpened.focus();
233+
this._focusMonitor.focusVia(this._elementFocusedBeforeDrawerWasOpened, this._openedVia);
228234
} else {
229235
this._elementRef.nativeElement.blur();
230236
}
231237
}
232238

233239
this._elementFocusedBeforeDrawerWasOpened = null;
240+
this._openedVia = null;
234241
}
235242

236243
ngAfterContentInit() {
@@ -255,10 +262,13 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
255262
this.toggle(coerceBooleanProperty(v));
256263
}
257264

258-
259-
/** Open the drawer. */
260-
open(): Promise<MatDrawerToggleResult> {
261-
return this.toggle(true);
265+
/**
266+
* Open the drawer.
267+
* @param openedVia Whether the drawer was opened by a key press, mouse click or programmatically.
268+
* Used for focus management after the sidenav is closed.
269+
*/
270+
open(openedVia?: FocusOrigin): Promise<MatDrawerToggleResult> {
271+
return this.toggle(true, openedVia);
262272
}
263273

264274
/** Close the drawer. */
@@ -269,12 +279,17 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
269279
/**
270280
* Toggle this drawer.
271281
* @param isOpen Whether the drawer should be open.
282+
* @param openedVia Whether the drawer was opened by a key press, mouse click or programmatically.
283+
* Used for focus management after the sidenav is closed.
272284
*/
273-
toggle(isOpen: boolean = !this.opened): Promise<MatDrawerToggleResult> {
285+
toggle(isOpen: boolean = !this.opened, openedVia: FocusOrigin = 'program'):
286+
Promise<MatDrawerToggleResult> {
287+
274288
this._opened = isOpen;
275289

276290
if (isOpen) {
277291
this._animationState = this._enableAnimations ? 'open' : 'open-instant';
292+
this._openedVia = openedVia;
278293
} else {
279294
this._animationState = 'void';
280295
}

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)