Repository navigation
[lexical-table] Bug Fix: Leave a cut with no end in the table to the editor's own cut - #9708
therealemmanueloppong wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
There was a problem hiding this comment.
Thanks for the fix. This looks good to merge as is.
The early return in packages/lexical-table/src/LexicalTableSelectionHelpers.ts (lines 970-977) hands a range with neither end in the table back to rich text's cut and to lower-priority CUT_COMMAND listeners, which fixes #9281. Select All + Cut over a heading, a table and a paragraph now leaves an empty paragraph, and its updates carry CUT_TAG. Cuts the table owns (a table selection, a range in a cell, a range with one end in the table) behave as before. The new test fails without the change and passes with it.
A few optional, non-blocking suggestions:
-
Add a
Verify:paragraph to the changeset (.changeset/leave-a-cut-whose-range-has-no-end.md). CONTRIBUTING.md (Changesets) and AGENTS.md ask a fix that changes observable editing behavior to carry one. For example:Verify: in a document that holds a table, cut a range with neither end in the table (for example Select All over a heading, a table and a paragraph) and check that CUT_COMMAND listeners below COMMAND_PRIORITY_HIGH run, the removal arrives in an update tagged CUT_TAG once the clipboard write resolves, and an empty paragraph rather than an empty heading is left. In a document with two tables, cutting from a cell of the second table to the text after it removes that table whole.
-
Assert the outcome the issue describes (
packages/lexical-table/src/__tests__/unit/CutOutsideTable.test.ts, lines 74-85). The test checks that a LOW listener ran. Registering rich text and asserting that a whole-document cut over heading/table/paragraph leaves a singleparagraph, with an update taggedCUT_TAG, would pin the behavior people actually hit. -
Mention and test the two-table case. Every table registers its own HIGH cut listener, and before this change the first table answered every range. That included a range with one end in a second table, which then never got that table's widening: cutting from a cell of a second 2×2 table to the text after it left a one-cell table behind. With this change the owning table answers and removes the table whole, as in a single-table document. The description says ranges with an end in the table are unchanged, so this improvement deserves a sentence there and a test.
-
Paste the Test plan output. AGENTS.md (Commits and Pull Requests) asks the
### Before/### Aftersections to include the real output of the commands that were run, not a description of it.
lexical
@lexical/a11y
@lexical/clipboard
@lexical/code
@lexical/code-core
@lexical/code-prism
@lexical/code-shiki
@lexical/compiler
@lexical/devtools-core
@lexical/dragon
@lexical/eslint-plugin
@lexical/extension
@lexical/fast-check
@lexical/file
@lexical/hashtag
@lexical/headless
@lexical/history
@lexical/html
@lexical/internal
@lexical/link
@lexical/list
@lexical/mark
@lexical/markdown
@lexical/mdast
@lexical/offset
@lexical/overflow
@lexical/plain-text
@lexical/react
@lexical/rich-text
@lexical/selection
@lexical/table
@lexical/tailwind
@lexical/text
@lexical/utils
@lexical/y
@lexical/yjs
commit: |
…d a Verify paragraph
There was a problem hiding this comment.
Thanks for the fix. This looks good to me.
The early return in the table's CUT_COMMAND listener (packages/lexical-table/src/LexicalTableSelectionHelpers.ts, lines 970-977) hands a range with neither end in the table to rich text's cut and to lower-priority listeners. That fixes #9281. Table selections, ranges in a cell and ranges with one end in the table stay on the table's path.
What I checked:
- Against the base implementation, three of the four new
CutOutsideTable.test.tscases fail with the errors in your Before section, and all four pass on this head. Thelexical-table,lexical-rich-textandlexical-clipboardunit suites also pass here, as dopnpm run tscandpnpm run check-changesets. - In the playground in Chromium, Select All followed by Ctrl+X over a heading, a table and a paragraph now leaves one empty paragraph, with
CUT_TAGupdates and the lower-priority cut listeners running. Onmainit leaves an empty Heading 1. Cutting text inside a cell behaves the same as before. - The two-table case from the description behaves as the new test pins it: the table the range ends in removes itself whole.
- Undo after these cuts restores the document, as it does on
main. - The changeset follows CONTRIBUTING.md (Changesets), including the
Verify:paragraph a behavior fix needs.
No changes requested.
|
Heads up, and apologies for the noise: after opening this I noticed #9285 by @rinarakaki, opened on Sept 30, which fixes the same issue (#9281). I missed it when I checked for existing PRs. The two take the same approach in Please merge whichever you prefer. I am happy to close this one in favor of #9285, or to rebase it onto it, just say which. |
Description
Current behavior: once a document holds a table, the table's
CUT_COMMANDlistener (COMMAND_PRIORITY_HIGH) answers every range selection, wherever the range is. For a range outside the table it copies the range,$deleteCellHandlerreturnsfalse, then it callsselection.removeText()and returnstrue. So rich text's own cut never runs (a whole-document cut is not widened to its blocks and leaves the first block's type behind, and neither update is taggedCUT_TAG), and no listener belowCOMMAND_PRIORITY_HIGHruns either.This PR makes the listener return
falsefor a range selection with neither end inside its table, so the editor's own cut and other listeners handle it as they would with no table in the document. A table selection, a range in a cell, and a range with an end in the table are unchanged.It also fixes a document with more than one table. Every table registers its own listener, and before this change the first table answered every range, including one that ends in a second table, so that table never widened the range around itself: cutting from a cell of the second table to the text after it left a one-cell table behind. Now the table the range ends in answers, and removes the table whole, as in a single-table document.
One existing test changes: "Cut table in the middle of a range selection" relied on the table cutting a range whose ends are both outside the table. It now registers rich text and waits for the cut to finish, since rich text removes the range after its copy resolves. It gives the same
<p><br></p>.The diagnosis and the first version of the diff are from the issue.
Closes #9281
Test plan
New file:
packages/lexical-table/src/__tests__/unit/CutOutsideTable.test.ts(a range outside the table reaches a LOW listener; a range in a cell stays the table's; a whole-document cut over heading, table and paragraph leaves one paragraph in an update taggedCUT_TAG; the two-table case).Before
pnpm exec vitest run --project unit packages/lexical-table/src/__tests__/unit/CutOutsideTable.test.tsonmain'sLexicalTableSelectionHelpers.ts:After
pnpm run tsc,pnpm run check-changesets, eslint and prettier on the changed files are clean.Only the unit tests (jsdom, macOS) were run; the e2e and browser suites were not.