Skip to content

Grid: preserve tile content semantics when dragging is disabled - #84869

Merged
retrofox merged 9 commits into
WordPress:trunkfrom
adamwoodnz:rebase/grid-inactive-sortable-semantics
Oct 20, 2026
Merged

retrofox merged 9 commits into
WordPress:trunkfrom
adamwoodnz:rebase/grid-inactive-sortable-semantics

Conversation

@adamwoodnz

@adamwoodnz adamwoodnz commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What?

Closes #82507, closes #84528. Supersedes #82511, which is @chriskmnds' work rebased on trunk. Maintainer edits on #82511 need write access, so I could not push the rebase to the original branch.

Remove inactive drag-control semantics from tile content in DashboardGrid and DashboardLanes, including pinned items.

Why?

In read mode, and for pinned tiles in edit mode, tile content is wrapped in a focusable role="button" with aria-disabled="true". Screen reader users hear enabled links, buttons and form controls inside a disabled button, and keyboard users meet an extra tab stop that does nothing.

How?

Apply the sortable attributes only when dragging is enabled. Keep the listeners, refs, the enabled activator and the resize and action controls as they are.

Changes on top of #82511:

  • Trunk's Vitest migration (Test: Finish repository Vitest migration and expand Browser Mode coverage #82742) replaced the item-interactions.jsdom.test.tsx files with item-interactions.browser.test.tsx. The expect( activator ).toBeNull() change moved into the new files.
  • sortable-semantics.jsdom.test.tsx failed npm run test:unit:conventions because it stubs ResizeObserver. It is now sortable-semantics.browser.test.tsx, with the same 8 cases and real userEvent keys.
  • New InteractiveContent stories for both layouts: tiles with a link, a button and a select, and one pinned tile. They open in read mode, so the a11y addon checks them. The EditMode stories now pin their first tile.
  • The README keyboard steps now say that only draggable tiles in edit mode take focus.
  • The CHANGELOG.md entry is under Unreleased, after the Grid 0.10.0 release.

Testing Instructions

Checks run with Node 24.18.0:

  • npm run test:unit:vitest -- packages/grid packages/widget-dashboard: 35 files, 299 tests pass.
  • npm run typecheck -- packages/grid passes.
  • ESLint passes on the changed files.
  • npm run lint:changelogs -- --require-pr=84869 packages/grid/CHANGELOG.md passes.

Manual checks in Storybook, for Widgets/Grid/DashboardGrid and Widgets/Grid/DashboardLanes:

  1. Open Interactive Content. Confirm that no tile has an aria-roledescription="sortable" wrapper, and that no control has an ancestor with aria-disabled="true", role="button" or tabindex. The Accessibility panel shows no violations.
  2. Turn on editMode. The pinned tile's controls still have no such ancestor, and its resize handle remains. The draggable tiles wrap their content in the activator, which is Storybook: Resolve accessibility issues flagged in Storybook #81596 and out of scope.
  3. Open Edit Mode. The first tile is pinned and cannot be dragged.

Testing Instructions for Keyboard

  1. In Interactive Content, Tab through the tiles. Focus goes from Details to Refresh to Metric in each tile and never stops on a tile wrapper.
  2. Turn on editMode. In the pinned tile, Tab reaches Details, Refresh and Metric, and then the resize handle.
  3. Focus a draggable tile's activator. Press Space to pick it up and Escape to cancel. Focus stays on the activator.

I ran all of the steps above in Chrome on both layouts. With the fix reverted, the Accessibility panel reports a serious nested-interactive violation on all 3 tiles of each Interactive Content story, so the stories catch a regression. No screen reader testing.

Screenshots or screencast

No visual change to the components. This changes accessibility semantics only.

Use of AI Tools

The original change was written by @chriskmnds with OpenAI Codex. I used Claude Code to rebase it, move the tests to Browser Mode, add the stories and run the checks. I reviewed the changes.

🤖 Generated with Claude Code

@adamwoodnz
adamwoodnz requested a review from retrofox as a code owner October 15, 2026 22:01
@adamwoodnz adamwoodnz added [Package] Grid /packages/grid [Package] Widget Dashboard /packages/widget-dashboard labels Oct 15, 2026
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

Preview in WordPress Playground

🎉 Props

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @theritiktiwari.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: theritiktiwari.

Co-authored-by: chriskmnds <chriskmnds@git.wordpress.org>
Co-authored-by: adamwoodnz <adamwood@git.wordpress.org>
Co-authored-by: retrofox <retrofox@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

@adamwoodnz
adamwoodnz force-pushed the rebase/grid-inactive-sortable-semantics branch from a37807a to 55ed154 Compare October 15, 2026 22:02
adamwoodnz added a commit to adamwoodnz/gutenberg that referenced this pull request Oct 15, 2026
The CHANGELOG check requires the Unreleased entry to cite the PR that
carries it. The fix now lands through WordPress#84869, which supersedes WordPress#82511.
@adamwoodnz
adamwoodnz force-pushed the rebase/grid-inactive-sortable-semantics branch from c43ad3c to 60c636a Compare October 19, 2026 19:41
adamwoodnz added a commit to adamwoodnz/gutenberg that referenced this pull request Oct 19, 2026
The CHANGELOG check requires the Unreleased entry to cite the PR that
carries it. The fix now lands through WordPress#84869, which supersedes WordPress#82511.

@retrofox retrofox left a comment •

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.

Thanks, @adamwoodnz and @chriskmnds. From my pov, the fix is correct and as small as it gets.

I ran sortable-semantics.browser.test.tsx locally against the trunk sources: 4 of the 8 cases fail, read mode and pinned on both surfaces. With the change, all 8 pass.

On the question in #84452 #84528, there was no reason to keep the attributes when dragging is off.

The spread predates the draggable flag, and the aria-disabled="true" assertion that #81967 added only recorded what dnd-kit emits.

Nothing in the packages depends on the disabled activator.

Two things before merging. Grid 0.10.0 went out today, so after the merge the Bug Fixes entry lands under ## 0.10.0.

It needs a rebase and the entry moved to Unreleased.

Also, maybe we can add a story that shows the fix? A tile with a link, a button, and a select in read mode, and a pinned item (draggable: false) in EditMode.

None of the current stories have either, and the a11y addon catches a regression through nested-interactive with them.

Nit: the Accessibility section of the README says "Tab to focus a tile". That now holds only for draggable tiles in edit mode.

Out of scope, as you noted: draggable tiles in edit mode still wrap their content in the role="button" activator (#81596).

The widget dashboard mutes the body, header, and footer with inert in edit mode, so no live control ends up nested there, but a bare grid consumer still hits it.

The fix is to move the handle in the chrome to carry attributes and the keyboard listener, leaving the pointer listener on the tile. That deserves its own issue.

Once this lands, we can close #82511.

chriskmnds and others added 8 commits October 21, 2026 08:01
Trunk's Vitest conventions check rejects jsdom tests that stub
observer APIs, so the `ResizeObserver` stub made Static Checks fail
after the rebase. In Browser Mode the test also uses real keyboard
events and focus in place of `fireEvent` and fake timers.
The CHANGELOG check requires the Unreleased entry to cite the PR that
carries it. The fix now lands through WordPress#84869, which supersedes WordPress#82511.
Grid 0.10.0 was released while this PR was open. The rebase applied
the entry cleanly inside the released 0.10.0 section, which would
have listed the fix under a version that does not include it.
Tiles are no longer focusable in read mode or when pinned, so the
keyboard steps apply only to draggable tiles in edit mode.
No story had native controls inside a tile or a pinned item, so the
a11y addon could not catch a regression of the drag wrapper around
them. The new stories open in read mode, where the addon runs, and
the EditMode stories now pin one tile.
@adamwoodnz
adamwoodnz force-pushed the rebase/grid-inactive-sortable-semantics branch from 60c636a to 7c183d2 Compare October 20, 2026 19:07
@adamwoodnz adamwoodnz added the [Type] Bug An existing feature does not function as intended label Oct 20, 2026
@retrofox

Copy link
Copy Markdown
Contributor

Should we add closes #84528 to the PR desc?

@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Thanks for the review, @retrofox. I pushed these changes:

  • Changelog: rebased on trunk and moved the entry under Unreleased (9ba600d). The rebase had applied it inside ## 0.10.0 without a conflict, as you expected.
  • Stories: added Interactive Content to both DashboardGrid and DashboardLanes (7c183d2). Each tile has a link, a button and a select, and the first tile is pinned. The stories open in read mode, so the a11y addon checks them. They show no violations with the fix. With the fix reverted, they report nested-interactive on all 3 tiles. The Edit Mode stories now pin their first tile.
  • README: the keyboard steps now say "In edit mode, Tab to focus a draggable tile" (fddda03).

I agree that #81596 needs its own issue. I updated the description with the new testing steps.

@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Yes, thanks. It's the same bug as #82507, so this closes both. I added it to the description.

@adamwoodnz adamwoodnz self-assigned this Oct 20, 2026
@adamwoodnz adamwoodnz moved this from Todo to Ready to ship (PR only) in @adamwoodnz's WIP Oct 20, 2026
The stories open in read mode, where no tile is draggable, so the
"Draggable" labels misled readers. The descriptions now say that the
a11y addon is what checks the stories, and that the pinned tile only
matters in edit mode.
@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Please merge at your leisure @retrofox if you're happy with it. I don't have permissions.

@retrofox

Copy link
Copy Markdown
Contributor

Thanks, Adam.

@retrofox retrofox added the [Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). label Oct 20, 2026
@retrofox
retrofox enabled auto-merge (squash) October 20, 2026 20:15
@retrofox
retrofox merged commit 0a17c03 into WordPress:trunk Oct 20, 2026
119 checks passed
@github-project-automation github-project-automation Bot moved this from Ready to ship (PR only) to Done in @adamwoodnz's WIP Oct 20, 2026
@github-actions github-actions Bot added this to the Gutenberg 24.4 milestone Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). [Package] Grid /packages/grid [Package] Widget Dashboard /packages/widget-dashboard [Type] Bug An existing feature does not function as intended

Projects

Status: Done

3 participants