diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index 75bda0f018b..0624d80d30f 100644 --- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs @@ -25,6 +25,7 @@ import { copyToClipboard, deleteTableColumns, deleteTableRows, + expect, focusEditor, html, initialize, @@ -1360,6 +1361,47 @@ test.describe.parallel('Tables', () => { ); }); + test('Can delete all with range selection anchored in table', async ({ + page, + isCollab, + isPlainText, + }) => { + test.skip(isPlainText || isCollab); + await initialize({isCollab, page}); + await focusEditor(page); + await insertTable(page, 1, 1); + // Remove paragraph before + await moveUp(page); + await page.keyboard.press('Backspace'); + await assertHTML( + page, + html` + + + + + +
+


+
+


+ `, + ); + // Select all but from the table + const modifier = process.platform === 'darwin' ? 'Meta' : 'Control'; + await page.keyboard.press(`${modifier}+A`); + // The observer is active + await expect(page.locator('.table-cell-action-button')).toBeVisible(); + await page.keyboard.press('Backspace'); + await assertHTML( + page, + html` +


+ `, + ); + }); + test(`Horizontal rule inside cell`, async ({page, isPlainText, isCollab}) => { await initialize({isCollab, page}); test.skip(isPlainText); diff --git a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts index 4353604567e..be84112c306 100644 --- a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts +++ b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts @@ -336,22 +336,30 @@ export function applyTableHandlers( event: KeyboardEvent | ClipboardEvent | null, ): boolean => { const selection = $getSelection(); + if (!($isTableSelection(selection) || $isRangeSelection(selection))) { + return false; + } - if (!$isSelectionInTable(selection, tableNode)) { - const nodes = selection ? selection.getNodes() : null; - if (nodes) { - const table = nodes.find( - (node) => - $isTableNode(node) && node.getKey() === tableObserver.tableNodeKey, - ); - if ($isTableNode(table)) { - const parentNode = table.getParent(); - if (!parentNode) { - return false; - } - table.remove(); - } - } + // If the selection is inside the table but should remove the whole table + // we expand the selection so that both the anchor and focus are outside + // the table and the editor's command listener will handle the delete + const isAnchorInside = tableNode.isParentOf(selection.anchor.getNode()); + const isFocusInside = tableNode.isParentOf(selection.focus.getNode()); + if (isAnchorInside !== isFocusInside) { + const tablePoint = isAnchorInside ? 'anchor' : 'focus'; + const outerPoint = isAnchorInside ? 'focus' : 'anchor'; + // Preserve the outer point + const {key, offset, type} = selection[outerPoint]; + // Expand the selection around the table + const newSelection = + tableNode[ + selection[tablePoint].isBefore(selection[outerPoint]) + ? 'selectPrevious' + : 'selectNext' + ](); + // Restore the outer point of the selection + newSelection[outerPoint].set(key, offset, type); + // Let the base implementation handle the rest return false; } @@ -363,15 +371,6 @@ export function applyTableHandlers( tableObserver.clearText(); return true; - } else if ($isRangeSelection(selection)) { - const tableCellNode = $findMatchingParent( - selection.anchor.getNode(), - (n) => $isTableCellNode(n), - ); - - if (!$isTableCellNode(tableCellNode)) { - return false; - } } return false;