Skip to content

Commit f0f5e1d

Browse files
rajat1saxenaRajat
andauthored
bug fix: text renderer crashing due to empty content nodes (#799)
Co-authored-by: Rajat <hi@rajatsaxena.dev>
1 parent e37f8ad commit f0f5e1d

2 files changed

Lines changed: 99 additions & 1 deletion

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { render, screen } from "@testing-library/react";
2+
3+
import { TextRenderer } from "../../../../../packages/page-blocks/src/components/text-renderer";
4+
5+
jest.mock("@courselit/text-editor", () => ({
6+
createExtensions: jest.fn(() => []),
7+
createId: jest.fn((text: string) =>
8+
text.toLowerCase().replace(/\s+/g, "-"),
9+
),
10+
emptyDoc: { type: "doc", content: [] },
11+
extractTextFromNode: jest.fn((node) =>
12+
node?.content?.map((child) => child.text ?? "").join(""),
13+
),
14+
}));
15+
16+
jest.mock("@tiptap/static-renderer", () => ({
17+
renderToReactElement: jest.fn(({ content }) => {
18+
const hasEmptyTextNode = (node: any): boolean => {
19+
if (node?.type === "text" && node.text === "") {
20+
return true;
21+
}
22+
23+
return Array.isArray(node?.content)
24+
? node.content.some(hasEmptyTextNode)
25+
: false;
26+
};
27+
28+
if (hasEmptyTextNode(content)) {
29+
throw new RangeError("Empty text nodes are not allowed");
30+
}
31+
32+
return <p>{content.content[0].content[0].text}</p>;
33+
}),
34+
}));
35+
36+
describe("TextRenderer", () => {
37+
it("renders content with empty text nodes", () => {
38+
expect(() =>
39+
render(
40+
<TextRenderer
41+
json={
42+
{
43+
type: "doc",
44+
content: [
45+
{
46+
type: "paragraph",
47+
content: [
48+
{
49+
type: "text",
50+
text: "Post body",
51+
},
52+
],
53+
},
54+
{
55+
type: "paragraph",
56+
content: [
57+
{
58+
type: "text",
59+
text: "",
60+
},
61+
],
62+
},
63+
],
64+
} as any
65+
}
66+
/>,
67+
),
68+
).not.toThrow();
69+
70+
expect(screen.getByText("Post body")).toBeInTheDocument();
71+
});
72+
});

packages/page-blocks/src/components/text-renderer.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,35 @@ interface TextRendererProps {
2121
className?: string;
2222
theme?: ThemeStyle;
2323
}
24+
25+
function removeEmptyTextNodes(node: any): any {
26+
if (!node || typeof node !== "object") {
27+
return node;
28+
}
29+
30+
if (node.type === "text" && node.text === "") {
31+
return undefined;
32+
}
33+
34+
if (!Array.isArray(node.content)) {
35+
return node;
36+
}
37+
38+
const content = node.content
39+
.map(removeEmptyTextNodes)
40+
.filter((child: any) => child !== undefined);
41+
42+
return {
43+
...node,
44+
content,
45+
};
46+
}
47+
2448
export function TextRenderer({ json, className, theme }: TextRendererProps) {
2549
const extensions = createExtensions();
26-
const content = ((json as any) ?? (emptyDoc as any)) as TextEditorContent;
50+
const content = removeEmptyTextNodes(
51+
(json as any) ?? (emptyDoc as any),
52+
) as TextEditorContent;
2753

2854
const rendered = renderToReactElement({
2955
extensions,

0 commit comments

Comments
 (0)