From 5c3463c44718e1f7a4d906e4877bebe1875631ac Mon Sep 17 00:00:00 2001 From: Vinay Kushwaha Date: Fri, 13 Dec 2024 21:16:28 +0530 Subject: [PATCH 1/2] [lexical-table][lexical-playground] Fix: Insertion of multiple rows --- .../__tests__/e2e/Tables.spec.mjs | 403 ++++++++++++++++++ .../plugins/TableActionMenuPlugin/index.tsx | 6 +- .../lexical-table/src/LexicalTableUtils.ts | 48 ++- 3 files changed, 437 insertions(+), 20 deletions(-) diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index 1b1ba63cd8b..cfd765381f7 100644 --- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs @@ -34,6 +34,7 @@ import { insertSampleImage, insertTable, insertTableColumnBefore, + insertTableRowAbove, insertTableRowBelow, IS_COLLAB, IS_LINUX, @@ -4674,4 +4675,406 @@ test.describe.parallel('Tables', () => { `, ); }); + + test('Can insert multiple rows above the selection', async ({ + page, + isCollab, + isPlainText, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + + await insertTable(page, 5, 5); + + await selectCellsFromTableCords( + page, + {x: 0, y: 1}, + {x: 4, y: 3}, + true, + false, + ); + + await insertTableRowAbove(page); + + await assertHTML( + page, + html` +


+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+ `, + ); + }); + + test('Can insert multiple rows below the selection', async ({ + page, + isCollab, + isPlainText, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + + await insertTable(page, 5, 5); + + await selectCellsFromTableCords( + page, + {x: 0, y: 1}, + {x: 4, y: 3}, + true, + false, + ); + + await insertTableRowBelow(page); + + await assertHTML( + page, + html` +


+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+ `, + ); + }); }); diff --git a/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx b/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx index 2e17272cbf7..b5275c7cd22 100644 --- a/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx +++ b/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx @@ -309,11 +309,13 @@ function TableActionMenu({ const insertTableRowAtSelection = useCallback( (shouldInsertAfter: boolean) => { editor.update(() => { - $insertTableRow__EXPERIMENTAL(shouldInsertAfter); + for (let i = 0; i < selectionCounts.rows; i++) { + $insertTableRow__EXPERIMENTAL(shouldInsertAfter); + } onClose(); }); }, - [editor, onClose], + [editor, onClose, selectionCounts.rows], ); const insertTableColumnAtSelection = useCallback( diff --git a/packages/lexical-table/src/LexicalTableUtils.ts b/packages/lexical-table/src/LexicalTableUtils.ts index 6c354285ec4..84e4ab3f5a7 100644 --- a/packages/lexical-table/src/LexicalTableUtils.ts +++ b/packages/lexical-table/src/LexicalTableUtils.ts @@ -258,20 +258,31 @@ export function $insertTableRow__EXPERIMENTAL( $isRangeSelection(selection) || $isTableSelection(selection), 'Expected a RangeSelection or TableSelection', ); + const anchor = selection.anchor.getNode(); const focus = selection.focus.getNode(); + const [anchorCell] = $getNodeTriplet(anchor); const [focusCell, , grid] = $getNodeTriplet(focus); - const [gridMap, focusCellMap] = $computeTableMap(grid, focusCell, focusCell); + const [gridMap, focusCellMap, anchorCellMap] = $computeTableMap( + grid, + focusCell, + anchorCell, + ); const columnCount = gridMap[0].length; + const {startRow: anchorStartRow} = anchorCellMap; const {startRow: focusStartRow} = focusCellMap; let insertedRow: TableRowNode | null = null; if (insertAfter) { - const focusEndRow = focusStartRow + focusCell.__rowSpan - 1; - const focusEndRowMap = gridMap[focusEndRow]; + const insertAfterEndRow = + Math.max( + focusStartRow + focusCell.__rowSpan, + anchorStartRow + anchorCell.__rowSpan, + ) - 1; + const insertAfterEndRowMap = gridMap[insertAfterEndRow]; const newRow = $createTableRowNode(); for (let i = 0; i < columnCount; i++) { - const {cell, startRow} = focusEndRowMap[i]; - if (startRow + cell.__rowSpan - 1 <= focusEndRow) { - const currentCell = focusEndRowMap[i].cell as TableCellNode; + const {cell, startRow} = insertAfterEndRowMap[i]; + if (startRow + cell.__rowSpan - 1 <= insertAfterEndRow) { + const currentCell = insertAfterEndRowMap[i].cell as TableCellNode; const currentCellHeaderState = currentCell.__headerState; const headerState = getHeaderState( @@ -286,20 +297,21 @@ export function $insertTableRow__EXPERIMENTAL( cell.setRowSpan(cell.__rowSpan + 1); } } - const focusEndRowNode = grid.getChildAtIndex(focusEndRow); + const insertAfterEndRowNode = grid.getChildAtIndex(insertAfterEndRow); invariant( - $isTableRowNode(focusEndRowNode), - 'focusEndRow is not a TableRowNode', + $isTableRowNode(insertAfterEndRowNode), + 'insertAfterEndRow is not a TableRowNode', ); - focusEndRowNode.insertAfter(newRow); + insertAfterEndRowNode.insertAfter(newRow); insertedRow = newRow; } else { - const focusStartRowMap = gridMap[focusStartRow]; + const insertBeforeStartRow = Math.min(focusStartRow, anchorStartRow); + const insertBeforeStartRowMap = gridMap[insertBeforeStartRow]; const newRow = $createTableRowNode(); for (let i = 0; i < columnCount; i++) { - const {cell, startRow} = focusStartRowMap[i]; - if (startRow === focusStartRow) { - const currentCell = focusStartRowMap[i].cell as TableCellNode; + const {cell, startRow} = insertBeforeStartRowMap[i]; + if (startRow === insertBeforeStartRow) { + const currentCell = insertBeforeStartRowMap[i].cell as TableCellNode; const currentCellHeaderState = currentCell.__headerState; const headerState = getHeaderState( @@ -314,12 +326,12 @@ export function $insertTableRow__EXPERIMENTAL( cell.setRowSpan(cell.__rowSpan + 1); } } - const focusStartRowNode = grid.getChildAtIndex(focusStartRow); + const insertBeforeStartRowNode = grid.getChildAtIndex(insertBeforeStartRow); invariant( - $isTableRowNode(focusStartRowNode), - 'focusEndRow is not a TableRowNode', + $isTableRowNode(insertBeforeStartRowNode), + 'insertBeforeStartRow is not a TableRowNode', ); - focusStartRowNode.insertBefore(newRow); + insertBeforeStartRowNode.insertBefore(newRow); insertedRow = newRow; } return insertedRow; From f961fd6ea63fd4867e9408ae7cb97d4f7dad24c4 Mon Sep 17 00:00:00 2001 From: Vinay Kushwaha Date: Sat, 14 Dec 2024 12:55:02 +0530 Subject: [PATCH 2/2] skipped e2e test for isCollab --- packages/lexical-playground/__tests__/e2e/Tables.spec.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index e6732a57158..670e78ff850 100644 --- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs @@ -4683,6 +4683,7 @@ test.describe.parallel('Tables', () => { }) => { await initialize({isCollab, page}); test.skip(isPlainText); + test.skip(isCollab); await focusEditor(page); @@ -4884,6 +4885,7 @@ test.describe.parallel('Tables', () => { }) => { await initialize({isCollab, page}); test.skip(isPlainText); + test.skip(isCollab); await focusEditor(page);