FreeGridv0.8.1한국어

Accessibility & Shadow DOM

ARIA grid roles, the focused cell read by screen readers, and a grid inside a Shadow DOM.

Code for this demo

import { createGrid } from '@freegridjs/core';
import css from '@freegridjs/core/styles.css?inline';
import { demoColumns, makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';

export default function mount(ctx: DemoContext) {
  const { lang } = ctx;
  const h = (en: string, ko: string) => tr(lang, { en, ko });
  // the grid lives in a shadow root, as in a web component; the stylesheet goes into the shadow root
  const shadow = ctx.host.attachShadow({ mode: 'open' });
  const style = document.createElement('style');
  style.textContent = css;
  const box = document.createElement('div');
  box.style.height = '100%';
  shadow.append(style, box);

  const grid = createGrid(box, { columns: demoColumns(lang), data: makeRows(200), locale: lang, rowSelection: 'multiple', groupPanel: true });
  grid.on('focusChange', e => {
    // what a screen reader announces: the focused cell, through aria-activedescendant
    const gridEl = shadow.querySelector('.fg-grid')!;
    const cell = shadow.getElementById(gridEl.getAttribute('aria-activedescendant') ?? '');
    ctx.log(`${h('focus', '포커스')} → ${h('row', '행')} ${e.rowIndex + 1}, ${e.colId}: ${cell?.textContent ?? ''}`);
  });

  text(ctx, h('This grid sits inside a Shadow DOM. Tab into it and use the arrow keys: the log shows what a screen reader reads. Menus and filters open inside the shadow root.',
    '이 그리드는 Shadow DOM 안에 있습니다. Tab으로 들어가 방향키로 움직이면 스크린리더가 읽는 내용이 아래에 표시됩니다. 메뉴와 필터도 shadow root 안에서 열립니다.'));
  separator(ctx);
  button(ctx, h('Show ARIA roles', 'ARIA 역할 보기'), () => {
    const g = shadow.querySelector('.fg-grid')!;
    ctx.log(`role=${g.getAttribute('role')} aria-rowcount=${g.getAttribute('aria-rowcount')} aria-colcount=${g.getAttribute('aria-colcount')} aria-multiselectable=${g.getAttribute('aria-multiselectable')}`);
    ctx.log(`${h('header rows', '헤더 행')}: ${shadow.querySelectorAll('.fg-header [role=row]').length}, ${h('column headers', '열 머리글')}: ${shadow.querySelectorAll('[role=columnheader]').length}`);
  });
  button(ctx, h('Group by country (becomes a treegrid)', '국가별 묶기 (treegrid로 바뀜)'), () => {
    grid.setGroupBy(grid.getGroupBy().length ? [] : ['country']);
    setTimeout(() => ctx.log(`role=${shadow.querySelector('.fg-grid')!.getAttribute('role')}`), 50);
  });
  return grid;
}