table-nav
table-nav is a headless utility library, that enables accessible keyboard navigation of data grids (aka tables).
The ARIA specs for data grid navigation can be found here:
table-nav consists of:
-
core
: Contains all the logic of the library, which is framework-agnostic -
react
: Useful wrappers to work with React projects -
svelte
(soon): Useful wrappers to work with Svelte projects. Example link
Installation
yarn add @table-nav/core @table-nav/react
Usage
To use this library you can either use directly the core
package or the react
package.
Core
Below is an example on how you can use the core
package in a simple HTML table.
This package is framework-agnostic, and you can find an example integration with Svelte here.
// DataGridNav is the main class of the library
import { DataGridNav } from "@table-nav/core";
// Create a new instance of the class
const dataGridNav = new DataGridNav({
// Library is written in TS and all the options will be recommended inside your IDE
debug: true,
});
// Use the function provided by the library to handle the keydown/up events
table.addEventListener("keydown", dataGridNav.tableKeyDown);
table.addEventListener("keyup", dataGridNav.tableKeyUp); // This is neccessery to allow more than 1 key shortcuts
React
For projects using react, @table-nav/react
provides a useful hook to work with.
// Import hook from "@table-nav/react"
import { useTableNav } from '@table-nav/react';
// Inside your HOC component
const { tableNav, listeners } = useTableNav();
<YourTable {...listeners} />
useTableNav
returns an object with 2 properties:
-
tableNav
: An instance of theDataGridNav
class. Useful for programmatic navigation, and enabling/disabling the functionality for use-cases, like widget focus inside cell. -
listeners
: An object with theonKeyDown
andonKeyUp
listeners, that you can spread inside your table element.
All the examples inside packages/storybook
are with @table-nav/react
, so feel free to take a look how they work.
Grid Navigation
Cell Navigation
Note WTF (What the focus) is going on. There is no standardised way to know what is actually focusable in the web. To comply with this lib and work properly, you need to add a
tabindex
attribute explicitly to the elements you want to be focusable, inside a cell, except forinput
andtextarea
elements.
Key | Description |
---|---|
Restores grid navigation. | |
If the cell contains multiple widgets, moves focus to the next widget inside the cell, optionally wrapping to the first widget if focus is on the last widget. Otherwise, passes the key event to the focused widget.Arrow Down is disabled issue
|
|
If the cell contains multiple widgets, moves focus to the previous widget inside the cell, optionally wrapping to the first widget if focus is on the last widget. Otherwise, passes the key event to the focused widget. Arrow Up is disabled issue
|
The below keystrokes are supported natively by browsers, so they are not implement
-
Tab
: moves focus to the next widget in the grid. Optionally, the focus movement may wrap inside a single cell or within the grid itself. (As described, there should be no trap focus inside a grid cell). -
Shift + Tab
: moves focus to the previous widget in the grid. Optionally, the focus movement may wrap inside a single cell or within the grid itself.
🐝
Known issues - Vertical navigation needs to be a bit smarter: video
- WTF (what the focus) should happen in cell navigation, when you stumble an input? Related issue
Contributions
If you want a feature that is not supported or found a bug that you want to fix, fork the repo, and then make a PR with your proposed changes. Still a small project so there are no strict guidelines.
📚
Useful links - https://www.w3.org/WAI/ARIA/apg/patterns/grid/
- https://www.ag-grid.com/example/
- https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
- https://github.com/ETSOO/Shared/blob/master/src/Keyboard.ts
- https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-noninteractive-element-interactions.md