Writing Flow: fix arrow keys skipping paragraph containing link#77474
Conversation
…link Adds a failing E2E regression test for #77473, in which pressing ArrowDown from a paragraph skips over the next paragraph when it contains a link. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
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: 0 B Total Size: 7.75 MB ℹ️ View Unchanged
|
Fixes #77473. Commit #75141 added a check in `isTabCandidate` (inside `getClosestTabbable`) to skip any node that (a) has a block client ID and (b) contains nested focusable elements. The intent was to skip block wrapper divs in favour of their inner contenteditable children. However, `getBlockClientId()` returns a truthy value for *any* node inside a block — not just the block wrapper. A `<p contenteditable="true">` that contains an `<a href>` was therefore also skipped: the `<p>` was skipped because it has a nested focusable element (the link), and the `<a>` was subsequently skipped by the existing contenteditable guard. Navigation fell through to the next plain paragraph, bypassing the paragraph with the link entirely. Fix: add `node.contentEditable !== 'true'` to the skip condition so that actual editing surfaces are never bypassed regardless of what inline elements they contain. Only non-contenteditable wrappers (i.e. real block wrappers) are now subject to the "skip in favour of children" logic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Flaky tests detected in e0c11f5. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/24627135984
|
| if ( | ||
| node.contentEditable !== 'true' && | ||
| getBlockClientId( node ) && | ||
| focus.focusable |
There was a problem hiding this comment.
This was introduced in #75141, we could try to switch to focus.tabbable, which should normally skip focusable elements within contenteditable. Maybe we can try it?
There was a problem hiding this comment.
I don't actually know if that will still fix it, but links within contenteditable are not tabbable while they are focusable, so theoretically it should work if the utility behaves correctly.
There was a problem hiding this comment.
If we have fix tabbable to follow the spec better, then maybe we should do this separately from 7.0.
There was a problem hiding this comment.
Edit: GitHub didn't update to show your comments, which is why the paragraph below sounds repetitive. :/
Just switching to focus.tabbable isn't enough, which suggests there may be something to fix in the tabbable module. But right now we have the chance to get this fix in time for WP 7.0, so I'd be OK with the current patch. Pushed a comment clarifying the intention in e0c11f5.
|
There was a conflict while trying to cherry-pick the commit to the wp/7.0 branch. Please resolve the conflict manually and create a PR to the wp/7.0 branch. PRs to wp/7.0 are similar to PRs to trunk, but you should base your PR on the wp/7.0 branch instead of trunk. |
Fixes #77473. Commit #75141 added a check in `isTabCandidate` (inside `getClosestTabbable`) to skip any node that (a) has a block client ID and (b) contains nested focusable elements. The intent was to skip block wrapper divs in favour of their inner contenteditable children. However, `getBlockClientId()` returns a truthy value for *any* node inside a block — not just the block wrapper. A `<p contenteditable="true">` that contains an `<a href>` was therefore also skipped: the `<p>` was skipped because it has a nested focusable element (the link), and the `<a>` was subsequently skipped by the existing contenteditable guard. Navigation fell through to the next plain paragraph, bypassing the paragraph with the link entirely. Fix: add `node.contentEditable !== 'true'` to the skip condition so that actual editing surfaces are never bypassed regardless of what inline elements they contain. Only non-contenteditable wrappers (i.e. real block wrappers) are now subject to the "skip in favour of children" logic. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org>
Fixes #77473. Commit #75141 added a check in `isTabCandidate` (inside `getClosestTabbable`) to skip any node that (a) has a block client ID and (b) contains nested focusable elements. The intent was to skip block wrapper divs in favour of their inner contenteditable children. However, `getBlockClientId()` returns a truthy value for *any* node inside a block — not just the block wrapper. A `<p contenteditable="true">` that contains an `<a href>` was therefore also skipped: the `<p>` was skipped because it has a nested focusable element (the link), and the `<a>` was subsequently skipped by the existing contenteditable guard. Navigation fell through to the next plain paragraph, bypassing the paragraph with the link entirely. Fix: add `node.contentEditable !== 'true'` to the skip condition so that actual editing surfaces are never bypassed regardless of what inline elements they contain. Only non-contenteditable wrappers (i.e. real block wrappers) are now subject to the "skip in favour of children" logic. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org>
…link (#77478) * Writing Flow: fix arrow keys skipping paragraph containing link (#77474) Fixes #77473. Commit #75141 added a check in `isTabCandidate` (inside `getClosestTabbable`) to skip any node that (a) has a block client ID and (b) contains nested focusable elements. The intent was to skip block wrapper divs in favour of their inner contenteditable children. However, `getBlockClientId()` returns a truthy value for *any* node inside a block — not just the block wrapper. A `<p contenteditable="true">` that contains an `<a href>` was therefore also skipped: the `<p>` was skipped because it has a nested focusable element (the link), and the `<a>` was subsequently skipped by the existing contenteditable guard. Navigation fell through to the next plain paragraph, bypassing the paragraph with the link entirely. Fix: add `node.contentEditable !== 'true'` to the skip condition so that actual editing surfaces are never bypassed regardless of what inline elements they contain. Only non-contenteditable wrappers (i.e. real block wrappers) are now subject to the "skip in favour of children" logic. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org> * Remove e2e test on format-toolbar visibility --------- Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org> Co-authored-by: t-hamano <wildworks@git.wordpress.org>
|
This PR was backported to |
Fixes #77473.
What
When the cursor was in a paragraph and the next paragraph contained a link (or any focusable inline element wrapping the full content), pressing
ArrowDownwould skip over it and land in the paragraph after. The same happened in the opposite direction withArrowUp, or indeed with the horizontal arrows at the edges of the paragraphs.Why
I'd like this to be vetted by @ellatrix
Commit #75141 added a check in
isTabCandidateto skip any node that (a) has a block client ID and (b) contains nested focusable elements — intended to skip block wrapper divs in favour of their inner contenteditable children.However,
getBlockClientId()returns a truthy value for any node inside a block, not just the wrapper. So a<p contenteditable="true">containing an<a href>was also skipped: the<p>was rejected because it had a nested focusable (the link), and the<a>was subsequently rejected by the existing contenteditable guard. Navigation fell through to the next plain paragraph.Fix
Add
node.contentEditable !== 'true'to the skip condition so that actual editing surfaces are never bypassed regardless of what inline elements they contain. Only non-contenteditable wrappers are subject to the "skip in favour of children" logic.Testing
All 126 tests pass across Chromium, Firefox, and WebKit.
🤖 Generated with Claude Code