From 5ada460517ae52c20ad22acf4e9a67f2c86d3390 Mon Sep 17 00:00:00 2001
From: Matthew Lipski Paragraph list item Caption Caption Paragraph list item Caption Paragraph list item Caption Paragraph 1 Paragraph 1 Bullet List Item 1Heading
2Heading
2Heading
2Heading 1
Heading 1
-
+
Heading
@@ -6,7 +6,7 @@
Paragraph
Paragraph
+
Plain Red Text Blue Background diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/contextParagraph/basic.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/contextParagraph/basic.html index ca43744a1..f7b852f0f 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/contextParagraph/basic.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/contextParagraph/basic.html @@ -1 +1 @@ -
Hello World
\ No newline at end of file +Hello World
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/lineBreaks.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/lineBreaks.html index 2971f1105..ff100b250 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/lineBreaks.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/lineBreaks.html @@ -1 +1 @@ -Hello World
\ No newline at end of file +Hello World
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/nested.html index 6b2e1554c..863e2204e 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/nested.html @@ -1,3 +1,3 @@ -Hello World
-Hello World
-Hello World
\ No newline at end of file +Hello World
+Hello World
+Hello World
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/styled.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/styled.html index 7e7e60707..962c4cea9 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/styled.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/customParagraph/styled.html @@ -1,5 +1,6 @@React Custom Paragraph
\ No newline at end of file +React Custom Paragraph
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/nested.html index 68e209191..677ec39d3 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/nested.html @@ -1,3 +1,15 @@ -Custom React Paragraph
-Nested React Custom Paragraph 1
-Nested React Custom Paragraph 2
\ No newline at end of file +Custom React Paragraph
+Nested React Custom Paragraph 1
+Nested React Custom Paragraph 2
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html index aff3d3abc..4d0b88207 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html @@ -1,6 +1,7 @@
Date: Fri, 25 Apr 2025 14:03:11 +0200
Subject: [PATCH 3/4] Implemented PR feedback
---
.../html/util/serializeBlocksExternalHTML.ts | 28 +-
.../core/src/blocks/defaultBlockHelpers.ts | 56 ++-
packages/core/src/blocks/defaultProps.ts | 18 +
.../text/html/basicBlocksWithProps.html | 8 +-
.../__snapshots__/html/complex/misc.html | 6 +-
.../__snapshots__/html/paragraph/styled.html | 4 +-
.../html/backgroundColorProp.json | 23 +-
.../html/backgroundColorStyle.json | 16 +-
.../__snapshots__/html/textColorProp.json | 23 +-
.../__snapshots__/html/textColorStyle.json | 16 +-
.../parse/parseTestInstances.ts | 464 +++++++++---------
11 files changed, 341 insertions(+), 321 deletions(-)
diff --git a/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts b/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts
index c3084bbae..a39c2d7b9 100644
--- a/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts
+++ b/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts
@@ -144,15 +144,19 @@ function serializeBlock<
// We wrap the output in an `li` element for list items, and so we want to
// add the attributes to that element instead as it is the "root".
if (!listType) {
- // TODO: This is specifically for default blocks, as default props get
- // rendered out to inline styles instead of `data-*` attributes for
- // external HTML. Will need to revisit this when we convert default
- // blocks to use the custom block API.
+ // Copies the styles and prop-related attributes from the `blockContent`
+ // element onto its first child, as the `blockContent` element is omitted
+ // from external HTML. This is so prop data is preserved via `data-*`
+ // attributes or inline styles.
+ //
+ // The styles are specifically for default props on default blocks, as
+ // they get converted from `data-*` attributes for external HTML. Will
+ // need to revisit this when we convert default blocks to use the custom
+ // block API.
const style = ret.dom.getAttribute("style");
if (style) {
(ret.dom.firstChild! as HTMLElement).setAttribute("style", style);
}
-
for (const attr of blockContentDataAttributes) {
(ret.dom.firstChild! as HTMLElement).setAttribute(
attr.name,
@@ -189,15 +193,19 @@ function serializeBlock<
}
const li = doc.createElement("li");
- // TODO: This is specifically for default blocks, as default props get
- // rendered out to inline styles instead of `data-*` attributes for
- // external HTML. Will need to revisit this when we convert default
- // blocks to use the custom block API.
+ // Copies the styles and prop-related attributes from the `blockContent`
+ // element onto its first child, as the `blockContent` element is omitted
+ // from external HTML. This is so prop data is preserved via `data-*`
+ // attributes or inline styles.
+ //
+ // The styles are specifically for default props on default blocks, as
+ // they get converted from `data-*` attributes for external HTML. Will
+ // need to revisit this when we convert default blocks to use the custom
+ // block API.
const style = ret.dom.getAttribute("style");
if (style) {
li.setAttribute("style", style);
}
-
for (const attr of blockContentDataAttributes) {
li.setAttribute(attr.name, attr.value);
}
diff --git a/packages/core/src/blocks/defaultBlockHelpers.ts b/packages/core/src/blocks/defaultBlockHelpers.ts
index 295f06d70..b8220e310 100644
--- a/packages/core/src/blocks/defaultBlockHelpers.ts
+++ b/packages/core/src/blocks/defaultBlockHelpers.ts
@@ -1,5 +1,6 @@
import { blockToNode } from "../api/nodeConversions/blockToNode.js";
import type { BlockNoteEditor } from "../editor/BlockNoteEditor.js";
+import { COLORS_DEFAULT } from "../editor/defaultColors.js";
import type {
BlockNoDefaults,
BlockSchema,
@@ -93,18 +94,58 @@ export const defaultBlockToHTML = <
);
}
- // TODO: This is obviously pretty hacky - will need to revisit this when we
- // convert default blocks to use the custom block API.
+ // When exporting to external HTML, we convert from `data-*` attributes to
+ // inline styles properties which can be understood by external applications.
+ //
+ // Note: This is a bit hacky to do this here as we're just hardcoding this for
+ // props on default blocks. We should revisit this when we migrate internal
+ // blocks to use the custom blocks API.
if (external) {
const dom = renderSpec.dom as HTMLElement;
if (dom.hasAttribute("data-background-color")) {
- dom.style.backgroundColor = dom.getAttribute("data-background-color")!;
+ const backgroundColor = dom.getAttribute("data-background-color")!;
+
+ // If the background color is one of the default colors, we set the
+ // color's hex code from the default theme, as this will look nicer than
+ // using regular CSS colors. For example, instead of
+ // `background-color: red`, we use `background-color: #fbe4e4`.
+ if (backgroundColor in COLORS_DEFAULT) {
+ const cssVariableName =
+ `--blocknote-background-${backgroundColor}` as any;
+
+ dom.style.setProperty(
+ cssVariableName,
+ COLORS_DEFAULT[backgroundColor as keyof typeof COLORS_DEFAULT]
+ .background
+ );
+ dom.style.backgroundColor = `var(${cssVariableName})`;
+ } else {
+ dom.style.backgroundColor = backgroundColor;
+ }
+
dom.removeAttribute("data-background-color");
}
if (dom.hasAttribute("data-text-color")) {
- dom.style.color = dom.getAttribute("data-text-color")!;
+ const textColor = dom.getAttribute("data-text-color")!;
+
+ // If the text color is one of the default colors, we set the color's hex
+ // code from the default theme, as this will look nicer than using regular
+ // CSS colors. For example, instead of `color: red`, we use
+ // `color: #e03e3e`.
+ if (textColor in COLORS_DEFAULT) {
+ const cssVariableName = `--blocknote-text-${textColor}` as any;
+
+ dom.style.setProperty(
+ cssVariableName,
+ COLORS_DEFAULT[textColor as keyof typeof COLORS_DEFAULT].text
+ );
+ dom.style.color = `var(${cssVariableName})`;
+ } else {
+ dom.style.color = textColor;
+ }
+
dom.removeAttribute("data-text-color");
}
@@ -112,6 +153,13 @@ export const defaultBlockToHTML = <
dom.style.textAlign = dom.getAttribute("data-text-alignment")!;
dom.removeAttribute("data-text-alignment");
}
+
+ // We also remove the `data-level` attribute for heading blocks, as this
+ // information can be inferred from whether a `h1`, `h2`, or `h3 tag is
+ // used.
+ if (dom.hasAttribute("data-level")) {
+ dom.removeAttribute("data-level");
+ }
}
return renderSpec as {
diff --git a/packages/core/src/blocks/defaultProps.ts b/packages/core/src/blocks/defaultProps.ts
index a524b19a9..44193cd01 100644
--- a/packages/core/src/blocks/defaultProps.ts
+++ b/packages/core/src/blocks/defaultProps.ts
@@ -30,6 +30,16 @@ const getBackgroundColorAttribute = (
}
if (element.style.backgroundColor) {
+ // Check if `element.style.backgroundColor` matches the string:
+ // `var(--blocknote-background- Paragraph 1 Paragraph 1 Numbered List Item 1 Bullet List Item 1 Paragraph Paragraph
+
Plain
Red Text
Blue Background
diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorProp.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorProp.json
index f2ebc5ed9..15f5d0729 100644
--- a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorProp.json
+++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorProp.json
@@ -4,30 +4,13 @@
"content": [
{
"styles": {},
- "text": "Red Background",
+ "text": "Blue Background",
"type": "text",
},
],
"id": "1",
"props": {
- "backgroundColor": "red",
- "textAlignment": "left",
- "textColor": "default",
- },
- "type": "paragraph",
- },
- {
- "children": [],
- "content": [
- {
- "styles": {},
- "text": "Green Background",
- "type": "text",
- },
- ],
- "id": "2",
- "props": {
- "backgroundColor": "green",
+ "backgroundColor": "blue",
"textAlignment": "left",
"textColor": "default",
},
@@ -42,7 +25,7 @@
"type": "text",
},
],
- "id": "3",
+ "id": "2",
"props": {
"backgroundColor": "blue",
"textAlignment": "left",
diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorStyle.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorStyle.json
index 0f1dfc15e..8afe915ba 100644
--- a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorStyle.json
+++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/backgroundColorStyle.json
@@ -4,21 +4,9 @@
"content": [
{
"styles": {
- "backgroundColor": "red",
- },
- "text": "Red Background",
- "type": "text",
- },
- {
- "styles": {},
- "text": " ",
- "type": "text",
- },
- {
- "styles": {
- "backgroundColor": "green",
+ "backgroundColor": "blue",
},
- "text": "Green Background",
+ "text": "Blue Background",
"type": "text",
},
{
diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorProp.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorProp.json
index 7480fcab3..4211362bf 100644
--- a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorProp.json
+++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorProp.json
@@ -4,7 +4,7 @@
"content": [
{
"styles": {},
- "text": "Red Paragraph",
+ "text": "Blue Text",
"type": "text",
},
],
@@ -12,7 +12,7 @@
"props": {
"backgroundColor": "default",
"textAlignment": "left",
- "textColor": "red",
+ "textColor": "blue",
},
"type": "paragraph",
},
@@ -21,28 +21,11 @@
"content": [
{
"styles": {},
- "text": "Green Paragraph",
+ "text": "Blue Text",
"type": "text",
},
],
"id": "2",
- "props": {
- "backgroundColor": "default",
- "textAlignment": "left",
- "textColor": "green",
- },
- "type": "paragraph",
- },
- {
- "children": [],
- "content": [
- {
- "styles": {},
- "text": "Blue Paragraph",
- "type": "text",
- },
- ],
- "id": "3",
"props": {
"backgroundColor": "default",
"textAlignment": "left",
diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorStyle.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorStyle.json
index faeeee527..e78e1a4b3 100644
--- a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorStyle.json
+++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/textColorStyle.json
@@ -4,21 +4,9 @@
"content": [
{
"styles": {
- "textColor": "red",
- },
- "text": "Red Text",
- "type": "text",
- },
- {
- "styles": {},
- "text": " ",
- "type": "text",
- },
- {
- "styles": {
- "textColor": "green",
+ "textColor": "blue",
},
- "text": "Green Text",
+ "text": "Blue Text",
"type": "text",
},
{
diff --git a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
index 375bfd098..fb65ccccd 100644
--- a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
+++ b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
@@ -20,11 +20,11 @@ export const parseTestInstancesHTML: TestInstance<
testCase: {
name: "basicBlockTypes",
content: ` Paragraph None Bold Italic Underline Paragraph None Bold Italic Underline Bullet List Item Bullet List Item Nested Bullet List Item Nested Bullet List Item Bullet List Item Numbered List Item Numbered List Item Nested Numbered List Item Nested Numbered List Item Numbered List Item Checked List Item Checked List Item Nested Checked List Item Nested Checked List Item Checked List Item Bullet List Item Bullet List Item Nested Bullet List Item Nested Bullet List Item Bullet List Item Numbered List Item Numbered List Item Nested Numbered List Item Nested Numbered List Item Numbered List Item Checked List Item Checked List Item Nested Checked List Item Nested Checked List Item Checked List ItemHeading 1
+Heading 1
-
+
+
Heading
@@ -6,7 +6,9 @@
2
-
`,
+ },
+ executeTest: testParseHTML,
+ },
+ {
+ testCase: {
+ name: "nestedLists",
+ content: `Heading 1
-Heading 2
-Heading 3
-Strikethrough AllHeading 2
+ Heading 3
+ Strikethrough All
-
`,
- },
- executeTest: testParseHTML,
- },
- {
- testCase: {
- name: "nestedLists",
- content: `
-
-
-
-
-
-
-
-
-
-
+
`,
+
+
+
+
+
+
+
+
+
+
+
`,
},
executeTest: testParseHTML,
},
@@ -115,67 +115,67 @@ export const parseTestInstancesHTML: TestInstance<
testCase: {
name: "nestedListsWithParagraphs",
content: `
+
+
-
-
-
-
-
-
-
-
-
`,
+
-
-
+
+
+
+
+
+
+
`,
},
executeTest: testParseHTML,
},
@@ -183,49 +183,49 @@ export const parseTestInstancesHTML: TestInstance<
testCase: {
name: "mixedNestedLists",
content: `
+
+
-
-
-
-
-
-
-
-
-
`,
+
-
-
+
+
-
-
Image Caption
-Image Caption
+ `, }, executeTest: testParseHTML, }, @@ -276,27 +276,27 @@ export const parseTestInstancesHTML: TestInstance< testCase: { name: "deepNestedContent", content: `Paragraph
-Bold Italic Underline Strikethrough All
Paragraph
+Bold Italic Underline Strikethrough All
Nested Paragraph
-Nested Paragraph
+ `, }, executeTest: testParseHTML, }, @@ -315,21 +315,21 @@ export const parseTestInstancesHTML: TestInstance< testCase: { name: "twoTables", content: `
- Company - |
-
- Example Company Inc. -- Name: [Company Representative] - -Title: Chief Executive Officer - |
-
Company
+Example Company Inc.
++
Name: [Company Representative]
+ +Title: Chief Executive Officer
+