DataViewsPicker Table: Fix first-click row selection#78423
Conversation
…akit focus shift Switches the picker-table row from `onClick` to `onMouseDown` with `preventDefault()`. On the first interaction with the Composite, Ariakit calls `baseElement.focus()` without `preventScroll`, which both swallowed the click and scrolled the active row under the sticky `<thead>`. Suppressing focus on the row stops both.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: +96 B (0%) Total Size: 7.97 MB 📦 View Changed
ℹ️ View Unchanged
|
andrewserong
left a comment
There was a problem hiding this comment.
Good catch, I've bumped into this a few times recently and didn't quite understand why it was doing that 👍
A downside I'm noticing in testing is that without focus being placed on the row, if I click on a row and then switch to keyboard up/down arrows, I'm no longer within the body of the table, and so that navigation is no longer directly available. I.e on trunk I can do this:
2026-05-19.13.04.34.mp4
Is there any way to preserve focusing the row but fix the issue of the click being swallowed / the area being scrolled?
On balance I think the behaviour in this PR does feel better than trunk, though!
| } | ||
| } } | ||
| onClick={ () => { | ||
| // Toggle in/out of selection array |
There was a problem hiding this comment.
Any reason this comment was removed, it doesn't look like that behaviour has changed?
There was a problem hiding this comment.
No reason! I think I ctrl-X and didn't ctrl-Z 😄
|
Also, I was wondering if we'd need to apply a similar fix to the Grid picker layout, but in manual testing I didn't notice this bug there. |
|
Flaky tests detected in 0bcd53b. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/26074521281
|
Thanks for catching that! I don't know if there's a remedy. I'll test something out and, if it gets too deep into the weeds, I'll leave it and create an issue so that folks more learned that I can take a look. We can close this PR too if it comes to that. |
I didn't notice it there either. It's the default, right? Maybe that's why this bug wasn't obvious. 🤔 |
| event.currentTarget.parentElement?.focus( { | ||
| preventScroll: true, | ||
| } ); |
There was a problem hiding this comment.
This is for the keyboard navigation after selecting an item.
For now this only matters when a Composite container has a sticky sibling, which today is just picker-table.
| tbody { | ||
| scroll-margin-top: calc(var(--wpds-dimension-base) * 10); | ||
| } |
There was a problem hiding this comment.
Why scroll-margin-top on the tbody...
Tabbing into the table focuses the tbody, and the browser scrolls it to the top of the scroll container. Our sticky thead lives at that edge, so the first row ends up hidden under the header.
The sticky thead is on .dataviews-view-table itself , so it applies anywhere the table layout renders, including outside the modal. picker-table inherits it.
The fix scroll-margin-top tells the browser to leave room above when scrolling the element into view.
Why not put it in Composite.Item?
Composite is a generic keyboard widget used for menus, toolbars, selects, grids. Presumably not all consumers have a sticky header sibling. A scroll offset there would be the wrong default.
If this pattern ever spreads, maybe the better fix is upstream, e.g., an Ariakit option to make container focus use preventScroll.
There was a problem hiding this comment.
I'm going to drop this change - I was just trying something out.
andrewserong
left a comment
There was a problem hiding this comment.
Thanks for the update, this is testing nicely for me now, with keyboard focus where it should be after clicking.
I didn't notice it there either. It's the default, right? Maybe that's why this bug wasn't obvious. 🤔
Ah, I wonder if it might be because we don't have a sticky header above the grid area, so maybe that's why it isn't present there. In any case, we can revisit separately if we notice any oddities in the grid behaviour.
Why scroll-margin-top on the tbody...
Good catch on that one, I hadn't noticed it when tabbing from the header. I see that's present in trunk too, so doesn't seem like a blocker to landing this fix?
This one LGTM!
|
Thanks for the quick re-test! 🙇🏻 |
|
Gave this a test and it works well from what I can see. Something to mention from a keyboard accessibility point of view (but not introduced here), the File Name field currently adds tab stops to the picker (I think due to the tooltip) which is not a great experience. Especially so in the table layout as it's there by default, but it's also reproducible in the grid layout when adding the field. |
Oh I think I came across that and briefly thought to myself "that's annoying" and didn't think much of it. Could be that the view needs another round of a11y/keyboard testing. 🤔 Thanks, folks! |
Good catch! I've added a to-do list item to the Media interaction in modal tracking issue. |
Summary
In the picker-table layout, the first click on a row was swallowed and scrolled the active row out from under the sticky
<thead>. On first focus into the Composite, Ariakit callsbaseElement.focus()withoutpreventScroll— which both ate the click and shifted the scroll.Handle selection on
onMouseDownandpreventDefault()so the browser never moves focus to the row. Subsequent rendered focus is managed by Ariakit on the Composite container, as before.Before
Kapture.2026-05-19.at.11.21.55.mp4
After
Kapture.2026-05-19.at.12.30.36.mp4
Test plan