fix(richtext-lexical): add missing line-breaks to plaintext conversion (#11951)
### What? Adds line-breaks after headings, lists, list items, tables, table rows, and table cells when converting lexical content to plaintext. ### Why? Currently text from those nodes is concatenated without a separator. ### How? Adds handling for these nodes to the plain text converter.
This commit is contained in:
@@ -5,6 +5,12 @@ import type {
|
||||
SerializedParagraphNode,
|
||||
SerializedTextNode,
|
||||
SerializedLineBreakNode,
|
||||
SerializedHeadingNode,
|
||||
SerializedListItemNode,
|
||||
SerializedListNode,
|
||||
SerializedTableRowNode,
|
||||
SerializedTableNode,
|
||||
SerializedTableCellNode,
|
||||
} from '../../../nodeTypes.js'
|
||||
import { convertLexicalToPlaintext } from './sync/index.js'
|
||||
|
||||
@@ -51,7 +57,83 @@ function paragraphNode(children: DefaultNodeTypes[]): SerializedParagraphNode {
|
||||
}
|
||||
}
|
||||
|
||||
function rootNode(nodes: DefaultNodeTypes[]): DefaultTypedEditorState {
|
||||
function headingNode(children: DefaultNodeTypes[]): SerializedHeadingNode {
|
||||
return {
|
||||
type: 'heading',
|
||||
children,
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
textFormat: 0,
|
||||
tag: 'h1',
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
function listItemNode(children: DefaultNodeTypes[]): SerializedListItemNode {
|
||||
return {
|
||||
type: 'listitem',
|
||||
children,
|
||||
checked: false,
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
value: 0,
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
function listNode(children: DefaultNodeTypes[]): SerializedListNode {
|
||||
return {
|
||||
type: 'list',
|
||||
children,
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
listType: 'bullet',
|
||||
start: 0,
|
||||
tag: 'ul',
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
function tableNode(children: (DefaultNodeTypes | SerializedTableRowNode)[]): SerializedTableNode {
|
||||
return {
|
||||
type: 'table',
|
||||
children,
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
function tableRowNode(
|
||||
children: (DefaultNodeTypes | SerializedTableCellNode)[],
|
||||
): SerializedTableRowNode {
|
||||
return {
|
||||
type: 'tablerow',
|
||||
children,
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
function tableCellNode(children: DefaultNodeTypes[]): SerializedTableCellNode {
|
||||
return {
|
||||
type: 'tablecell',
|
||||
children,
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
headerState: 0,
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
function rootNode(nodes: (DefaultNodeTypes | SerializedTableNode)[]): DefaultTypedEditorState {
|
||||
return {
|
||||
root: {
|
||||
type: 'root',
|
||||
@@ -72,7 +154,6 @@ describe('convertLexicalToPlaintext', () => {
|
||||
data,
|
||||
})
|
||||
|
||||
console.log('plaintext', plaintext)
|
||||
expect(plaintext).toBe('Basic Text')
|
||||
})
|
||||
|
||||
@@ -111,4 +192,67 @@ describe('convertLexicalToPlaintext', () => {
|
||||
|
||||
expect(plaintext).toBe('Basic Text\tNext Line')
|
||||
})
|
||||
|
||||
it('ensure new lines are added between paragraphs', () => {
|
||||
const data: DefaultTypedEditorState = rootNode([
|
||||
paragraphNode([textNode('Basic text')]),
|
||||
paragraphNode([textNode('Next block-node')]),
|
||||
])
|
||||
|
||||
const plaintext = convertLexicalToPlaintext({
|
||||
data,
|
||||
})
|
||||
|
||||
expect(plaintext).toBe('Basic text\n\nNext block-node')
|
||||
})
|
||||
|
||||
it('ensure new lines are added between heading nodes', () => {
|
||||
const data: DefaultTypedEditorState = rootNode([
|
||||
headingNode([textNode('Basic text')]),
|
||||
headingNode([textNode('Next block-node')]),
|
||||
])
|
||||
|
||||
const plaintext = convertLexicalToPlaintext({
|
||||
data,
|
||||
})
|
||||
|
||||
expect(plaintext).toBe('Basic text\n\nNext block-node')
|
||||
})
|
||||
|
||||
it('ensure new lines are added between list items and lists', () => {
|
||||
const data: DefaultTypedEditorState = rootNode([
|
||||
listNode([listItemNode([textNode('First item')]), listItemNode([textNode('Second item')])]),
|
||||
listNode([listItemNode([textNode('Next list')])]),
|
||||
])
|
||||
|
||||
const plaintext = convertLexicalToPlaintext({
|
||||
data,
|
||||
})
|
||||
|
||||
expect(plaintext).toBe('First item\nSecond item\n\nNext list')
|
||||
})
|
||||
|
||||
it('ensure new lines are added between tables, table rows, and table cells', () => {
|
||||
const data: DefaultTypedEditorState = rootNode([
|
||||
tableNode([
|
||||
tableRowNode([
|
||||
tableCellNode([textNode('Cell 1, Row 1')]),
|
||||
tableCellNode([textNode('Cell 2, Row 1')]),
|
||||
]),
|
||||
tableRowNode([
|
||||
tableCellNode([textNode('Cell 1, Row 2')]),
|
||||
tableCellNode([textNode('Cell 2, Row 2')]),
|
||||
]),
|
||||
]),
|
||||
tableNode([tableRowNode([tableCellNode([textNode('Cell in Table 2')])])]),
|
||||
])
|
||||
|
||||
const plaintext = convertLexicalToPlaintext({
|
||||
data,
|
||||
})
|
||||
|
||||
expect(plaintext).toBe(
|
||||
'Cell 1, Row 1 | Cell 2, Row 1\nCell 1, Row 2 | Cell 2, Row 2\n\nCell in Table 2',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -86,11 +86,25 @@ export function convertLexicalNodesToPlaintext({
|
||||
}
|
||||
} else {
|
||||
// Default plaintext converter heuristic
|
||||
if (node.type === 'paragraph') {
|
||||
if (
|
||||
node.type === 'paragraph' ||
|
||||
node.type === 'heading' ||
|
||||
node.type === 'list' ||
|
||||
node.type === 'table'
|
||||
) {
|
||||
if (plainTextArray?.length) {
|
||||
// Only add a new line if there is already text in the array
|
||||
plainTextArray.push('\n\n')
|
||||
}
|
||||
} else if (node.type === 'listitem' || node.type === 'tablerow') {
|
||||
if (plainTextArray?.length) {
|
||||
// Only add a new line if there is already text in the array
|
||||
plainTextArray.push('\n')
|
||||
}
|
||||
} else if (node.type === 'tablecell') {
|
||||
if (plainTextArray?.length) {
|
||||
plainTextArray.push(' | ')
|
||||
}
|
||||
} else if (node.type === 'linebreak') {
|
||||
plainTextArray.push('\n')
|
||||
} else if (node.type === 'tab') {
|
||||
|
||||
Reference in New Issue
Block a user