strapi / blocks-react-renderer

A React renderer for the Strapi's Blocks rich text editor
Other
141 stars 23 forks source link

[bug]: Links seem to be nested into paragraphs or not being parsed #19

Closed jeystaats closed 9 months ago

jeystaats commented 9 months ago

What version of @strapi/blocks-react-renderer are you using?

1.0.0

What's Wrong?

In the documentation/readme it mentions the difference between a link and paragraph. In the meanwhile I get a link nested in a paragraph while the blocks renderer doesn't seem to find this particular element at all.

Strapi 14.15.5

Response:

 {
      "type":"paragraph",
      "children":[
         {
            "text":"",
            "type":"text"
         },
         {
            "type":"link",
            "url":"https://test.com",
            "children":[
               {
                  "type":"text",
                  "text":"Read more",
                  "bold":true
               }
            ]
         },
         {
            "text":"",
            "type":"text"
         }
      ]
   },
   {
      "type":"paragraph",
      "children":[
         {
            "type":"text",
            "text":"Another part of a text"
         }
      ]
   }

BlocksRenderer:

 <StrapiRichContentEditor
      content={content}
      blocks={{
        paragraph: ({ ...props }) => (
          <Paragraph {...props} theme={theme} />
        ),
        heading: ({ ...props }) => (
          <Heading {...props} theme={theme} />
        ),
        link: ({ children, url }) => {
          return (
            <Link
              href={url || "#"}
              label={children}
              className={classNames("w-full text-sm inline", {
                "text-white": isBlack,
                "text-screen-black": isWhite,
                underline: true,
              })}
              description={""}
            />
          );
        },
      }}
    />

To Reproduce

This is how it looks like in the Rich Text Blocks.

Screenshot

Expected Behaviour

I would expect the renderer to catch the correct type based on nested elements or at least catch a link.

remidej commented 9 months ago

Hello. I suspect that the issue could come from your custom link component not properly rendering the children prop. Could you check 2 things:

jeystaats commented 9 months ago

Hello. I suspect that the issue could come from your custom link component not properly rendering the children prop. Could you check 2 things:

  • if you remove your custom link component, can you actually see the link in your frontend? (this is just to narrow down the issue, I know you want your custom link component)
  • check what your Link component does with the label prop. Maybe it expects label to just be a string? Ideally I think your Link component just render children via composition: <Link>{children}</Link

Cheers for the fast reply, much appreciated.

Changed it to a NextJS Link component, but still seems to give a JSON response with the link nested into the paragraph. So it seems that OR the backend in Strap returns a different response than usual?

So for now it doesn't even hit the part of the link but continually goes to the paragraph block instead.

It still has the following response for the Strapi Rich Content field

[
  {
    "type": "paragraph",
    "children": [
      {
        "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Neque laoreet suspendisse interdum consectetur libero id faucibus nisl. Velit ut tortor pretium viverra suspendisse potenti nullam ac tortor. In hac habitasse platea dictumst vestibulum rhoncus est. Sed risus ultricies tristique nulla aliquet. Facilisi morbi tempus iaculis urna id volutpat. Sed nisi lacus sed viverra tellus in hac habitasse platea. Mauris cursus mattis molestie a iaculis. Pharetra diam sit amet nisl suscipit adipiscing bibendum est ultricies. Ut tortor pretium viverra suspendisse potenti nullam ac. Pulvinar mattis nunc sed blandit libero volutpat.",
        "type": "text"
      }
    ]
  },
  {
    "type": "paragraph",
    "children": [
      {
        "text": "",
        "type": "text"
      }
    ]
  },
  {
    "type": "paragraph",
    "children": [
      {
        "text": "",
        "type": "text"
      },
      {
        "type": "link",
        "url": "https://test.com",
        "children": [
          {
            "text": "This is a link",
            "type": "text"
          }
        ]
      },
      {
        "text": "",
        "type": "text"
      }
    ]
  },
  {
    "type": "paragraph",
    "children": [
      {
        "type": "text",
        "text": ""
      }
    ]
  }
]
remidej commented 9 months ago

I'm not actually sure there's a problem then. Links are not a normal block, it's an inline block. It cannot exist at the root of your context array, but only inside the children of another block. So if you have a paragraph with only a link as the content, it's normal that it will still be wrapped in a paragraph

Please let me know if I misunderstood your issue

jeystaats commented 9 months ago

I'm not actually sure there's a problem then. Links are not a normal block, it's an inline block. It cannot exist at the root of your context array, but only inside the children of another block. So if you have a paragraph with only a link as the content, it's normal that it will still be wrapped in a paragraph

Please let me know if I misunderstood your issue

I think the issue was misunderstood:

So if you look at my BlocksRenderer you see three elements:

remidej commented 9 months ago

I see, thanks for the details. You're right, the renderer should be able to render the link component on its own inside of a paragraph, you shouldn't have to do that logic.

I suspect it could be because of the way you defined your paragraph component. Could you try using composition, instead of just spreading the props?

paragraph: ({ children, ...props }) => (
  <Paragraph {...props} theme={theme}>{children}</Paragraph>
),

If that is the problem I think it should also affect the modifiers and the lists

jeystaats commented 9 months ago

I see, thanks for the details. You're right, the renderer should be able to render the link component on its own inside of a paragraph, you shouldn't have to do that logic.

I suspect it could be because of the way you defined your paragraph component. Could you try using composition, instead of just spreading the props?

paragraph: ({ children, ...props }) => (
  <Paragraph {...props} theme={theme}>{children}</Paragraph>
),

If that is the problem I think it should also affect the modifiers and the lists

Edit Found the solution, it was an issue on my side, but it had to do with the check rather if it had an empty line:

My Paragraph component first did this:

 const text = dlv(children, "0.props.text", "");

  // Fixes empty lines
  // https://github.com/strapi/blocks-react-renderer/issues/12
  const isEmptyLine = text === "";

  if (isEmptyLine) {
    return <br role="separator" />;
  }

Now does this:

 const text =
    (Array.isArray(children) && children[0]?.props?.text) || children;

  // Fixes empty lines
  // https://github.com/strapi/blocks-react-renderer/issues/12
  const isEmptyLine = text === "";

  if (isEmptyLine) {
    return <br role="separator" />;
  }

So the issue was rather on the part where I checked rather if the children had an empty text with the delve library.

The new situation is much nicer and checks it much better rather what the content should be of the children.

Thanks for everything!

Edited

Thanks again for the fast reply, tried to use your suggestion, but I think it has something to do with the structure of the JSON. Unfortunately the JSON response always seem to nest the link as type under the paragraph. Now does that mean I need to add a modifier to the modifiers list named link?

If so, then the typing doesn't hint that at the moment or it doesn't seem to support that.

For now it just doesn't seem to recognise the children of the paragraph that have a type called link. The only thing for me that works is that I make a if statement in the Paragraph component that checks if the children have a property called type in it and then call the Link component for that.

Kinda defeats the purpose of this amazing component though.

remidej commented 9 months ago

Ah yes I can how your empty line solution would create an issue then. I'm guessing you could also check that children length is one to be extra sure that the paragraph block is empty.

And no, links should not be in modifiers, they're in blocks. It's normal that they can only be used inside other blocks though as they're an inline block, not a root-level block

xardit commented 4 months ago

For me it was the cache of the api not reflecting the link that i added!