Skip to content

Calendar keyboard navigation improvements #8314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9c93344
feat(IgxMonthView): add one tab stop in the month view #6275
ddincheva Oct 1, 2020
aadcf0a
feat(IgxYearsView): add tabIndex to years view elements #6275
ddincheva Oct 1, 2020
ab5ada7
chore(*): remove tabIndex from day-item component
ddincheva Oct 2, 2020
5a5ddfe
feat(*): reset tab Index when navigationg out of the calendar #6275
ddincheva Oct 4, 2020
3be5074
Merge branch 'master' of https://github.com/IgniteUI/igniteui-angular…
ddincheva Oct 6, 2020
345fb9d
feat(DateItem): update activeDate on focus #6275
ddincheva Oct 7, 2020
93a2aff
feat(Calendar): recalc activeDate when views are changed #6275
ddincheva Oct 7, 2020
7084415
chore(*): remove leftover code
ddincheva Oct 7, 2020
57c73ca
chore(*): return nav service to initial state
ddincheva Oct 7, 2020
9bf6560
feat(Calendar): remove tabIndex from the component itself #6275
ddincheva Oct 7, 2020
cc749c4
Merge branch 'master' of https://github.com/IgniteUI/igniteui-angular…
ddincheva Oct 8, 2020
4c66da6
chore(*): clear up some leftover logic
ddincheva Oct 8, 2020
35f3787
test(IgxCalendar): verify that tabIndex is applied correctly #6275
ddincheva Oct 8, 2020
ea169d1
chore(*): fix linting errors
ddincheva Oct 8, 2020
af50200
Merge branch 'master' into ddincheva/calendarKB
zdrawku Oct 8, 2020
618e8ff
Merge branch 'master' into ddincheva/calendarKB
zdrawku Oct 9, 2020
a618947
fix(YearsView): navigate correctly in the years view #6275
ddincheva Oct 9, 2020
9a49f9b
Merge branch 'ddincheva/calendarKB' of https://github.com/IgniteUI/ig…
ddincheva Oct 9, 2020
60e4b2a
chore(*): clear useless code
ddincheva Oct 9, 2020
b860ae8
Merge branch 'master' of https://github.com/IgniteUI/igniteui-angular…
ddincheva Oct 9, 2020
e55e390
fix(Calendar): reset the date only when the days view looses focus #6275
ddincheva Oct 9, 2020
3db55a7
Merge branch 'master' of https://github.com/IgniteUI/igniteui-angular…
ddincheva Oct 9, 2020
3d62f06
chore(*): update the changelog
ddincheva Oct 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ All notable changes for each version of this project will be documented in this
- `onOpening` and `onClosing` events are emitting now arguments of `ToggleViewCancelableEventArgs` type.
- `IgxSelect`
- Added `aria-labelledby` property for the items list container(marked as `role="listbox"`). This will ensure the users of assistive technologies will also know what the list items container is used for, upon opening.
- `IgxDatePicker`
- `IgxDatePicker`
- **Breaking Change** - Deprecated the `label` property.
- Added `aria-labelledby` property for the input field. This will ensure the users of assistive technologies will also know what component is used for, upon input focus.
- `igxNavigationDrawer`
- Added `disableAnimation` property which enables/disables the animation, when toggling the drawer. Set to `false` by default.
- `igxTabs`
- Added `disableAnimation` property which enables/disables the transition animation of the tabs' content. Set to `false` by default.
- `IgxExpansionPanel`
- `IExpansionPanelEventArgs.panel` - Deprecated. Usе `owner` property to get a reference to the panel.
- `IExpansionPanelEventArgs.panel` - Deprecated. Usе `owner` property to get a reference to the panel.
- `IgxCalendarComponent`, `IgxMonthsViewComponent` and `IgxYearsViewComponent`
- `tabIndex` property was removed in order to improve on page navigation and to be compliant with W3 accessability recommendations; Also the date grid in the calendar is now only one tab stop, the same approach is applied and in the `IgxMonthsViewComponent` and `IgxYearsViewComponent`;

### New Features
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,45 @@ describe('Multi-View Calendar - ', () => {
HelperTestFunctions.verifyCalendarSubHeaders(fixture, [march2020, april2020, may2020]);
}));

it('Verify tabindex is correct when navigating with arrow keys', fakeAsync(() => {
calendar.hideOutsideDays = true;
fixture.detectChanges();

let dates = HelperTestFunctions.getMonthViewDates(fixture, 0);
UIInteractions.simulateClickEvent(dates[0]);
fixture.detectChanges();
tick();

UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', dates[0]);
fixture.detectChanges();
tick();
dates = HelperTestFunctions.getMonthViewDates(fixture, 0);
expect(document.activeElement).toEqual(dates[7]);
expect(dates[7].tabIndex).toBe(0);

UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', dates[7]);
fixture.detectChanges();
tick();
dates = HelperTestFunctions.getMonthViewDates(fixture, 0);
expect(document.activeElement).toEqual(dates[0]);
expect(dates[0].tabIndex).toBe(0);

UIInteractions.triggerKeyDownEvtUponElem('ArrowRight', dates[0]);
fixture.detectChanges();
tick();
dates = HelperTestFunctions.getMonthViewDates(fixture, 0);
expect(document.activeElement).toEqual(dates[1]);
expect(dates[1].tabIndex).toBe(0);

UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', dates[1]);
fixture.detectChanges();
tick();
dates = HelperTestFunctions.getMonthViewDates(fixture, 0);
expect(document.activeElement).toEqual(dates[0]);
expect(dates[0].tabIndex).toBe(0);
}));


it('Verify navigation with pageUp', fakeAsync(() => {
let monthDates = HelperTestFunctions.getMonthViewDates(fixture, 1);
UIInteractions.simulateClickEvent(monthDates[16]);
Expand Down Expand Up @@ -694,17 +733,19 @@ describe('Multi-View Calendar - ', () => {
fixture.detectChanges();
inactiveDates = HelperTestFunctions.getMonthViewInactiveDates(fixture, 0);
inactiveDates.forEach(date => {
expect(date.tabIndex).toEqual(0);
expect(date.tabIndex).toEqual(-1);
});

monthDates = HelperTestFunctions.getMonthViewDates(fixture, 0);
for (let index = 6; index < 14; index++) {
expect(monthDates[index].tabIndex).toEqual(0);
expect(monthDates[index].tabIndex).toEqual(-1);

}

expect(monthDates[0].tabIndex).toEqual(0);
}));

it('Verify navigation with Home and End keys', fakeAsync(() => {
it('Verify navigation with Home and End keys and check the tabindex', fakeAsync(() => {
let monthDates = HelperTestFunctions.getMonthViewDates(fixture, 1);
UIInteractions.simulateClickEvent(monthDates[16]);
fixture.detectChanges();
Expand All @@ -716,13 +757,17 @@ describe('Multi-View Calendar - ', () => {

monthDates = HelperTestFunctions.getMonthViewDates(fixture, 0);
expect(document.activeElement).toEqual(monthDates[0]);
expect(monthDates[0].tabIndex).toEqual(0);


UIInteractions.triggerKeyDownEvtUponElem('End', monthDates[0], true);
fixture.detectChanges();
tick();

monthDates = HelperTestFunctions.getMonthViewDates(fixture, 2);
expect(document.activeElement).toEqual(monthDates[monthDates.length - 1]);
expect(monthDates[monthDates.length - 1].tabIndex).toEqual(0);

}));

it('Verify navigation with Home and End keys when there are disabled dates', fakeAsync(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ <h2 class="igx-calendar__header-date">
[selection]="selection"
[locale]="locale"
[value]="value"
[(activeDate)]="activeDate"
[viewDate]="i | IgxGetViewDate:viewDate"
[weekStart]="weekStart"
[formatOptions]="formatOptions"
Expand All @@ -59,7 +60,8 @@ <h2 class="igx-calendar__header-date">
[hideOutsideDays]="hideOutsideDays"
[showWeekNumbers]="showWeekNumbers"
(onViewChanging)="viewChanging($event)"
(onDateSelection)="childClicked($event)">
(onDateSelection)="childClicked($event)"
(monthsViewBlur)="resetActiveDate()">
</igx-days-view>
</div>
</div>
Expand Down
40 changes: 27 additions & 13 deletions projects/igniteui-angular/src/lib/calendar/calendar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,6 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
*/
public callback: (next) => void;

/**
* The default `tabindex` attribute for the component.
*
* @hidden
* @internal
*/
@HostBinding('attr.tabindex')
public tabindex = 0;

/**
* The default aria role attribute for the component.
*
Expand Down Expand Up @@ -402,6 +393,12 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
return this.selectedDates ? this.selectedDates : new Date();
}

/**
* @hidden
* @internal
*/
public activeDate = new Date().toLocaleDateString();

/**
* @hidden
* @internal
Expand Down Expand Up @@ -492,10 +489,10 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
*/
public nextMonth(isKeydownTrigger = false) {
if (isKeydownTrigger && this.animationAction === 'prev') { return; }
this.isKeydownTrigger = isKeydownTrigger;
this.previousViewDate = this.viewDate;
this.viewDate = this.calendarModel.getNextMonth(this.viewDate);
this.animationAction = ScrollMonth.NEXT;
this.isKeydownTrigger = isKeydownTrigger;
}

/**
Expand Down Expand Up @@ -556,7 +553,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
requestAnimationFrame(() => {
if (this.dacadeView) {
this.dacadeView.date = args;
this.dacadeView.el.nativeElement.focus();
this.dacadeView.calendarDir.find(date => date.isCurrentYear).nativeElement.focus();
}
});
}
Expand All @@ -571,7 +568,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
requestAnimationFrame(() => {
if (this.dacadeView) {
this.dacadeView.date = args;
this.dacadeView.el.nativeElement.focus();
this.dacadeView.calendarDir.find(date => date.isCurrentYear).nativeElement.focus();
}
});
}
Expand Down Expand Up @@ -732,6 +729,9 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
(event.fromState === 'void' && event.toState === ScrollMonth.NONE)) {
this.viewDateChanged.emit({ previousValue: this.previousViewDate, currentValue: this.viewDate });
}
if (!this.isKeydownTrigger) {
this.resetActiveDate();
}

if (this.monthScrollDirection !== ScrollMonth.NONE) {
this.scrollMonth$.next();
Expand Down Expand Up @@ -768,6 +768,21 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
}
}

/**
* @hidden
* @internal
*/
public resetActiveDate() {
if (!this.monthViews) { return; }
let dates = [];
this.monthViews.map(mv => mv.dates).forEach(days => { dates = dates.concat(days.toArray()); });
const date = dates.find(day => day.selected && day.isCurrentMonth) || dates.find(day => day.isToday && day.isCurrentMonth)
|| dates.find(d => d.isFocusable);
if (date) {
this.activeDate = date.date.date.toLocaleDateString();
}
}

/**
* Keyboard navigation of the calendar
* @hidden
Expand All @@ -781,7 +796,6 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
return;
}


const isPageDown = event.key === 'PageDown';
const step = isPageDown ? 1 : -1;
let monthView = this.daysView as IgxDaysViewComponent;
Expand Down
15 changes: 12 additions & 3 deletions projects/igniteui-angular/src/lib/calendar/calendar.directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,22 @@ export class IgxCalendarYearDirective {
return this.isCurrentYear;
}

@HostBinding('attr.tabindex')
public get tabIndex(): number {
return this.isCurrentYear ? 0 : -1;
}


public get isCurrentYear(): boolean {
return this.date.getFullYear() === this.value.getFullYear();
}

public get nativeElement() {
return this.elementRef.nativeElement;
}

constructor(public elementRef: ElementRef) {}

@HostListener('click')
public onClick() {
this.onYearSelection.emit(this.value);
Expand All @@ -75,9 +87,6 @@ export class IgxCalendarMonthDirective {
@Output()
public onMonthSelection = new EventEmitter<Date>();

@HostBinding('attr.tabindex')
public tabindex = 0;

@HostBinding('class.igx-calendar__month')
public get defaultCSS(): boolean {
return !this.isCurrentMonth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,14 @@ export class IgxDayItemComponent {
return this.selection !== CalendarSelection.RANGE;
}

@HostBinding('attr.tabindex')
public get tabindex(): number {
return this.isDisabled || this.isHidden ? -1 : 0;
}

private _selected = false;

constructor(private elementRef: ElementRef) { }

@HostListener('click')
@HostListener('keydown.enter')
public onSelect() {
@HostListener('click', ['$event'])
@HostListener('keydown.enter', ['$event'])
public onSelect(event) {
event.stopPropagation();
this.onDateSelection.emit(this.date);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
[specialDates]="specialDates"
[outOfRangeDates]="outOfRangeDates"
[hideOutsideDays]="hideOutsideDays"
[attr.tabindex]="tabIndex(day)"
(focus)="activeDate = day.date.toLocaleDateString()"
(onDateSelection)="selectDay($event)">
{{ formattedDate(day.date) }}
</igx-day-item>
Expand Down
Loading