Skip to content

Commit

Permalink
fix(tables): Add missing paragraph node when copying empty cells from…
Browse files Browse the repository at this point in the history
… external resources (facebook#5670)
  • Loading branch information
vect-spasquereau authored Mar 6, 2024
1 parent 0acc1f5 commit 9f84191
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/lexical-table/src/LexicalTableCellNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@ export function convertTableCellNodeElement(
}

return {
after: (childLexicalNodes) => {
if (childLexicalNodes.length === 0) {
childLexicalNodes.push($createParagraphNode());
}
return childLexicalNodes;
},
forChild: (lexicalNode, parentLexicalNode) => {
if ($isTableCellNode(parentLexicalNode) && !$isElementNode(lexicalNode)) {
const paragraphNode = $createParagraphNode();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {$createTableCellNode, TableCellHeaderStates} from '@lexical/table';
import {initializeUnitTest} from 'lexical/src/__tests__/utils';

const editorConfig = Object.freeze({
namespace: '',
theme: {
tableCell: 'test-table-cell-class',
},
});

describe('LexicalTableCellNode tests', () => {
initializeUnitTest((testEnv) => {
test('TableCellNode.constructor', async () => {
const {editor} = testEnv;

await editor.update(() => {
const cellNode = $createTableCellNode(TableCellHeaderStates.NO_STATUS);

expect(cellNode).not.toBe(null);
});

expect(() =>
$createTableCellNode(TableCellHeaderStates.NO_STATUS),
).toThrow();
});

test('TableCellNode.createDOM()', async () => {
const {editor} = testEnv;

await editor.update(() => {
const cellNode = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
expect(cellNode.createDOM(editorConfig).outerHTML).toBe(
`<td class="${editorConfig.theme.tableCell}"></td>`,
);

const headerCellNode = $createTableCellNode(TableCellHeaderStates.ROW);
expect(headerCellNode.createDOM(editorConfig).outerHTML).toBe(
`<th class="${editorConfig.theme.tableCell}"></th>`,
);

const colSpan = 2;
const cellWithRowSpanNode = $createTableCellNode(
TableCellHeaderStates.NO_STATUS,
colSpan,
);
expect(cellWithRowSpanNode.createDOM(editorConfig).outerHTML).toBe(
`<td colspan="${colSpan}" class="${editorConfig.theme.tableCell}"></td>`,
);

const cellWidth = 200;
const cellWithCustomWidthNode = $createTableCellNode(
TableCellHeaderStates.NO_STATUS,
undefined,
cellWidth,
);
expect(cellWithCustomWidthNode.createDOM(editorConfig).outerHTML).toBe(
`<td style="width: ${cellWidth}px;" class="${editorConfig.theme.tableCell}"></td>`,
);
});
});
});
});
45 changes: 42 additions & 3 deletions packages/lexical-table/src/__tests__/unit/LexicalTableNode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,39 @@
*
*/

import {$insertDataTransferForRichText} from '@lexical/clipboard';
import {$createTableNode} from '@lexical/table';
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
import {
$createParagraphNode,
$getRoot,
$getSelection,
$isRangeSelection,
} from 'lexical';
import {
DataTransferMock,
initializeUnitTest,
invariant,
} from 'lexical/src/__tests__/utils';

const editorConfig = Object.freeze({
namespace: '',
theme: {
TableCellHeaderStates: 'test-table-row-class',
table: 'test-table-class',
tableCell: 'test-table-cell-class',
},
});

describe('LexicalTableNode tests', () => {
initializeUnitTest((testEnv) => {
beforeEach(async () => {
const {editor} = testEnv;
await editor.update(() => {
const root = $getRoot();
const paragraph = $createParagraphNode();
root.append(paragraph);
paragraph.select();
});
});

test('TableNode.constructor', async () => {
const {editor} = testEnv;

Expand All @@ -43,5 +62,25 @@ describe('LexicalTableNode tests', () => {
);
});
});

test('Copy table from an external source', async () => {
const {editor} = testEnv;

const dataTransfer = new DataTransferMock();
dataTransfer.setData(
'text/html',
'<html><body><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-16a69100-7fff-6cb9-b829-cb1def16a58d"><div dir="ltr" style="margin-left:0pt;" align="left"><table style="border:none;border-collapse:collapse;table-layout:fixed;width:468pt"><colgroup><col /><col /></colgroup><tbody><tr style="height:22.015pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Hello there</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">General Kenobi!</span></p></td></tr><tr style="height:22.015pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Lexical is nice</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><br /></td></tr></tbody></table></div></b><!--EndFragment--></body></html>',
);
await editor.update(() => {
const selection = $getSelection();
invariant($isRangeSelection(selection), 'isRangeSelection(selection)');
$insertDataTransferForRichText(dataTransfer, selection, editor);
});
// Make sure paragraph is inserted inside empty cells
const emptyCell = '<td><p><br></p></td>';
expect(testEnv.innerHTML).toBe(
`<table><tr><td><p dir="ltr"><span data-lexical-text="true">Hello there</span></p></td><td><p dir="ltr"><span data-lexical-text="true">General Kenobi!</span></p></td></tr><tr><td><p dir="ltr"><span data-lexical-text="true">Lexical is nice</span></p></td>${emptyCell}</tr></table>`,
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {$createTableRowNode} from '@lexical/table';
import {initializeUnitTest} from 'lexical/src/__tests__/utils';

const editorConfig = Object.freeze({
namespace: '',
theme: {
tableRow: 'test-table-row-class',
},
});

describe('LexicalTableRowNode tests', () => {
initializeUnitTest((testEnv) => {
test('TableRowNode.constructor', async () => {
const {editor} = testEnv;

await editor.update(() => {
const rowNode = $createTableRowNode();

expect(rowNode).not.toBe(null);
});

expect(() => $createTableRowNode()).toThrow();
});

test('TableRowNode.createDOM()', async () => {
const {editor} = testEnv;

await editor.update(() => {
const rowNode = $createTableRowNode();
expect(rowNode.createDOM(editorConfig).outerHTML).toBe(
`<tr class="${editorConfig.theme.tableRow}"></tr>`,
);

const rowHeight = 36;
const rowWithCustomHeightNode = $createTableRowNode(36);
expect(rowWithCustomHeightNode.createDOM(editorConfig).outerHTML).toBe(
`<tr style="height: ${rowHeight}px;" class="${editorConfig.theme.tableRow}"></tr>`,
);
});
});
});
});

0 comments on commit 9f84191

Please sign in to comment.