interactive-row.ts 1.2 KB

123456789101112131415161718192021222324252627282930313233
  1. import type { KeyboardEvent } from 'react'
  2. /**
  3. * Spread onto a clickable row — a `<TableRow>` or a list-row `<div>` — to make
  4. * it keyboard-operable: Tab reaches it, Enter/Space activate it, and the
  5. * `data-row-interactive` attribute gives it a visible focus ring plus a
  6. * pointer cursor (styled in globals.css).
  7. *
  8. * <TableRow {...interactiveRow(() => router.push(href))}>
  9. *
  10. * Rows keep their own semantics on purpose. They can't be `<button>`s —
  11. * nesting the row's dismiss/menu buttons inside one is invalid HTML — and
  12. * `role="button"` on a container with interactive children is an ARIA
  13. * violation. A focusable element that announces its content and activates on
  14. * Enter is the pragmatic, axe-clean middle ground.
  15. *
  16. * The `target === currentTarget` guard keeps Enter/Space pressed on inner
  17. * controls (checkboxes, kebab menus) from also triggering the row.
  18. */
  19. export function interactiveRow(activate: () => void) {
  20. return {
  21. 'data-row-interactive': true,
  22. tabIndex: 0,
  23. onClick: activate,
  24. onKeyDown: (e: KeyboardEvent<HTMLElement>) => {
  25. if (e.target !== e.currentTarget) return
  26. if (e.key === 'Enter' || e.key === ' ') {
  27. e.preventDefault()
  28. activate()
  29. }
  30. },
  31. }
  32. }