contentful / rich-text

Libraries for handling and rendering Rich Text 📄
MIT License
549 stars 109 forks source link

Div Embedded in P tag #153

Open waltercolindres opened 4 years ago

waltercolindres commented 4 years ago

Want to clean up the output to remove p tags from around divs for videos.


const renderContent = {
    renderNode: {
      "embedded-asset-block": (node) => {
        const file = node.data.target.fields.file.url;
        const title = node.data.target.fields.title;
        const alt = node.data.target.fields.alt;
        return (
          <img className="reviews-image" src={file} alt={file} title={title} />
        );
      },
      [INLINES.HYPERLINK]: (node, children) => {
        if (node.data.uri.includes("player.vimeo.com/video")) {
          return (
            <div className="videoWrapper">
              <iframe
                title="Unique Title 001"
                src={node.data.uri}
                frameBorder="0"
                allowFullScreen
              />
            </div>
          );
        } else {
          return <a href={node.data.uri}>{children}</a>;
        }
      },
    },
  };

Any ideas? I can get the div to show up properly but there's always a p tag around it.

<p><b></b><div class="videoWrapper"><iframe title="Unique Title 001" src="https://player.vimeo.com/video/XXXXX" frameborder="0" allowfullscreen=""></iframe></div></p>

heyitstowler commented 4 years ago

The INLINES.HYPERLINK node is always going to be nested in a paragraph node. The only way to remove the paragraph would be to rewrite the paragraph render function to manually check its children to see if it matches the case that you're highlighting and not render the p tag:


[BLOCKS.PARAGRAPH]: (node, children) => {
  if (functionToDetermineIfThisIsTheCaseYoureLookingFor(children)) {
     return children
  } else {
     return <p>{children}</p>
  }
defjosiah commented 2 years ago

I ran into this exact thing, and it was also giving us issues with some text styles not being applied directly after if an "inline-embedded-entry" (which is a more complicated component with divs, and an email input CTA) was rendered.

I fixed it by checking the children, separating out any "inline-embedded-entry" types, and rendering them separately.

Pasted below in case this helps anyone else. I'd honestly expect that this would be handled by the library. I feel like this is a very common use-case, I was surprised that it didn't handle it correctly (since divs, etc. as descendants of p tags cause all sorts of issues).

[BLOCKS.PARAGRAPH]: (entry: Block | Inline, children) => {
      const paragraphEntry = entry as Paragraph;
      if (!Array.isArray(children)) {
        return (
          <Text mb={3} size="s2">
            {children}
          </Text>
        );
      }

      const zipped: Array<[Inline | ContentfulText, ReactNode]> = paragraphEntry.content.map(
        (e, i) => [e, children[i]],
      );
      const { inline, standard } = zipped.reduce<{
        inline: Array<ReactNode>;
        standard: Array<ReactNode>;
      }>(
        (acc, curr) => {
          const [entry, child] = curr;
          if (entry.nodeType === INLINES.EMBEDDED_ENTRY) {
            acc.inline = acc.inline.concat(child);
            return acc;
          }
          acc.standard = acc.standard.concat(child);
          return acc;
        },
        { inline: [], standard: [] },
      );
      return (
        <>
          {inline}
          <Text mb={3} size="s2">
            {standard}
          </Text>
        </>
      );
    }
furkanbay commented 1 year ago

I just write an ugly but working.


      const contentElements = node.content.map((contentNode, index) => {
        if (
          contentNode.nodeType === 'hyperlink' &&
          contentNode.data.uri === 'some text'
        ) {
          return {
            type: 'withoutParagraph',
            content: <Fragment key={index}>{children[index]}</Fragment>,
          }
        } else {
          return {
            type: 'withParagraph',
            content: children[index],
          }
        }
      })
      const withoutParagraph = contentElements.some(
        (contentElement) => contentElement.type === 'withoutParagraph'
      )
      if (withoutParagraph) {
        return (
          <Fragment>
            {contentElements.map((contentElement) => contentElement.content)}
          </Fragment>
        )
      } else {
        return <p>{children}</p>
      }
    }