diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index b6a683f022e..670e78ff850 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,408 @@ test.describe.parallel('Tables', () => { `, ); }); + + test('Can insert multiple rows above the selection', async ({ + page, + isCollab, + isPlainText, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + test.skip(isCollab); + + 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); + test.skip(isCollab); + + 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-playground/src/themes/PlaygroundEditorTheme.css b/packages/lexical-playground/src/themes/PlaygroundEditorTheme.css index 2e985c4dc8d..d7041331a52 100644 --- a/packages/lexical-playground/src/themes/PlaygroundEditorTheme.css +++ b/packages/lexical-playground/src/themes/PlaygroundEditorTheme.css @@ -213,6 +213,14 @@ padding: 6px 8px; position: relative; outline: none; + overflow: auto; +} +/* + A firefox workaround to allow scrolling of overflowing table cell + ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1904159 +*/ +.PlaygroundEditorTheme__tableCell > * { + overflow: inherit; } .PlaygroundEditorTheme__tableCellResizer { position: absolute; 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;