ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
26.35k stars 2.22k forks source link

[Bug]: getJSON does not return custom fields on initialization #5172

Closed tylersuchan closed 3 months ago

tylersuchan commented 3 months ago

Affected Packages

@tiptap/react

Version(s)

2.3.0

Bug Description

Hello!

I tried searching current issues to see if anyone else had this issue, but I wasn't able to find anything. I am using Tiptap with React. For my team's usecase, we need to be able to communicate with a backend service when certain user interactions occur. We want to be able to render additional components within the editor based on certain metadata that these services return. Our tiptap component is initialized like so:

import Highlight from "@tiptap/extension-highlight";
import Image from "@tiptap/extension-image";
import Link from "@tiptap/extension-link";
import Subscript from "@tiptap/extension-subscript";
import Superscript from "@tiptap/extension-superscript";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextAlign from "@tiptap/extension-text-align";
import Underline from "@tiptap/extension-underline";
import { BubbleMenu, EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import UniqueID from "@tiptap-pro/extension-unique-id";

function Tiptap() {
  const editor = useEditor({
    editorProps: {
      attributes: {
        class:
          "tw-prose tw-max-w-none tw-rounded-lg !tw-pt-20 tw-h-[calc(100dvh-10rem)] tw-overflow-auto tw-p-12 tw-outline-none"
      }
    },
    content: { content: convertArrayToJSONContent(mockReport), type: "doc" },
    extensions: [
      StarterKit.configure(),
      Highlight,
      TaskList,
      Link,
      Image.configure({ HTMLAttributes: { class: "tw-max-w-full" } }),
      TaskItem,
      Underline,
      Subscript,
      Superscript,
      TextAlign.configure({
        types: ["heading", "paragraph"]
      }),
      UniqueID.configure({ types: ["heading", "paragraph", "plotly"] }),
      SelectedHTML
    ]
  });

You'll notice I'm setting the content to be the return value of this function called convertArrayToJSONContent. That was an attempt to convert the initial report information I'm getting from the backend into the format tiptap requires, defined like so: image As we can see, Content can be of type JSONContent, which allows for custom fields to be included (the [key: string]: any bit.

The definition for convertArrayToJSONContent is defined like so:

// Function to convert an array of ReportContent to an array of JSONContent
export const convertArrayToJSONContent = (reportContents: ReportContent[]): JSONContent[] => {
  return reportContents?.map(convertToJSONContent);
};

const convertToJSONContent = (reportContent: ReportContent): JSONContent => {
  const { content, content_guid, content_index, content_type, metadata } = reportContent;
  const jsonContent: JSONContent = {
    type: content_type === "text" && "paragraph",
    attrs: {
      id: content_guid,
      textAlign: content_type === "text" && "left",
      level: content_index
    },
    content: [{ type: content_type, text: content, metadata: metadata }],
    metadata: metadata
  };
  return jsonContent;
};

Where reportContents is a list of ReportContent objects. That is the mockReport in this case, which I will define here (and shorten to not make this complicated):

export const mockReport: ReportContent[] = [
  {
    content: "• My example text",
    content_guid: "99d9686f-44c8-4cd1-b835-10b2c5db8437",
    content_index: 1,
    content_type: "text",
    metadata: {
      someKey: ["Some value"]
    }
  }
]

You will notice that in the convertToJSONContent function, I attempt to set metadata both within content and as its own field. However, when I attempt to log editor.getJSON, none of the metadata fields are included. This is the object returned to me:

{
    "type": "doc",
    "content": [
        {
            "type": "paragraph",
            "attrs": {
                "id": "99d9686f-44c8-4cd1-b835-10b2c5db8437",
                "textAlign": "left"
            },
            "content": [
                {
                    "type": "text",
                    "text": "• My example text"
                }
            ]
        }
    ]
}

No metadata to be found. Would anyone know why this would be? Is this a bug, or is it possible I'm doing something wrong/misinterpreting the documentation?

Any assistance is much appreciated. Thank you!

Browser Used

Chrome

Code Example URL

No response

Expected Behavior

When setting the content field in useEditor for @tiptap/react to be able to provide additional key: value pairings defined within the JSONContent type.

Additional Context (Optional)

No response

Dependency Updates

nperez0111 commented 3 months ago

Tiptap represents it's information based on Prosemirrror, which strictly adheres to it's format and does not allow additional metadata to be attached to the node/mark. The only additional data it allows are attrs which are like in HTML <img src="/image.png" /> where the attrs would be src: "/image.png" anything that is not defined in this format is stripped out of the prosemirror schema.

If your editor requires that metadata for it's functioning, then add the metadata to attrs, if it does not require it, then I'd probably keep that metadata around somewhere and have an attr to associate the id to the metadata information that you want.

This is not seen as a deficiency of tiptap, just how prosemirror works and is able to enforce consistency