diff --git a/projects/angular/src/data/datagrid/datagrid-virtual-scroll.directive.spec.ts b/projects/angular/src/data/datagrid/datagrid-virtual-scroll.directive.spec.ts index 002616e5a1..03c739c22c 100644 --- a/projects/angular/src/data/datagrid/datagrid-virtual-scroll.directive.spec.ts +++ b/projects/angular/src/data/datagrid/datagrid-virtual-scroll.directive.spec.ts @@ -18,6 +18,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { animationFrameScheduler, BehaviorSubject, Observable } from 'rxjs'; import { ClarityModule } from '../../clr-angular.module'; +import { Keys } from '../../utils/enums/keys.enum'; import { ClrDatagridVirtualScrollDirective } from './datagrid-virtual-scroll.directive'; import { DATAGRID_SPEC_PROVIDERS } from './helpers.spec'; @@ -221,18 +222,18 @@ export default function (): void { expect(document.activeElement).toBe(headerCheckboxCell); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'PageDown' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.PageDown })); // active checkbox input with ID clr-dg-row-cb364 expect(document.activeElement).toBe(grid.querySelectorAll('[type=checkbox]')[22]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'PageDown' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.PageDown })); sleep(); fixture.whenStable(); fixture.whenRenderingDone(); // active checkbox input with ID clr-dg-row-cb383 expect(document.activeElement).toBe(grid.querySelectorAll('[type=checkbox]')[41]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'PageUp' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.PageUp })); sleep(); fixture.whenStable(); fixture.whenRenderingDone(); diff --git a/projects/angular/src/data/datagrid/datagrid.spec.ts b/projects/angular/src/data/datagrid/datagrid.spec.ts index 03d2d8618d..0df8d32a93 100644 --- a/projects/angular/src/data/datagrid/datagrid.spec.ts +++ b/projects/angular/src/data/datagrid/datagrid.spec.ts @@ -897,20 +897,34 @@ export default function (): void { const grid = context.clarityElement.querySelector('[role=grid]'); expect(grid).toBeDefined(); const cells = grid.querySelectorAll('[role=gridcell], [role=columnheader]'); - expect(cells.length).toBe(12); // 3*2 data, 3 select radios, 3 headers + expect(cells.length).toBe(12); + // data matrix 3*2 data, 3 select radios, 3 headers. Legend: 0h -> Index: 0, Type: header + // |0h| 1h| 2h| + // |3r| 4d| 5d| + // |6r| 7d| 8d| + // |9r|10d|11d| + // cell flow: start at index 0 -> 3 -> 4 (check) -> 5 (check) -> 8 (check)-> 7 (check)-> 4 (check) end + // need to start with this cell exactly, because it has tabindex=0 cells[0].focus(); expect(document.activeElement).toBe(cells[0]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowRight })); + + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowRight })); + expect(document.activeElement).toBe(cells[4]); + // second time, to avoid cycling over cells with radios - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowRight })); - expect(document.activeElement).toBe(cells[2]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowRight })); expect(document.activeElement).toBe(cells[5]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowLeft })); + + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); + expect(document.activeElement).toBe(cells[8]); + + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowLeft })); + expect(document.activeElement).toBe(cells[7]); + + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowUp })); expect(document.activeElement).toBe(cells[4]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowUp })); - expect(document.activeElement).toBe(cells[1]); }); it('Moves focus to inner actionable element', function () { @@ -918,7 +932,7 @@ export default function (): void { const cells = grid.querySelectorAll('[role=gridcell], [role=columnheader]'); cells[0].focus(); expect(document.activeElement).toBe(cells[0]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); expect(document.activeElement).toBe(cells[3].querySelector('[type=radio]')); }); @@ -927,12 +941,12 @@ export default function (): void { const cells = grid.querySelectorAll('[role=gridcell], [role=columnheader]'); cells[0].focus(); expect(document.activeElement).toBe(cells[0]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowDown })); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowDown })); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); expect(document.activeElement).toBe(cells[9].querySelector('[type=radio]')); // we're at the edge, then we click once more to get to the placeholder - grid.dispatchEvent(new KeyboardEvent('keydown', { code: Keys.ArrowDown })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.ArrowDown })); expect(document.activeElement).toBe(cells[9].querySelector('[type=radio]')); }); @@ -954,11 +968,11 @@ export default function (): void { expect(document.activeElement).toBe(cells[0]); // focus at bottom datagrid radio input - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'PageDown' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.PageDown })); expect(document.activeElement).toBe(cells[9].querySelector('[type=radio]')); // focus at top datagrid radio input - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'PageUp' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.PageUp })); expect(document.activeElement).toBe(cells[3].querySelector('[type=radio]')); }); @@ -967,9 +981,9 @@ export default function (): void { const cells = grid.querySelectorAll('[role=gridcell], [role=columnheader]'); cells[0].focus(); expect(document.activeElement).toBe(cells[0]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'End' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.End })); expect(document.activeElement).toBe(cells[2]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'Home' })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.Home })); expect(document.activeElement).toBe(cells[0]); }); @@ -978,9 +992,9 @@ export default function (): void { const cells = grid.querySelectorAll('[role=gridcell], [role=columnheader]'); cells[0].focus(); expect(document.activeElement).toBe(cells[0]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'End', ctrlKey: true })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.End, ctrlKey: true })); expect(document.activeElement).toBe(cells[11]); - grid.dispatchEvent(new KeyboardEvent('keydown', { code: 'Home', ctrlKey: true })); + grid.dispatchEvent(new KeyboardEvent('keydown', { key: Keys.Home, ctrlKey: true })); expect(document.activeElement).toBe(cells[0]); }); }); diff --git a/projects/angular/src/data/datagrid/utils/key-navigation-grid.controller.ts b/projects/angular/src/data/datagrid/utils/key-navigation-grid.controller.ts index 4cf5ec3960..9a6411ec63 100644 --- a/projects/angular/src/data/datagrid/utils/key-navigation-grid.controller.ts +++ b/projects/angular/src/data/datagrid/utils/key-navigation-grid.controller.ts @@ -9,6 +9,8 @@ import { Injectable, NgZone, OnDestroy } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { Keys } from '../../../utils/enums/keys.enum'; + export function getTabableItems(el: HTMLElement) { const tabableSelector = [ 'a[href]', @@ -103,19 +105,19 @@ export class KeyNavigationGridController implements OnDestroy { // Skip column resize events if ( (e.target as HTMLElement).classList.contains('drag-handle') && - (e.code === 'ArrowLeft' || e.code === 'ArrowRight') + (e.key === Keys.ArrowLeft || e.key === Keys.ArrowRight) ) { return; } if ( - e.code === 'ArrowUp' || - e.code === 'ArrowDown' || - e.code === 'ArrowLeft' || - e.code === 'ArrowRight' || - e.code === 'End' || - e.code === 'Home' || - e.code === 'PageUp' || - e.code === 'PageDown' + e.key === Keys.ArrowUp || + e.key === Keys.ArrowDown || + e.key === Keys.ArrowLeft || + e.key === Keys.ArrowRight || + e.key === Keys.End || + e.key === Keys.Home || + e.key === Keys.PageUp || + e.key === Keys.PageDown ) { const { x, y } = this.getNextItemCoordinate(e); const activeItem = this.rows @@ -171,7 +173,7 @@ export class KeyNavigationGridController implements OnDestroy { private getNextItemCoordinate(e: any) { let currentCell = this.cells ? Array.from(this.cells).find(i => i.getAttribute('tabindex') === '0') : null; - if (e.code === 'Tab') { + if (e.key === Keys.Tab) { currentCell = document.activeElement as HTMLElement; } const currentRow = this.rows && currentCell ? Array.from(this.rows).find(r => r.contains(currentCell)) : null; @@ -185,35 +187,35 @@ export class KeyNavigationGridController implements OnDestroy { let y = currentRow && currentCell && this.rows ? Array.from(this.rows).indexOf(currentRow) : 0; const dir = this.host.dir; - const inlineStart = dir === 'rtl' ? 'ArrowRight' : 'ArrowLeft'; - const inlineEnd = dir === 'rtl' ? 'ArrowLeft' : 'ArrowRight'; + const inlineStart = dir === 'rtl' ? Keys.ArrowRight : Keys.ArrowLeft; + const inlineEnd = dir === 'rtl' ? Keys.ArrowLeft : Keys.ArrowRight; const itemsPerPage = Math.floor(this.host?.querySelector('.datagrid').clientHeight / this.rows[0].clientHeight) - 1 || 0; - if (e.code === 'ArrowUp' && y !== 0) { + if (e.key === Keys.ArrowUp && y !== 0) { y = y - 1; - } else if (e.code === 'ArrowDown' && y < numOfRows) { + } else if (e.key === Keys.ArrowDown && y < numOfRows) { y = y + 1; - } else if (e.code === inlineStart && x !== 0) { + } else if (e.key === inlineStart && x !== 0) { x = x - 1; - } else if (e.code === inlineEnd && x < numOfColumns) { + } else if (e.key === inlineEnd && x < numOfColumns) { x = x + 1; - } else if (e.code === 'End') { + } else if (e.key === Keys.End) { x = numOfColumns; if (e.ctrlKey) { y = numOfRows; } - } else if (e.code === 'Home') { + } else if (e.key === Keys.Home) { x = 0; if (e.ctrlKey) { y = 0; } - } else if (e.code === 'PageUp') { + } else if (e.key === Keys.PageUp) { y = y - itemsPerPage > 0 ? y - itemsPerPage + 1 : 1; - } else if (e.code === 'PageDown') { + } else if (e.key === Keys.PageDown) { y = y + itemsPerPage < numOfRows ? y + itemsPerPage : numOfRows; } diff --git a/projects/angular/src/utils/enums/keys.enum.ts b/projects/angular/src/utils/enums/keys.enum.ts index 39ae8f68f3..df24efccc0 100644 --- a/projects/angular/src/utils/enums/keys.enum.ts +++ b/projects/angular/src/utils/enums/keys.enum.ts @@ -18,6 +18,8 @@ export enum Keys { Spacebar = ' ', Home = 'Home', End = 'End', + PageDown = 'PageDown', + PageUp = 'PageUp', } export enum IEKeys {