Skip to content

[WC-3084] feat(datagrid-web): add WCAG 4.1.3 status region and selection accessibility improvements - #2300

Open
iobuhov wants to merge 13 commits into
mainfrom
3084/a11y-dg-selection
Open

[WC-3084] feat(datagrid-web): add WCAG 4.1.3 status region and selection accessibility improvements#2300
iobuhov wants to merge 13 commits into
mainfrom
3084/a11y-dg-selection

Conversation

@iobuhov

@iobuhov iobuhov commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds screen reader announcements for selection changes and comprehensive keyboard accessibility for DataGrid selection controls, meeting WCAG 2.2 AA requirements.

Changes

  • Status region (role="status") announces selection count changes to screen readers (WCAG 4.1.3)
  • aria-label on select-all checkbox: "Select all rows" (WCAG 4.1.2)
  • Focus management: Returns focus to logical element when SelectAllBar disappears (WAI-ARIA APG)
  • aria-disabled instead of native disabled on SelectAllBar button to prevent focus loss
  • aria-live="assertive" on SelectAllBar button to announce label changes
  • Focus indicators: Added :focus-visible outline to SelectAllBar buttons (WCAG 2.4.7)
  • CSS cleanup: Removed blanket .table *:focus { outline: 0 } rule
  • E2E tests: 18 comprehensive Playwright tests covering all accessibility requirements

Test Plan

  • All unit tests pass (225/225)
  • All E2E tests pass (18/18)
  • Lint clean (0 errors)
  • Manual screen reader testing (VoiceOver)
  • Keyboard-only navigation verified

Test Project

Updated test project branch: datagrid-web/data-widgets-3.0_2

🤖 Generated with Claude Code

@iobuhov
iobuhov requested a review from a team as a code owner July 2, 2026 13:39
@iobuhov iobuhov changed the title feat(datagrid-web): add WCAG 4.1.3 status region and selection accessibility improvements [WC-3084] feat(datagrid-web): add WCAG 4.1.3 status region and selection accessibility improvements Jul 14, 2026
samuelreichert
samuelreichert previously approved these changes Jul 15, 2026
"create-translation": "rui-create-translation",
"dev": "pluggable-widgets-tools start:ts",
"e2e": "run-e2e ci",
"e2e": "MENDIX_VERSION=10.24.12.86709 run-e2e ci",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔶 Medium — Unrelated datagrid-date-filter-web change should not be in this PR

MENDIX_VERSION=10.24.12.86709 is now hard-coded inline in the e2e script for this widget, which is unrelated to the DataGrid selection accessibility work. This makes the change easy to miss in review and hard to revert independently. Hard-coded version strings also risk diverging from the rest of the CI matrix.

Please either revert this file, or land it in a separate targeted PR with an explanation of why this package needs a pinned version.


### Fixed

- We fixed an issue where screen readers announced a row's selection state twice, as both "selected" and "checked", when selecting rows using checkboxes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔶 Medium — CHANGELOG understates the scope of user-visible changes

Only the double-announcement fix is documented. This PR also introduces several other changes that are observable to keyboard and screen-reader users:

  • Screen-reader status region announcing selection count changes (WCAG 4.1.3)
  • aria-label="Select all rows" on the select-all checkbox
  • Focus-return after clearing selection (checkbox or active-cell fallback)
  • Removal of the blanket outline: 0 rule, restoring visible focus indicators throughout the grid

Suggested expansion:

### Added

- We added a screen reader announcement region that announces selection count changes as rows are selected or deselected (WCAG 4.1.3).
- We added an accessible label ("Select all rows") to the select-all checkbox so screen readers can identify its purpose.

### Fixed

- We fixed an issue where keyboard focus indicators were hidden inside the data grid because a blanket CSS rule suppressed them.
- We fixed an issue where clearing a selection could cause keyboard focus to be lost; focus now returns to the select-all checkbox or the active grid cell.
- We fixed an issue where screen readers announced a row's selection state twice, as both "selected" and "checked", when selecting rows using checkboxes.


&:focus-visible {
outline: 1px solid var(--brand-primary, $brand-primary);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Low — Duplicate :focus-visible selector

widget-datagrid-btn-link now has two separate &:focus-visible rules — one for background-color (inside the :hover, :focus-visible block above) and this new standalone one for outline. The duplicate is valid CSS but confusing. They can be merged:

Suggested change
}
&:hover,
&:focus-visible {
background-color: var(--brand-primary-50, $color-default-lighter);
outline: 1px solid var(--brand-primary, $brand-primary);
}

(The :focus-visible block that follows can then be removed.)

Comment on lines +22 to +27
await page.keyboard.press("Tab");
if (await grid.evaluate(el => el.contains(document.activeElement))) {
break;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Low — Tab-loop pattern is fragile

The for (let i = 0; i < 20; i++) loop is a silent failure mode. If the tab order grows (more controls added to the page), the loop exits without the target element ever being focused, and the test either silently passes or gives a confusing assertion error. This pattern appears in several tests (lines 22–27, 57–62, 83–88).

Prefer await element.focus() to place focus programmatically, then assert keyboard behaviour from there. If the goal is specifically to test Tab-into-component navigation, replace the loop with await expect(target).toBeFocused() after a series of Tab presses, which will retry rather than giving up silently.

iobuhov and others added 9 commits August 14, 2026 10:14
…heckbox labels

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…tus region

- Add selectionStatusStore factory to widget-plugin-grid with smart text logic
- Create SelectionStatus component with role="status" and sr-only styling
- Create SelectionStatusViewModel for DI pattern
- Wire up DI tokens, injection hooks, and container bindings
- Integrate SelectionStatus into WidgetFooter outside conditional visibility
- Status region announces "All X rows selected" vs "Y items selected" correctly
- Export selection model functions from widget-plugin-grid main entry

Implementation follows WCAG 4.1.3 Status Messages requirements:
- Uses role="status" (implicit aria-live="polite" + aria-atomic="true")
- Always present in DOM when selection enabled
- Announces complete messages without interrupting screen reader
- Matches visual SelectAllBar text to prevent announcement/visual mismatch

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…E tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ction aria labels

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…v scripts

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Avoid redundant "checked" + "X selected" announcements by only
announcing selection status for bulk operations (select all, clear
selection, keyboard shortcuts) and suppressing announcements for
individual checkbox clicks.

- Add shouldAnnounce flag to selectionStatusStore
- Set flag in SelectActionsProvider based on operation type
- Update SelectionStatusViewModel to check shouldAnnounce
- Update E2E tests to verify new behavior

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…tion

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@iobuhov
iobuhov force-pushed the 3084/a11y-dg-selection branch from c66e1af to 9eca7a5 Compare August 14, 2026 08:14
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants