Skip to content

Commit 7a584ad

Browse files
peterblazejewiczmaxokorokov
authored andcommitted
fix(datepicker): retain focus on navigation links (#3381)
This fixes an issue on some clients due to browser being inconsistent on handling focus on the button clicks. After the change the navigation button within the datepicker will retain focus to allow subsequent keyboard based navigation within datepicker instance. Fixes: #2780
1 parent 72f1206 commit 7a584ad

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

src/datepicker/datepicker-navigation.spec.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,35 @@ describe('ngb-datepicker-navigation', () => {
9292
it('should send navigation events', () => {
9393
const fixture =
9494
createTestComponent(`<ngb-datepicker-navigation (navigate)="onNavigate($event)"></ngb-datepicker-navigation>`);
95-
96-
const links = getNavigationLinks(fixture.nativeElement);
95+
const[previousButton, nextButton] = getNavigationLinks(fixture.nativeElement);
96+
const previousButtonSpan = previousButton.querySelector<HTMLElement>('span');
97+
const nextButtonSpan = nextButton.querySelector<HTMLElement>('span');
9798
spyOn(fixture.componentInstance, 'onNavigate');
9899

99100
// prev
100-
links[0].click();
101+
previousButton.click();
102+
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.PREV);
103+
previousButtonSpan.click();
101104
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.PREV);
102105

103106
// next
104-
links[1].click();
107+
nextButton.click();
105108
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.NEXT);
109+
nextButtonSpan.click();
110+
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.NEXT);
111+
});
112+
113+
it('should retain focus on the navigation links after click', () => {
114+
const fixture = createTestComponent(`<ngb-datepicker-navigation></ngb-datepicker-navigation>`);
115+
const[previousButton, nextButton] = getNavigationLinks(fixture.nativeElement);
116+
117+
// prev
118+
previousButton.click();
119+
expect(document.activeElement).toBe(previousButton);
120+
121+
// next
122+
nextButton.click();
123+
expect(document.activeElement).toBe(nextButton);
106124
});
107125

108126
it('should have buttons of type button', () => {

src/datepicker/datepicker-navigation.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {NgbDatepickerI18n} from './datepicker-i18n';
1010
styleUrls: ['./datepicker-navigation.scss'],
1111
template: `
1212
<div class="ngb-dp-arrow">
13-
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="navigate.emit(navigation.PREV)" [disabled]="prevDisabled"
13+
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="onClickPrev($event)" [disabled]="prevDisabled"
1414
i18n-aria-label="@@ngb.datepicker.previous-month" aria-label="Previous month"
1515
i18n-title="@@ngb.datepicker.previous-month" title="Previous month">
1616
<span class="ngb-dp-navigation-chevron"></span>
@@ -32,7 +32,7 @@ import {NgbDatepickerI18n} from './datepicker-i18n';
3232
<div class="ngb-dp-arrow" *ngIf="i !== months.length - 1"></div>
3333
</ng-template>
3434
<div class="ngb-dp-arrow right">
35-
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="navigate.emit(navigation.NEXT)" [disabled]="nextDisabled"
35+
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="onClickNext($event)" [disabled]="nextDisabled"
3636
i18n-aria-label="@@ngb.datepicker.next-month" aria-label="Next month"
3737
i18n-title="@@ngb.datepicker.next-month" title="Next month">
3838
<span class="ngb-dp-navigation-chevron"></span>
@@ -55,4 +55,14 @@ export class NgbDatepickerNavigation {
5555
@Output() select = new EventEmitter<NgbDate>();
5656

5757
constructor(public i18n: NgbDatepickerI18n) {}
58+
59+
onClickPrev(event: MouseEvent) {
60+
(event.currentTarget as HTMLElement).focus();
61+
this.navigate.emit(this.navigation.PREV);
62+
}
63+
64+
onClickNext(event: MouseEvent) {
65+
(event.currentTarget as HTMLElement).focus();
66+
this.navigate.emit(this.navigation.NEXT);
67+
}
5868
}

0 commit comments

Comments
 (0)