From d1de559d7337e1d75591840874fecbf454b8ce71 Mon Sep 17 00:00:00 2001
From: Matthew Lipski <matthewlipski@gmail.com>
Date: Tue, 28 Nov 2023 23:28:15 +0100
Subject: [PATCH 1/4] Fixed commands and internal copy/paste for inline content

---
 .../Blocks/api/inlineContent/createSpec.ts    | 24 ++++-
 .../Blocks/api/inlineContent/internal.ts      | 35 +++++++-
 .../extensions/Blocks/nodes/BlockContainer.ts | 28 +++---
 packages/react/src/ReactInlineContentSpec.tsx | 89 +++++++++++++++----
 4 files changed, 139 insertions(+), 37 deletions(-)

diff --git a/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts b/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts
index 124923268a..9f9bf80256 100644
--- a/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts
+++ b/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts
@@ -1,8 +1,12 @@
 import { Node } from "@tiptap/core";
 import { nodeToCustomInlineContent } from "../../../../api/nodeConversions/nodeConversions";
 import { propsToAttributes } from "../blocks/internal";
+import { Props } from "../blocks/types";
 import { StyleSchema } from "../styles/types";
-import { createInlineContentSpecFromTipTapNode } from "./internal";
+import {
+  addInlineContentAttributes,
+  createInlineContentSpecFromTipTapNode,
+} from "./internal";
 import {
   InlineContentConfig,
   InlineContentFromConfig,
@@ -57,6 +61,14 @@ export function createInlineContentSpec<
       return propsToAttributes(inlineContentConfig.propSchema);
     },
 
+    parseHTML() {
+      return [
+        {
+          tag: `.bn-inline-content-section[data-inline-content-type="${inlineContentConfig.type}"]`,
+        },
+      ];
+    },
+
     renderHTML({ node }) {
       const editor = this.options.editor;
 
@@ -68,7 +80,15 @@ export function createInlineContentSpec<
         ) as any as InlineContentFromConfig<T, S> // TODO: fix cast
       );
 
-      return output;
+      return {
+        dom: addInlineContentAttributes(
+          output.dom,
+          inlineContentConfig.type,
+          node.attrs as Props<T["propSchema"]>,
+          inlineContentConfig.propSchema
+        ),
+        contentDOM: output.contentDOM,
+      };
     },
   });
 
diff --git a/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts b/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts
index 9c623c44cf..38e99f713e 100644
--- a/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts
+++ b/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts
@@ -1,5 +1,6 @@
 import { Node } from "@tiptap/core";
-import { PropSchema } from "../blocks/types";
+import { camelToDataKebab } from "../blocks/internal";
+import { Props, PropSchema } from "../blocks/types";
 import {
   InlineContentConfig,
   InlineContentImplementation,
@@ -7,6 +8,38 @@ import {
   InlineContentSpec,
   InlineContentSpecs,
 } from "./types";
+import { mergeCSSClasses } from "../../../../shared/utils";
+
+// Function that adds necessary classes and attributes to the `dom` element
+// returned from a custom inline content's 'render' function, to ensure no data
+// is lost on copy & paste.
+export function addInlineContentAttributes<
+  IType extends string,
+  PSchema extends PropSchema
+>(
+  element: HTMLElement,
+  inlineContentType: IType,
+  inlineContentProps: Props<PSchema>,
+  propSchema: PSchema
+): HTMLElement {
+  // Sets inline content section class
+  element.className = mergeCSSClasses(
+    "bn-inline-content-section",
+    element.className
+  );
+  // Sets content type attribute
+  element.setAttribute("data-inline-content-type", inlineContentType);
+  // Adds props as HTML attributes in kebab-case with "data-" prefix. Skips props
+  // set to their default values.
+  Object.entries(inlineContentProps)
+    .filter(([prop, value]) => value !== propSchema[prop].default)
+    .map(([prop, value]) => {
+      return [camelToDataKebab(prop), value];
+    })
+    .forEach(([prop, value]) => element.setAttribute(prop, value));
+
+  return element;
+}
 
 // This helper function helps to instantiate a InlineContentSpec with a
 // config and implementation that conform to the type of Config
diff --git a/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts b/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts
index 443cc7d7fd..bba83b4308 100644
--- a/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts
+++ b/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts
@@ -483,13 +483,12 @@ export const BlockContainer = Node.create<{
         // Reverts block content type to a paragraph if the selection is at the start of the block.
         () =>
           commands.command(({ state }) => {
-            const { contentType } = getBlockInfoFromPos(
+            const { contentType, startPos } = getBlockInfoFromPos(
               state.doc,
               state.selection.from
             )!;
 
-            const selectionAtBlockStart =
-              state.selection.$anchor.parentOffset === 0;
+            const selectionAtBlockStart = state.selection.from === startPos + 1;
             const isParagraph = contentType.name === "paragraph";
 
             if (selectionAtBlockStart && !isParagraph) {
@@ -504,8 +503,12 @@ export const BlockContainer = Node.create<{
         // Removes a level of nesting if the block is indented if the selection is at the start of the block.
         () =>
           commands.command(({ state }) => {
-            const selectionAtBlockStart =
-              state.selection.$anchor.parentOffset === 0;
+            const { startPos } = getBlockInfoFromPos(
+              state.doc,
+              state.selection.from
+            )!;
+
+            const selectionAtBlockStart = state.selection.from === startPos + 1;
 
             if (selectionAtBlockStart) {
               return commands.liftListItem("blockContainer");
@@ -522,10 +525,8 @@ export const BlockContainer = Node.create<{
               state.selection.from
             )!;
 
-            const selectionAtBlockStart =
-              state.selection.$anchor.parentOffset === 0;
-            const selectionEmpty =
-              state.selection.anchor === state.selection.head;
+            const selectionAtBlockStart = state.selection.from === startPos + 1;
+            const selectionEmpty = state.selection.empty;
             const blockAtDocStart = startPos === 2;
 
             const posBetweenBlocks = startPos - 1;
@@ -552,17 +553,14 @@ export const BlockContainer = Node.create<{
         // end of the block.
         () =>
           commands.command(({ state }) => {
-            const { node, contentNode, depth, endPos } = getBlockInfoFromPos(
+            const { node, depth, endPos } = getBlockInfoFromPos(
               state.doc,
               state.selection.from
             )!;
 
             const blockAtDocEnd = false;
-            const selectionAtBlockEnd =
-              state.selection.$anchor.parentOffset ===
-              contentNode.firstChild!.nodeSize;
-            const selectionEmpty =
-              state.selection.anchor === state.selection.head;
+            const selectionAtBlockEnd = state.selection.from === endPos - 1;
+            const selectionEmpty = state.selection.empty;
             const hasChildBlocks = node.childCount === 2;
 
             if (
diff --git a/packages/react/src/ReactInlineContentSpec.tsx b/packages/react/src/ReactInlineContentSpec.tsx
index 70f2db7c7d..2bf31e234f 100644
--- a/packages/react/src/ReactInlineContentSpec.tsx
+++ b/packages/react/src/ReactInlineContentSpec.tsx
@@ -1,9 +1,12 @@
 import {
+  camelToDataKebab,
   createInternalInlineContentSpec,
   createStronglyTypedTiptapNode,
   InlineContentConfig,
   InlineContentFromConfig,
   nodeToCustomInlineContent,
+  Props,
+  PropSchema,
   propsToAttributes,
   StyleSchema,
 } from "@blocknote/core";
@@ -36,6 +39,40 @@ export type ReactInlineContentImplementation<
   // }>;
 };
 
+// Function that wraps the React component returned from 'blockConfig.render' in
+// a `NodeViewWrapper` which also acts as a `blockContent` div. It contains the
+// block type and props as HTML attributes.
+export function reactWrapInInlineContentStructure<
+  BType extends string,
+  PSchema extends PropSchema
+>(
+  element: JSX.Element,
+  inlineContentType: BType,
+  inlineContentProps: Props<PSchema>,
+  propSchema: PSchema
+) {
+  return () => (
+    // Creates inline content section element
+    <NodeViewWrapper
+      as={"span"}
+      // Sets inline content section class
+      className={"bn-inline-content-section"}
+      // Sets content type attribute
+      data-inline-content-type={inlineContentType}
+      // Adds props as HTML attributes in kebab-case with "data-" prefix. Skips
+      // props set to their default values.
+      {...Object.fromEntries(
+        Object.entries(inlineContentProps)
+          .filter(([prop, value]) => value !== propSchema[prop].default)
+          .map(([prop, value]) => {
+            return [camelToDataKebab(prop), value];
+          })
+      )}>
+      {element}
+    </NodeViewWrapper>
+  );
+}
+
 // A function to create custom block for API consumers
 // we want to hide the tiptap node from API consumers and provide a simpler API surface instead
 export function createReactInlineContentSpec<
@@ -50,6 +87,8 @@ export function createReactInlineContentSpec<
     name: inlineContentConfig.type as T["type"],
     inline: true,
     group: "inline",
+    selectable: inlineContentConfig.content === "styled",
+    atom: inlineContentConfig.content === "none",
     content: (inlineContentConfig.content === "styled"
       ? "inline*"
       : "") as T["content"] extends "styled" ? "inline*" : "",
@@ -58,9 +97,13 @@ export function createReactInlineContentSpec<
       return propsToAttributes(inlineContentConfig.propSchema);
     },
 
-    // parseHTML() {
-    //   return parse(blockConfig);
-    // },
+    parseHTML() {
+      return [
+        {
+          tag: `.bn-inline-content-section[data-inline-content-type="${inlineContentConfig.type}"]`,
+        },
+      ];
+    },
 
     renderHTML({ node }) {
       const editor = this.options.editor;
@@ -72,9 +115,15 @@ export function createReactInlineContentSpec<
       ) as any as InlineContentFromConfig<T, S>; // TODO: fix cast
       const Content = inlineContentImplementation.render;
 
-      return renderToDOMSpec((refCB) => (
-        <Content inlineContent={ic} contentRef={refCB} />
-      ));
+      return renderToDOMSpec((refCB) => {
+        const FullContent = reactWrapInInlineContentStructure(
+          <Content inlineContent={ic} contentRef={refCB} />,
+          inlineContentConfig.type,
+          node.attrs as Props<T["propSchema"]>,
+          inlineContentConfig.propSchema
+        );
+        return <FullContent />;
+      });
     },
 
     // TODO: needed?
@@ -88,20 +137,22 @@ export function createReactInlineContentSpec<
             const ref = (NodeViewContent({}) as any).ref;
 
             const Content = inlineContentImplementation.render;
-            return (
-              <NodeViewWrapper as="span">
-                <Content
-                  contentRef={ref}
-                  inlineContent={
-                    nodeToCustomInlineContent(
-                      props.node,
-                      editor.inlineContentSchema,
-                      editor.styleSchema
-                    ) as any as InlineContentFromConfig<T, S> // TODO: fix cast
-                  }
-                />
-              </NodeViewWrapper>
+            const FullContent = reactWrapInInlineContentStructure(
+              <Content
+                contentRef={ref}
+                inlineContent={
+                  nodeToCustomInlineContent(
+                    props.node,
+                    editor.inlineContentSchema,
+                    editor.styleSchema
+                  ) as any as InlineContentFromConfig<T, S> // TODO: fix cast
+                }
+              />,
+              inlineContentConfig.type,
+              props.node.attrs as Props<T["propSchema"]>,
+              inlineContentConfig.propSchema
             );
+            return <FullContent />;
           },
           {
             className: "bn-ic-react-node-view-renderer",

From bea2771694aa4e021c2b4cde084597e8c7a4280b Mon Sep 17 00:00:00 2001
From: Matthew Lipski <matthewlipski@gmail.com>
Date: Wed, 29 Nov 2023 00:00:46 +0100
Subject: [PATCH 2/4] Fixed internal copy/paste for styles

---
 .../Blocks/api/styles/createSpec.ts           | 34 +++++++++++----
 .../extensions/Blocks/api/styles/internal.ts  | 26 ++++++++++++
 packages/react/src/ReactInlineContentSpec.tsx | 26 +++++++-----
 packages/react/src/ReactStyleSpec.tsx         | 41 ++++++++++++++-----
 4 files changed, 97 insertions(+), 30 deletions(-)

diff --git a/packages/core/src/extensions/Blocks/api/styles/createSpec.ts b/packages/core/src/extensions/Blocks/api/styles/createSpec.ts
index 9f0d742f75..607be9fdde 100644
--- a/packages/core/src/extensions/Blocks/api/styles/createSpec.ts
+++ b/packages/core/src/extensions/Blocks/api/styles/createSpec.ts
@@ -1,6 +1,6 @@
 import { Mark } from "@tiptap/core";
 import { UnreachableCaseError } from "../../../../shared/utils";
-import { createInternalStyleSpec } from "./internal";
+import { addStyleAttributes, createInternalStyleSpec } from "./internal";
 import { StyleConfig, StyleSpec } from "./types";
 
 export type CustomStyleImplementation<T extends StyleConfig> = {
@@ -31,17 +31,25 @@ export function createStyleSpec<T extends StyleConfig>(
       return {
         stringValue: {
           default: undefined,
-          // TODO: parsing
-
-          // parseHTML: (element) =>
-          //   element.getAttribute(`data-${styleConfig.type}`),
-          // renderHTML: (attributes) => ({
-          //   [`data-${styleConfig.type}`]: attributes.stringValue,
-          // }),
+          parseHTML: (element) => element.getAttribute("data-value"),
+          renderHTML: (attributes) =>
+            attributes.stringValue !== undefined
+              ? {
+                  "data-value": attributes.stringValue,
+                }
+              : {},
         },
       };
     },
 
+    parseHTML() {
+      return [
+        {
+          tag: `.bn-style[data-style-type="${styleConfig.type}"]`,
+        },
+      ];
+    },
+
     renderHTML({ mark }) {
       let renderResult: {
         dom: HTMLElement;
@@ -58,7 +66,15 @@ export function createStyleSpec<T extends StyleConfig>(
       }
 
       // const renderResult = styleImplementation.render();
-      return renderResult;
+      return {
+        dom: addStyleAttributes(
+          renderResult.dom,
+          styleConfig.type,
+          mark.attrs.stringValue,
+          styleConfig.propSchema
+        ),
+        contentDOM: renderResult.contentDOM,
+      };
     },
   });
 
diff --git a/packages/core/src/extensions/Blocks/api/styles/internal.ts b/packages/core/src/extensions/Blocks/api/styles/internal.ts
index 648bb133d5..f52b04e037 100644
--- a/packages/core/src/extensions/Blocks/api/styles/internal.ts
+++ b/packages/core/src/extensions/Blocks/api/styles/internal.ts
@@ -7,6 +7,32 @@ import {
   StyleSpec,
   StyleSpecs,
 } from "./types";
+import { mergeCSSClasses } from "../../../../shared/utils";
+
+// Function that adds necessary classes and attributes to the `dom` element
+// returned from a custom style's 'render' function, to ensure no data is lost
+// on internal copy & paste.
+export function addStyleAttributes<
+  SType extends string,
+  PSchema extends StylePropSchema
+>(
+  element: HTMLElement,
+  styleType: SType,
+  styleValue: PSchema extends "boolean" ? undefined : string,
+  propSchema: PSchema
+): HTMLElement {
+  // Sets inline content section class
+  element.className = mergeCSSClasses("bn-style", element.className);
+  // Sets content type attribute
+  element.setAttribute("data-style-type", styleType);
+  // Adds style value as an HTML attribute in kebab-case with "data-" prefix, if
+  // the style takes a string value.
+  if (propSchema === "string") {
+    element.setAttribute("data-value", styleValue as string);
+  }
+
+  return element;
+}
 
 // This helper function helps to instantiate a stylespec with a
 // config and implementation that conform to the type of Config
diff --git a/packages/react/src/ReactInlineContentSpec.tsx b/packages/react/src/ReactInlineContentSpec.tsx
index 2bf31e234f..22018eaba1 100644
--- a/packages/react/src/ReactInlineContentSpec.tsx
+++ b/packages/react/src/ReactInlineContentSpec.tsx
@@ -1,4 +1,5 @@
 import {
+  addInlineContentAttributes,
   camelToDataKebab,
   createInternalInlineContentSpec,
   createStronglyTypedTiptapNode,
@@ -39,15 +40,15 @@ export type ReactInlineContentImplementation<
   // }>;
 };
 
-// Function that wraps the React component returned from 'blockConfig.render' in
-// a `NodeViewWrapper` which also acts as a `blockContent` div. It contains the
-// block type and props as HTML attributes.
+// Function that adds a wrapper with necessary classes and attributes to the
+// component returned from a custom inline content's 'render' function, to
+// ensure no data is lost on internal copy & paste.
 export function reactWrapInInlineContentStructure<
-  BType extends string,
+  IType extends string,
   PSchema extends PropSchema
 >(
   element: JSX.Element,
-  inlineContentType: BType,
+  inlineContentType: IType,
   inlineContentProps: Props<PSchema>,
   propSchema: PSchema
 ) {
@@ -114,16 +115,19 @@ export function createReactInlineContentSpec<
         editor.styleSchema
       ) as any as InlineContentFromConfig<T, S>; // TODO: fix cast
       const Content = inlineContentImplementation.render;
+      const output = renderToDOMSpec((refCB) => (
+        <Content inlineContent={ic} contentRef={refCB} />
+      ));
 
-      return renderToDOMSpec((refCB) => {
-        const FullContent = reactWrapInInlineContentStructure(
-          <Content inlineContent={ic} contentRef={refCB} />,
+      return {
+        dom: addInlineContentAttributes(
+          output.dom,
           inlineContentConfig.type,
           node.attrs as Props<T["propSchema"]>,
           inlineContentConfig.propSchema
-        );
-        return <FullContent />;
-      });
+        ),
+        contentDOM: output.contentDOM,
+      };
     },
 
     // TODO: needed?
diff --git a/packages/react/src/ReactStyleSpec.tsx b/packages/react/src/ReactStyleSpec.tsx
index 645a519e48..0084bd0140 100644
--- a/packages/react/src/ReactStyleSpec.tsx
+++ b/packages/react/src/ReactStyleSpec.tsx
@@ -1,4 +1,8 @@
-import { createInternalStyleSpec, StyleConfig } from "@blocknote/core";
+import {
+  addStyleAttributes,
+  createInternalStyleSpec,
+  StyleConfig,
+} from "@blocknote/core";
 import { Mark } from "@tiptap/react";
 import { FC } from "react";
 import { renderToDOMSpec } from "./ReactRenderUtil";
@@ -28,17 +32,25 @@ export function createReactStyleSpec<T extends StyleConfig>(
       return {
         stringValue: {
           default: undefined,
-          // TODO: parsing
-
-          // parseHTML: (element) =>
-          //   element.getAttribute(`data-${styleConfig.type}`),
-          // renderHTML: (attributes) => ({
-          //   [`data-${styleConfig.type}`]: attributes.stringValue,
-          // }),
+          parseHTML: (element) => element.getAttribute("data-value"),
+          renderHTML: (attributes) =>
+            attributes.stringValue !== undefined
+              ? {
+                  "data-value": attributes.stringValue,
+                }
+              : {},
         },
       };
     },
 
+    parseHTML() {
+      return [
+        {
+          tag: `.bn-style[data-style-type="${styleConfig.type}"]`,
+        },
+      ];
+    },
+
     renderHTML({ mark }) {
       const props: any = {};
 
@@ -47,10 +59,19 @@ export function createReactStyleSpec<T extends StyleConfig>(
       }
 
       const Content = styleImplementation.render;
-
-      return renderToDOMSpec((refCB) => (
+      const renderResult = renderToDOMSpec((refCB) => (
         <Content {...props} contentRef={refCB} />
       ));
+
+      return {
+        dom: addStyleAttributes(
+          renderResult.dom,
+          styleConfig.type,
+          mark.attrs.stringValue,
+          styleConfig.propSchema
+        ),
+        contentDOM: renderResult.contentDOM,
+      };
     },
   });
 

From a8f08eec903d68404d8d11e549bfdb2016cbc9f4 Mon Sep 17 00:00:00 2001
From: Matthew Lipski <matthewlipski@gmail.com>
Date: Wed, 29 Nov 2023 00:10:44 +0100
Subject: [PATCH 3/4] Small cleanup

---
 .../Blocks/api/inlineContent/createSpec.ts    | 17 ++++++---
 .../Blocks/api/inlineContent/internal.ts      |  2 +-
 .../Blocks/api/styles/createSpec.ts           | 37 ++++++++-----------
 .../extensions/Blocks/api/styles/internal.ts  | 23 +++++++++++-
 packages/react/src/ReactInlineContentSpec.tsx |  7 +---
 packages/react/src/ReactStyleSpec.tsx         | 24 ++----------
 6 files changed, 57 insertions(+), 53 deletions(-)

diff --git a/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts b/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts
index 9f9bf80256..534ce36bf2 100644
--- a/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts
+++ b/packages/core/src/extensions/Blocks/api/inlineContent/createSpec.ts
@@ -1,4 +1,5 @@
 import { Node } from "@tiptap/core";
+import { ParseRule } from "@tiptap/pm/model";
 import { nodeToCustomInlineContent } from "../../../../api/nodeConversions/nodeConversions";
 import { propsToAttributes } from "../blocks/internal";
 import { Props } from "../blocks/types";
@@ -41,6 +42,16 @@ export type CustomInlineContentImplementation<
   };
 };
 
+export function getInlineContentParseRules(
+  config: InlineContentConfig
+): ParseRule[] {
+  return [
+    {
+      tag: `.bn-inline-content-section[data-inline-content-type="${config.type}"]`,
+    },
+  ];
+}
+
 export function createInlineContentSpec<
   T extends InlineContentConfig,
   S extends StyleSchema
@@ -62,11 +73,7 @@ export function createInlineContentSpec<
     },
 
     parseHTML() {
-      return [
-        {
-          tag: `.bn-inline-content-section[data-inline-content-type="${inlineContentConfig.type}"]`,
-        },
-      ];
+      return getInlineContentParseRules(inlineContentConfig);
     },
 
     renderHTML({ node }) {
diff --git a/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts b/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts
index 38e99f713e..d081338be8 100644
--- a/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts
+++ b/packages/core/src/extensions/Blocks/api/inlineContent/internal.ts
@@ -12,7 +12,7 @@ import { mergeCSSClasses } from "../../../../shared/utils";
 
 // Function that adds necessary classes and attributes to the `dom` element
 // returned from a custom inline content's 'render' function, to ensure no data
-// is lost on copy & paste.
+// is lost on internal copy & paste.
 export function addInlineContentAttributes<
   IType extends string,
   PSchema extends PropSchema
diff --git a/packages/core/src/extensions/Blocks/api/styles/createSpec.ts b/packages/core/src/extensions/Blocks/api/styles/createSpec.ts
index 607be9fdde..14c1c2274f 100644
--- a/packages/core/src/extensions/Blocks/api/styles/createSpec.ts
+++ b/packages/core/src/extensions/Blocks/api/styles/createSpec.ts
@@ -1,6 +1,11 @@
 import { Mark } from "@tiptap/core";
+import { ParseRule } from "@tiptap/pm/model";
 import { UnreachableCaseError } from "../../../../shared/utils";
-import { addStyleAttributes, createInternalStyleSpec } from "./internal";
+import {
+  addStyleAttributes,
+  createInternalStyleSpec,
+  stylePropsToAttributes,
+} from "./internal";
 import { StyleConfig, StyleSpec } from "./types";
 
 export type CustomStyleImplementation<T extends StyleConfig> = {
@@ -17,6 +22,14 @@ export type CustomStyleImplementation<T extends StyleConfig> = {
 
 // TODO: support serialization
 
+export function getStyleParseRules(config: StyleConfig): ParseRule[] {
+  return [
+    {
+      tag: `.bn-style[data-style-type="${config.type}"]`,
+    },
+  ];
+}
+
 export function createStyleSpec<T extends StyleConfig>(
   styleConfig: T,
   styleImplementation: CustomStyleImplementation<T>
@@ -25,29 +38,11 @@ export function createStyleSpec<T extends StyleConfig>(
     name: styleConfig.type,
 
     addAttributes() {
-      if (styleConfig.propSchema === "boolean") {
-        return {};
-      }
-      return {
-        stringValue: {
-          default: undefined,
-          parseHTML: (element) => element.getAttribute("data-value"),
-          renderHTML: (attributes) =>
-            attributes.stringValue !== undefined
-              ? {
-                  "data-value": attributes.stringValue,
-                }
-              : {},
-        },
-      };
+      return stylePropsToAttributes(styleConfig.propSchema);
     },
 
     parseHTML() {
-      return [
-        {
-          tag: `.bn-style[data-style-type="${styleConfig.type}"]`,
-        },
-      ];
+      return getStyleParseRules(styleConfig);
     },
 
     renderHTML({ mark }) {
diff --git a/packages/core/src/extensions/Blocks/api/styles/internal.ts b/packages/core/src/extensions/Blocks/api/styles/internal.ts
index f52b04e037..27b32a3f7a 100644
--- a/packages/core/src/extensions/Blocks/api/styles/internal.ts
+++ b/packages/core/src/extensions/Blocks/api/styles/internal.ts
@@ -1,4 +1,4 @@
-import { Mark } from "@tiptap/core";
+import { Attributes, Mark } from "@tiptap/core";
 import {
   StyleConfig,
   StyleImplementation,
@@ -9,6 +9,27 @@ import {
 } from "./types";
 import { mergeCSSClasses } from "../../../../shared/utils";
 
+export function stylePropsToAttributes(
+  propSchema: StylePropSchema
+): Attributes {
+  if (propSchema === "boolean") {
+    return {};
+  }
+  return {
+    stringValue: {
+      default: undefined,
+      keepOnSplit: true,
+      parseHTML: (element) => element.getAttribute("data-value"),
+      renderHTML: (attributes) =>
+        attributes.stringValue !== undefined
+          ? {
+              "data-value": attributes.stringValue,
+            }
+          : {},
+    },
+  };
+}
+
 // Function that adds necessary classes and attributes to the `dom` element
 // returned from a custom style's 'render' function, to ensure no data is lost
 // on internal copy & paste.
diff --git a/packages/react/src/ReactInlineContentSpec.tsx b/packages/react/src/ReactInlineContentSpec.tsx
index 22018eaba1..adf51e09a2 100644
--- a/packages/react/src/ReactInlineContentSpec.tsx
+++ b/packages/react/src/ReactInlineContentSpec.tsx
@@ -3,6 +3,7 @@ import {
   camelToDataKebab,
   createInternalInlineContentSpec,
   createStronglyTypedTiptapNode,
+  getInlineContentParseRules,
   InlineContentConfig,
   InlineContentFromConfig,
   nodeToCustomInlineContent,
@@ -99,11 +100,7 @@ export function createReactInlineContentSpec<
     },
 
     parseHTML() {
-      return [
-        {
-          tag: `.bn-inline-content-section[data-inline-content-type="${inlineContentConfig.type}"]`,
-        },
-      ];
+      return getInlineContentParseRules(inlineContentConfig);
     },
 
     renderHTML({ node }) {
diff --git a/packages/react/src/ReactStyleSpec.tsx b/packages/react/src/ReactStyleSpec.tsx
index 0084bd0140..cb401850b7 100644
--- a/packages/react/src/ReactStyleSpec.tsx
+++ b/packages/react/src/ReactStyleSpec.tsx
@@ -1,7 +1,9 @@
 import {
   addStyleAttributes,
   createInternalStyleSpec,
+  getStyleParseRules,
   StyleConfig,
+  stylePropsToAttributes,
 } from "@blocknote/core";
 import { Mark } from "@tiptap/react";
 import { FC } from "react";
@@ -26,29 +28,11 @@ export function createReactStyleSpec<T extends StyleConfig>(
     name: styleConfig.type,
 
     addAttributes() {
-      if (styleConfig.propSchema === "boolean") {
-        return {};
-      }
-      return {
-        stringValue: {
-          default: undefined,
-          parseHTML: (element) => element.getAttribute("data-value"),
-          renderHTML: (attributes) =>
-            attributes.stringValue !== undefined
-              ? {
-                  "data-value": attributes.stringValue,
-                }
-              : {},
-        },
-      };
+      return stylePropsToAttributes(styleConfig.propSchema);
     },
 
     parseHTML() {
-      return [
-        {
-          tag: `.bn-style[data-style-type="${styleConfig.type}"]`,
-        },
-      ];
+      return getStyleParseRules(styleConfig);
     },
 
     renderHTML({ mark }) {

From 63caf0774c8ae7a1386194733bdd8967bdc123ae Mon Sep 17 00:00:00 2001
From: yousefed <yousefdardiry@gmail.com>
Date: Wed, 29 Nov 2023 09:54:16 +0100
Subject: [PATCH 4/4] fix some tests

---
 .../fontSize/basic/external.html              |  2 +-
 .../fontSize/basic/internal.html              |  2 +-
 .../__snapshots__/mention/basic/external.html |  2 +-
 .../__snapshots__/mention/basic/internal.html |  2 +-
 .../__snapshots__/small/basic/external.html   |  2 +-
 .../__snapshots__/small/basic/internal.html   |  2 +-
 .../__snapshots__/tag/basic/external.html     |  2 +-
 .../__snapshots__/tag/basic/internal.html     |  2 +-
 .../api/exporters/html/htmlConversion.test.ts |  3 ---
 .../testCases/cases/customInlineContent.ts    |  4 +--
 .../src/api/testCases/cases/defaultSchema.ts  | 26 +++++++++----------
 .../ParagraphBlockContent.ts                  |  1 +
 .../fontSize/basic/external.html              |  2 +-
 .../fontSize/basic/internal.html              |  2 +-
 .../__snapshots__/mention/basic/external.html |  2 +-
 .../__snapshots__/mention/basic/internal.html |  2 +-
 .../__snapshots__/small/basic/external.html   |  2 +-
 .../__snapshots__/small/basic/internal.html   |  2 +-
 .../__snapshots__/tag/basic/external.html     |  2 +-
 .../__snapshots__/tag/basic/internal.html     |  2 +-
 20 files changed, 32 insertions(+), 34 deletions(-)

diff --git a/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/external.html b/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/external.html
index 4c7e8f174d..49b9ce6858 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content"><span style="font-size: 18px">This is text with a custom fontSize</span></p>
\ No newline at end of file
+<p class="bn-inline-content"><span style="font-size: 18px" class="bn-style" data-style-type="fontSize" data-value="18px">This is text with a custom fontSize</span></p>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/internal.html b/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/internal.html
index 3e2beaedd6..3fe864246c 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/fontSize/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><span style="font-size: 18px">This is text with a custom fontSize</span></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><span style="font-size: 18px" class="bn-style" data-style-type="fontSize" data-value="18px">This is text with a custom fontSize</span></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/mention/basic/external.html b/packages/core/src/api/exporters/html/__snapshots__/mention/basic/external.html
index e1513fed2d..2e6f533ca1 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/mention/basic/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/mention/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content">I enjoy working with<span>@Matthew</span></p>
\ No newline at end of file
+<p class="bn-inline-content">I enjoy working with<span class="bn-inline-content-section" data-inline-content-type="mention" data-user="Matthew">@Matthew</span></p>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/mention/basic/internal.html b/packages/core/src/api/exporters/html/__snapshots__/mention/basic/internal.html
index 7af6dad9c7..6ca7d81c2c 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/mention/basic/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/mention/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I enjoy working with<span>@Matthew</span></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I enjoy working with<span class="bn-inline-content-section" data-inline-content-type="mention" data-user="Matthew">@Matthew</span></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/small/basic/external.html b/packages/core/src/api/exporters/html/__snapshots__/small/basic/external.html
index 4206d07a95..35c3d5c232 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/small/basic/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/small/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content"><small>This is a small text</small></p>
\ No newline at end of file
+<p class="bn-inline-content"><small class="bn-style" data-style-type="small">This is a small text</small></p>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/small/basic/internal.html b/packages/core/src/api/exporters/html/__snapshots__/small/basic/internal.html
index 805c78112e..73836f647d 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/small/basic/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/small/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><small>This is a small text</small></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><small class="bn-style" data-style-type="small">This is a small text</small></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/tag/basic/external.html b/packages/core/src/api/exporters/html/__snapshots__/tag/basic/external.html
index 4229ae0a83..b8387e9a55 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/tag/basic/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/tag/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content">I love <span>#<span>BlockNote</span></span></p>
\ No newline at end of file
+<p class="bn-inline-content">I love <span class="bn-inline-content-section" data-inline-content-type="tag">#<span>BlockNote</span></span></p>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/tag/basic/internal.html b/packages/core/src/api/exporters/html/__snapshots__/tag/basic/internal.html
index dac5db0ca8..bac28633b0 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/tag/basic/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/tag/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I love <span>#<span>BlockNote</span></span></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I love <span class="bn-inline-content-section" data-inline-content-type="tag">#<span>BlockNote</span></span></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/htmlConversion.test.ts b/packages/core/src/api/exporters/html/htmlConversion.test.ts
index 9f52f2d558..f6592f1bb7 100644
--- a/packages/core/src/api/exporters/html/htmlConversion.test.ts
+++ b/packages/core/src/api/exporters/html/htmlConversion.test.ts
@@ -369,9 +369,6 @@ describe("Test HTML conversion", () => {
       for (const document of testCase.documents) {
         // eslint-disable-next-line no-loop-func
         it("Convert " + document.name + " to HTML", async () => {
-          if (document.name !== "complex/misc") {
-            return;
-          }
           const nameSplit = document.name.split("/");
           await convertToHTMLAndCompareSnapshots(
             editor,
diff --git a/packages/core/src/api/testCases/cases/customInlineContent.ts b/packages/core/src/api/testCases/cases/customInlineContent.ts
index 8ad1828152..304df912cb 100644
--- a/packages/core/src/api/testCases/cases/customInlineContent.ts
+++ b/packages/core/src/api/testCases/cases/customInlineContent.ts
@@ -87,7 +87,7 @@ export const customInlineContentTestCases: EditorTestCases<
                 user: "Matthew",
               },
               content: undefined,
-            } as any,
+            } as any, // TODO
           ],
         },
       ],
@@ -103,7 +103,7 @@ export const customInlineContentTestCases: EditorTestCases<
               type: "tag",
               // props: {},
               content: "BlockNote",
-            } as any,
+            } as any, // TODO
           ],
         },
       ],
diff --git a/packages/core/src/api/testCases/cases/defaultSchema.ts b/packages/core/src/api/testCases/cases/defaultSchema.ts
index bb7ccf4526..87aa6b01b1 100644
--- a/packages/core/src/api/testCases/cases/defaultSchema.ts
+++ b/packages/core/src/api/testCases/cases/defaultSchema.ts
@@ -24,7 +24,7 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "paragraph/empty",
       blocks: [
         {
-          type: "paragraph" as const,
+          type: "paragraph",
         },
       ],
     },
@@ -32,7 +32,7 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "paragraph/basic",
       blocks: [
         {
-          type: "paragraph" as const,
+          type: "paragraph",
           content: "Paragraph",
         },
       ],
@@ -41,12 +41,12 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "paragraph/styled",
       blocks: [
         {
-          type: "paragraph" as const,
+          type: "paragraph",
           props: {
             textAlignment: "center",
             textColor: "orange",
             backgroundColor: "pink",
-          } as const,
+          },
           content: [
             {
               type: "text",
@@ -83,15 +83,15 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "paragraph/nested",
       blocks: [
         {
-          type: "paragraph" as const,
+          type: "paragraph",
           content: "Paragraph",
           children: [
             {
-              type: "paragraph" as const,
+              type: "paragraph",
               content: "Nested Paragraph 1",
             },
             {
-              type: "paragraph" as const,
+              type: "paragraph",
               content: "Nested Paragraph 2",
             },
           ],
@@ -102,7 +102,7 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "image/button",
       blocks: [
         {
-          type: "image" as const,
+          type: "image",
         },
       ],
     },
@@ -110,7 +110,7 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "image/basic",
       blocks: [
         {
-          type: "image" as const,
+          type: "image",
           props: {
             url: "exampleURL",
             caption: "Caption",
@@ -123,20 +123,20 @@ export const defaultSchemaTestCases: EditorTestCases<
       name: "image/nested",
       blocks: [
         {
-          type: "image" as const,
+          type: "image",
           props: {
             url: "exampleURL",
             caption: "Caption",
             width: 256,
-          } as const,
+          },
           children: [
             {
-              type: "image" as const,
+              type: "image",
               props: {
                 url: "exampleURL",
                 caption: "Caption",
                 width: 256,
-              } as const,
+              },
             },
           ],
         },
diff --git a/packages/core/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts b/packages/core/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts
index a645ba347c..8c826f413e 100644
--- a/packages/core/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts
+++ b/packages/core/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts
@@ -15,6 +15,7 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
   group: "blockContent",
   parseHTML() {
     return [
+      { tag: "div[data-content-type=" + this.name + "]" },
       {
         tag: "p",
         priority: 200,
diff --git a/packages/react/src/test/__snapshots__/fontSize/basic/external.html b/packages/react/src/test/__snapshots__/fontSize/basic/external.html
index 00a5bc6b6e..6c8910692f 100644
--- a/packages/react/src/test/__snapshots__/fontSize/basic/external.html
+++ b/packages/react/src/test/__snapshots__/fontSize/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content"><span style="font-size: 18px;">This is text with a custom fontSize</span></p>
\ No newline at end of file
+<p class="bn-inline-content"><span style="font-size: 18px;" class="bn-style" data-style-type="fontSize" data-value="18px">This is text with a custom fontSize</span></p>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/fontSize/basic/internal.html b/packages/react/src/test/__snapshots__/fontSize/basic/internal.html
index a41d39869a..998d9bcf8b 100644
--- a/packages/react/src/test/__snapshots__/fontSize/basic/internal.html
+++ b/packages/react/src/test/__snapshots__/fontSize/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><span style="font-size: 18px;">This is text with a custom fontSize</span></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><span style="font-size: 18px;" class="bn-style" data-style-type="fontSize" data-value="18px">This is text with a custom fontSize</span></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/mention/basic/external.html b/packages/react/src/test/__snapshots__/mention/basic/external.html
index e1513fed2d..2e6f533ca1 100644
--- a/packages/react/src/test/__snapshots__/mention/basic/external.html
+++ b/packages/react/src/test/__snapshots__/mention/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content">I enjoy working with<span>@Matthew</span></p>
\ No newline at end of file
+<p class="bn-inline-content">I enjoy working with<span class="bn-inline-content-section" data-inline-content-type="mention" data-user="Matthew">@Matthew</span></p>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/mention/basic/internal.html b/packages/react/src/test/__snapshots__/mention/basic/internal.html
index 7af6dad9c7..6ca7d81c2c 100644
--- a/packages/react/src/test/__snapshots__/mention/basic/internal.html
+++ b/packages/react/src/test/__snapshots__/mention/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I enjoy working with<span>@Matthew</span></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I enjoy working with<span class="bn-inline-content-section" data-inline-content-type="mention" data-user="Matthew">@Matthew</span></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/small/basic/external.html b/packages/react/src/test/__snapshots__/small/basic/external.html
index 4206d07a95..35c3d5c232 100644
--- a/packages/react/src/test/__snapshots__/small/basic/external.html
+++ b/packages/react/src/test/__snapshots__/small/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content"><small>This is a small text</small></p>
\ No newline at end of file
+<p class="bn-inline-content"><small class="bn-style" data-style-type="small">This is a small text</small></p>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/small/basic/internal.html b/packages/react/src/test/__snapshots__/small/basic/internal.html
index 805c78112e..73836f647d 100644
--- a/packages/react/src/test/__snapshots__/small/basic/internal.html
+++ b/packages/react/src/test/__snapshots__/small/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><small>This is a small text</small></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content"><small class="bn-style" data-style-type="small">This is a small text</small></p></div></div></div></div>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/tag/basic/external.html b/packages/react/src/test/__snapshots__/tag/basic/external.html
index 4229ae0a83..b8387e9a55 100644
--- a/packages/react/src/test/__snapshots__/tag/basic/external.html
+++ b/packages/react/src/test/__snapshots__/tag/basic/external.html
@@ -1 +1 @@
-<p class="bn-inline-content">I love <span>#<span>BlockNote</span></span></p>
\ No newline at end of file
+<p class="bn-inline-content">I love <span class="bn-inline-content-section" data-inline-content-type="tag">#<span>BlockNote</span></span></p>
\ No newline at end of file
diff --git a/packages/react/src/test/__snapshots__/tag/basic/internal.html b/packages/react/src/test/__snapshots__/tag/basic/internal.html
index dac5db0ca8..bac28633b0 100644
--- a/packages/react/src/test/__snapshots__/tag/basic/internal.html
+++ b/packages/react/src/test/__snapshots__/tag/basic/internal.html
@@ -1 +1 @@
-<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I love <span>#<span>BlockNote</span></span></p></div></div></div></div>
\ No newline at end of file
+<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">I love <span class="bn-inline-content-section" data-inline-content-type="tag">#<span>BlockNote</span></span></p></div></div></div></div>
\ No newline at end of file