fix: resolve rich text rendering and data mapping issues
Hardened rich text conversion logic to handle malformed Tiptap documents and added null checks for CMS data in About section.
This commit is contained in:
133
lib/richtext.ts
133
lib/richtext.ts
@@ -10,62 +10,83 @@ import Highlight from "@tiptap/extension-highlight";
|
||||
import { FontFamily } from "@/lib/tiptap/fontFamily";
|
||||
|
||||
export function richTextToSafeHtml(doc: JSONContent): string {
|
||||
const raw = generateHTML(doc, [
|
||||
StarterKit,
|
||||
Underline,
|
||||
Link.configure({
|
||||
openOnClick: false,
|
||||
autolink: false,
|
||||
HTMLAttributes: { rel: "noopener noreferrer", target: "_blank" },
|
||||
}),
|
||||
TextStyle,
|
||||
FontFamily,
|
||||
Color,
|
||||
Highlight,
|
||||
]);
|
||||
if (!doc || typeof doc !== "object" || Object.keys(doc).length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return sanitizeHtml(raw, {
|
||||
allowedTags: [
|
||||
"p",
|
||||
"br",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"blockquote",
|
||||
"strong",
|
||||
"em",
|
||||
"u",
|
||||
"a",
|
||||
"ul",
|
||||
"ol",
|
||||
"li",
|
||||
"code",
|
||||
"pre",
|
||||
"span"
|
||||
],
|
||||
allowedAttributes: {
|
||||
a: ["href", "rel", "target"],
|
||||
span: ["style"],
|
||||
code: ["class"],
|
||||
pre: ["class"],
|
||||
p: ["class"],
|
||||
h1: ["class"],
|
||||
h2: ["class"],
|
||||
h3: ["class"],
|
||||
blockquote: ["class"],
|
||||
ul: ["class"],
|
||||
ol: ["class"],
|
||||
li: ["class"]
|
||||
},
|
||||
allowedSchemes: ["http", "https", "mailto"],
|
||||
allowProtocolRelative: false,
|
||||
allowedStyles: {
|
||||
span: {
|
||||
color: [/^#[0-9a-fA-F]{3,8}$/],
|
||||
"background-color": [/^#[0-9a-fA-F]{3,8}$/],
|
||||
"font-family": [/^(Inter|ui-sans-serif|ui-serif|ui-monospace)$/],
|
||||
// Ensure type is present to satisfy Tiptap requirement
|
||||
const typedDoc = { ...doc };
|
||||
if (!typedDoc.type) {
|
||||
typedDoc.type = "doc";
|
||||
}
|
||||
|
||||
// Ensure content is an array
|
||||
if (!typedDoc.content) {
|
||||
typedDoc.content = [];
|
||||
}
|
||||
|
||||
try {
|
||||
const raw = generateHTML(typedDoc, [
|
||||
StarterKit,
|
||||
Underline,
|
||||
Link.configure({
|
||||
openOnClick: false,
|
||||
autolink: false,
|
||||
HTMLAttributes: { rel: "noopener noreferrer", target: "_blank" },
|
||||
}),
|
||||
TextStyle,
|
||||
FontFamily,
|
||||
Color,
|
||||
Highlight,
|
||||
]);
|
||||
|
||||
return sanitizeHtml(raw, {
|
||||
allowedTags: [
|
||||
"p",
|
||||
"br",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"blockquote",
|
||||
"strong",
|
||||
"em",
|
||||
"u",
|
||||
"a",
|
||||
"ul",
|
||||
"ol",
|
||||
"li",
|
||||
"code",
|
||||
"pre",
|
||||
"span"
|
||||
],
|
||||
allowedAttributes: {
|
||||
a: ["href", "rel", "target"],
|
||||
span: ["style"],
|
||||
code: ["class"],
|
||||
pre: ["class"],
|
||||
p: ["class"],
|
||||
h1: ["class"],
|
||||
h2: ["class"],
|
||||
h3: ["class"],
|
||||
blockquote: ["class"],
|
||||
ul: ["class"],
|
||||
ol: ["class"],
|
||||
li: ["class"]
|
||||
},
|
||||
},
|
||||
});
|
||||
allowedSchemes: ["http", "https", "mailto"],
|
||||
allowProtocolRelative: false,
|
||||
allowedStyles: {
|
||||
span: {
|
||||
color: [/^#[0-9a-fA-F]{3,8}$/],
|
||||
"background-color": [/^#[0-9a-fA-F]{3,8}$/],
|
||||
"font-family": [/^(Inter|ui-sans-serif|ui-serif|ui-monospace)$/],
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.error("Error generating HTML from rich text:", error);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user