DEV Community

Hitesh Gharate
Hitesh Gharate

Posted on

Testing accessibility in AG Grid: a grid-aware helper for Jest and Playwright

Dense data grids are where accessibility problems like to hide. A table with thousands of rows, virtualized columns, inline buttons and color-coded values can look fine and still be hard to use with a keyboard or a screen reader.

General tools such as axe-core are great for checking a page. They are not built around grid-specific questions: Is the total row count exposed when rows are virtualized? Does the arrow key actually move focus one cell? Is a status shown only by a colored dot?

So I wrote a small helper, ag-grid-a11y-test, that adds grid-aware checks on top of the tools you already use. This post shows how to use it.

Status: early preview (alpha), not on npm yet. The code is public on GitHub and I am sharing it so people can try it and give feedback. The rules come from the ARIA grid pattern and AG Grid's commonly rendered markup. I have not yet validated them against every AG Grid version, so treat findings as prompts to investigate, not proof of conformance. Your feedback will decide what changes next.

What it checks

Structure (works in jsdom or a real browser):

  • The grid has a grid or treegrid role and an accessible name
  • aria-rowcount and aria-colcount are present and consistent with row and column indexes (important for virtualized grids)
  • Rows and cells have aria-rowindex and aria-colindex
  • Column headers have accessible names (this catches icon-only headers)
  • Sortable headers expose aria-sort

Patterns common in financial and data-heavy tables:

  • Icon-only buttons and checkboxes with no accessible name
  • Cells that show only an icon or colored element, with no text
  • Negative values shown by color alone (opt-in, you tell it your CSS class)
  • Abbreviated numbers like 1.2M (advisory)

Keyboard navigation (real browser, via Playwright):

  • Arrow keys move focus one cell and return to the start
  • Tab can eventually leave the grid (a warning if not, since a very large grid can cause false positives)

Each finding has a severity: error (very likely a real problem), warning (verify), or info (advisory).

Install

The package is not published to npm yet, so install it from the GitHub repo. Clone it and build a local package:

git clone https://github.com/Hitesh-Gharate/accessibility.git
cd accessibility
npm install
npm run build
npm pack   # creates ag-grid-a11y-test-0.1.0.tgz
Enter fullscreen mode Exit fullscreen mode

Then install that file into your Angular project (adjust the path):

npm install --save-dev ../accessibility/ag-grid-a11y-test-0.1.0.tgz
# optional, for the axe-core adapter
npm install --save-dev axe-core
Enter fullscreen mode Exit fullscreen mode

The examples below import from ag-grid-a11y-test, which is the package name inside that file.

Use it in an Angular unit test (Jest)

import { TestBed } from '@angular/core/testing';
import { auditGrid, assertNoViolations, formatReport } from 'ag-grid-a11y-test';
import { PositionsGridComponent } from './positions-grid.component';

it('positions grid passes accessibility checks', async () => {
  const fixture = TestBed.createComponent(PositionsGridComponent);
  fixture.detectChanges();
  await fixture.whenStable();
  fixture.detectChanges();

  const result = auditGrid(fixture.nativeElement, {
    gridSelector: '.ag-root',
    negativeValueClasses: ['loss'], // your app's class for negative values (optional)
  });

  console.log(formatReport(result)); // everything, including warnings and info
  assertNoViolations(result);        // fails the test on errors only
});
Enter fullscreen mode Exit fullscreen mode

jsdom has no layout engine, so AG Grid may not render rows unless virtualization is turned off in your test grid options. Check the AG Grid docs for the exact option names in your version.

A failing result reads like this (an example of the report format):

Grid accessibility audit: 1 error(s), 0 warning(s), 0 info.
  [ERROR] grid-rowcount: The grid is missing a valid integer aria-rowcount (use -1 if the total is unknown).
      at div.ag-root
      help: Without it, virtualized grids do not tell assistive technology how many rows exist.
Enter fullscreen mode Exit fullscreen mode

To be stricter, use assertNoViolations(result, { failOn: ['error', 'warning'] }). To silence a rule you have reviewed, use ignoreRules: ['abbreviated-number'].

Use it in a real browser (Playwright)

A real browser is the most reliable option, and it is the only way to test keyboard behavior.

import { test } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import { auditKeyboardNavigation, fromAxeResults, mergeResults, assertNoViolations } from 'ag-grid-a11y-test';

test('positions grid is accessible', async ({ page }) => {
  await page.goto('/positions');
  await page.waitForSelector('.ag-root [role=gridcell]');

  // 1. Structure and pattern checks, executed inside the page
  await page.addScriptTag({ path: require.resolve('ag-grid-a11y-test/browser') });
  const domResult = await page.evaluate(() => {
    const lib = (window as any).AgGridA11y;
    return lib.serialize(lib.auditGrid(document.body, { gridSelector: '.ag-root' }));
  });

  // 2. axe-core, scoped to the grid
  const axe = fromAxeResults(await new AxeBuilder({ page }).include('.ag-root').analyze());

  // 3. Keyboard navigation
  const keyboard = await auditKeyboardNavigation(page, { gridSelector: '.ag-root' });

  assertNoViolations(mergeResults(domResult, axe, keyboard));
});
Enter fullscreen mode Exit fullscreen mode

All three sources are merged into one report, so axe findings and grid-specific findings appear together.

What it cannot do

Please read this part before relying on it:

  • Automated checks find only a subset of accessibility problems. You still need manual keyboard testing and screen reader testing (NVDA, JAWS, VoiceOver).
  • Several rules are heuristics and can produce false positives: visual-only-cell, abbreviated-number and kbd-tab-trap in particular.
  • Passing this audit does not mean WCAG conformance.
  • The test fixtures in the repo are hand-written approximations of AG Grid markup, not captured output from a specific release.

What is next

Planned work includes integration tests against real AG Grid output for each major version, Home/End and Page Up/Down navigation, cell-editing keyboard flow, a visible-focus check, and row selection and group expand/collapse state.

If you try it on your own grid and a rule misfires or misses something, please open an issue with your AG Grid version, the rule id, and the smallest HTML snippet that reproduces it (remove any confidential data first). Pull requests are welcome too.

Not affiliated with or endorsed by AG Grid. AG Grid is a trademark of its owner.

Top comments (0)