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 1 commit 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/table': patch | ||
| --- | ||
|
|
||
| [fix] Leave a cut whose range has no end in a table to the editor's own cut and to other CUT_COMMAND listeners, instead of the table answering every range selection once the document holds a table |
There was a problem hiding this comment.
Optional: CONTRIBUTING.md (Changesets) asks a fix that changes observable editing behavior to add a Verify: paragraph. 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.| selection.focus.set(text.getKey(), 1, 'text'); | ||
| $setSelection(selection); | ||
| }); | ||
| expect(getLowPriorityCuts()).toBe(1); |
There was a problem hiding this comment.
Optional: this proves a LOW listener runs, but not the outcome from #9281. A case that registers rich text, cuts $selectAll() over heading/table/paragraph, awaits the cut, and asserts the root holds a single paragraph (plus an update tagged CUT_TAG) would fail on main, which leaves heading, and pin the user-visible fix.
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: |
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.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 diff are from the issue; I reproduced and checked it with the tests below.
Closes #9281
Changeset
Added
.changeset/leave-a-cut-whose-range-has-no-end.md(@lexical/table, patch,[fix]).pnpm run check-changesetspasses.Test plan
Before
The new
CutOutsideTable.test.tsfails onmain: aCOMMAND_PRIORITY_LOWcut listener never runs for a range in a paragraph after a table.After
CutOutsideTable.test.ts: the LOW listener runs for a range outside the table; it does not run for a range inside a cell (the table keeps those).pnpm exec vitest run --project unit packages/lexical-table packages/lexical-rich-text packages/lexical-clipboard: 52 files, 452 tests pass.pnpm run tsc, eslint and prettier on the changed files are clean.I ran the unit tests only (jsdom, macOS); I did not run the e2e suite.