Skip to content

Commit

Permalink
fix(datepicker): retain focus on navigation links (#3381)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
peterblazejewicz authored and maxokorokov committed Oct 21, 2019
1 parent 72f1206 commit 7a584ad
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
26 changes: 22 additions & 4 deletions src/datepicker/datepicker-navigation.spec.ts
Expand Up @@ -92,17 +92,35 @@ describe('ngb-datepicker-navigation', () => {
it('should send navigation events', () => {
const fixture =
createTestComponent(`<ngb-datepicker-navigation (navigate)="onNavigate($event)"></ngb-datepicker-navigation>`);

const links = getNavigationLinks(fixture.nativeElement);
const[previousButton, nextButton] = getNavigationLinks(fixture.nativeElement);
const previousButtonSpan = previousButton.querySelector<HTMLElement>('span');
const nextButtonSpan = nextButton.querySelector<HTMLElement>('span');
spyOn(fixture.componentInstance, 'onNavigate');

// prev
links[0].click();
previousButton.click();
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.PREV);
previousButtonSpan.click();
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.PREV);

// next
links[1].click();
nextButton.click();
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.NEXT);
nextButtonSpan.click();
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith(NavigationEvent.NEXT);
});

it('should retain focus on the navigation links after click', () => {
const fixture = createTestComponent(`<ngb-datepicker-navigation></ngb-datepicker-navigation>`);
const[previousButton, nextButton] = getNavigationLinks(fixture.nativeElement);

// prev
previousButton.click();
expect(document.activeElement).toBe(previousButton);

// next
nextButton.click();
expect(document.activeElement).toBe(nextButton);
});

it('should have buttons of type button', () => {
Expand Down
14 changes: 12 additions & 2 deletions src/datepicker/datepicker-navigation.ts
Expand Up @@ -10,7 +10,7 @@ import {NgbDatepickerI18n} from './datepicker-i18n';
styleUrls: ['./datepicker-navigation.scss'],
template: `
<div class="ngb-dp-arrow">
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="navigate.emit(navigation.PREV)" [disabled]="prevDisabled"
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="onClickPrev($event)" [disabled]="prevDisabled"
i18n-aria-label="@@ngb.datepicker.previous-month" aria-label="Previous month"
i18n-title="@@ngb.datepicker.previous-month" title="Previous month">
<span class="ngb-dp-navigation-chevron"></span>
Expand All @@ -32,7 +32,7 @@ import {NgbDatepickerI18n} from './datepicker-i18n';
<div class="ngb-dp-arrow" *ngIf="i !== months.length - 1"></div>
</ng-template>
<div class="ngb-dp-arrow right">
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="navigate.emit(navigation.NEXT)" [disabled]="nextDisabled"
<button type="button" class="btn btn-link ngb-dp-arrow-btn" (click)="onClickNext($event)" [disabled]="nextDisabled"
i18n-aria-label="@@ngb.datepicker.next-month" aria-label="Next month"
i18n-title="@@ngb.datepicker.next-month" title="Next month">
<span class="ngb-dp-navigation-chevron"></span>
Expand All @@ -55,4 +55,14 @@ export class NgbDatepickerNavigation {
@Output() select = new EventEmitter<NgbDate>();

constructor(public i18n: NgbDatepickerI18n) {}

onClickPrev(event: MouseEvent) {
(event.currentTarget as HTMLElement).focus();
this.navigate.emit(this.navigation.PREV);
}

onClickNext(event: MouseEvent) {
(event.currentTarget as HTMLElement).focus();
this.navigate.emit(this.navigation.NEXT);
}
}

0 comments on commit 7a584ad

Please sign in to comment.