prismicio / prismic-react

React components and hooks to fetch and present Prismic content
https://prismic.io/docs/technologies/homepage-reactjs
Apache License 2.0
154 stars 40 forks source link

how to dynamically hyperlink words #95

Closed abhisheknaiidu closed 2 years ago

abhisheknaiidu commented 2 years ago

context - I've content in prismic, which already have words hyperlinked, but i want to hyperlink few more words as per SEO, how can i add it using htmlSerializer ?

currently doing this, but it removes the previously hyperlinked words. any solution to resolve this?

const htmlSerializer = function (type, children, element, content, key) {
  switch (type) {
    case Elements.paragraph:
      let paragraphContent = children.text ? children.text : '';

      element.text = paragraphContent;
      keywords.map((s, idx) => {
        if (paragraphContent.includes(s.text)) {
          paragraphContent = paragraphContent.replace(s.text, `<a href=${s.link}>${s.text}</a>`);
        }
      });
       return <p dangerouslySetInnerHTML={{ __html: paragraphContent }} />;

      default:
      return null;
  }
}
angeloashmore commented 2 years ago

Hey @abhisheknaiidu, good question! Your HTML serializer generally looks good, but I think you could move the <a> replacements to Elements.span rather than Element.paragraph. Elements.span is the lowest level of the serializer; paragraphs, for example, will contain spans.

The reason you are seeing other hyperlinks being removed is because you are stopping the serializer from reaching the other Element types. This is due to not including children in your return type.

Here's an example you could try:

const htmlSerializer = function (type, children, element, content, key) {
  switch (type) {
    case Elements.span: {
      if (!content) {
        return null
      }

      // For each keyword, replace the keyword with a link.
      let contentWithSEOAnchors = content
      for (const keyword of keywords) {
        contentWithSEOAnchors.replace(
          keyword.text,
          `<a href="${keyword.link}">${keyword.text}</a>`,
        )
      }

      // For each new line (`\n`) in the content, replace it with a <br />
      // This is done in the default HTML serializer and replicated here.
      const result = []
      contentWithSEOAnchors.split('\n').forEach((line, index) => {
        if (index > 0) {
          result.push(<br />)
        }

        result.push(line)
      })

      return result
    }

    default: {
      return null
    }
  }
}
abhisheknaiidu commented 2 years ago

@angeloashmore thanks for replying, so with this I'm able to add the anchor tags to the content tho, but not able to split the function (\n) in the content, could you see this code . (it's splitting only the manual anchor tags only which were added in Prismic, not splitting the SEO one's) ⬇️


function htmlSerializer(type, element, content, children, key) {
  switch (type) {
    case Elements.paragraph:
     return React.createElement('p', { className: 'paragraph-class' }, children);

    case Elements.span: {
      if (!content) {
        return null
      }

      // For each keyword, replace the keyword with a link.
      let contentWithSEOAnchors = content;
      // console.log(content)
      for (const keyword of keywords) {
        contentWithSEOAnchors = contentWithSEOAnchors.replace(
          keyword.text,
          `<a href="${keyword.link}">${keyword.text}</a>`,
        )
      }

     // not able to split
      const result = []
      contentWithSEOAnchors.split('/n').forEach((line, index) => {
        console.log(line, index)
        if (index > 0) {
          result.push(<br />)
        }

        result.push(line)
      })
      // console.log(result) thi

      return result
    }
    default:
      return null;
  }
};
angeloashmore commented 2 years ago

@abhisheknaiidu Sorry, I didn't full test out that example serializer I posted. Could you try this one instead?

function htmlSerializer(type, element, content, children, key) {
  switch (element.type) {
    case Elements.paragraph:
      return (
        <p className="paragraph-class" key={key}>
          {children}
        </p>
      );

    case Elements.span: {
      if (!content) {
        return null;
      }

      // For each keyword, replace the keyword with a link.
      let contentWithSEOAnchors = [content];
      for (const keyword of keywords) {
        contentWithSEOAnchors = contentWithSEOAnchors.map((contentSegment) => {
          if (typeof contentSegment === "string") {
            const result = [];

            contentSegment.split(keyword.text).forEach((segment, index) => {
              if (index > 0) {
                result.push(
                  <a key={`br-${index}`} href={keyword.link}>
                    {keyword.text}
                  </a>
                );
              }

              result.push(
                <React.Fragment key={index}>{segment}</React.Fragment>
              );
            });

            return result.flat();
          } else {
            return contentSegment;
          }
        });
      }

      // For each new line (`\n`) in the content, replace it with a <br />
      // This is done in the default HTML serializer and replicated here.
      const result = [];
      contentWithSEOAnchors.flat().forEach((segment) => {
        if (typeof segment === "string") {
          segment.split("\n").forEach((line, index) => {
            if (index > 0) {
              result.push(<br key={`br-${index}`} />);
            }

            result.push(<React.Fragment key={index}>{line}</React.Fragment>);
          });
        } else {
          result.push(segment);
        }
      });

      return result;
    }
    default:
      return null;
  }
}
angeloashmore commented 2 years ago

Hey @abhisheknaiidu, I'm going to close this issue but please feel free to comment if you haven't been able to get this working.

Thanks!