sanity-io / block-content-to-react

Deprecated in favor of @portabletext/react
https://github.com/portabletext/react-portabletext
MIT License
161 stars 25 forks source link

Custom mark rendered as random id instead of mark name #51

Closed vonba closed 3 years ago

vonba commented 3 years ago

I am trying to include links in block content, but they are being output as marks with random ids such as 8d165bb07736 instead of link.

I have essentially just followed the Sanity tutorial here: https://www.sanity.io/guides/portable-text-internal-and-external-links

In other words I have added the following to annotations in the schema:

{
            name: 'link',
            type: 'object',
            title: 'External link',
            fields: [
              {
                name: 'href',
                type: 'url',
                title: 'URL',
              },
              {
                title: 'Open in new tab',
                name: 'blank',
                type: 'boolean',
              },
            ],
          },

It appears as expected in Sanity Studio: I am able to set the URL, there is a checkbox for "open in new tab", etc.

I am also providing a custom serializer:

const serializers = {
  marks: {
    link: ({mark, children}) => {
      // Read https://css-tricks.com/use-target_blank/
      const { blank, href } = mark
      return blank ?
        <a href={href} target="_blank" rel="noopener">{children}</a>
        : <a href={href}>{children}</a>
    }
  }
}
<BlockContent blocks={event.body} serializers={serializers} />

However in the console I am seeing that the custom serializer never kicks in, because the mark is not being named "link", instead it arrives as "8d165bb07736": Unknown mark type "8d165bb07736", please specify a serializer for it in the serializers.marks

Gatsby dependencies

"dependencies": {
    "@sanity/block-content-to-react": "^2.0.7",
    "babel-plugin-styled-components": "^1.12.0",
    "dotenv": "^9.0.0",
    "gatsby": "^3.4.1",
    "gatsby-image": "^3.9.0",
    "gatsby-plugin-anchor-links": "^1.2.1",
    "gatsby-plugin-image": "^1.4.1",
    "gatsby-plugin-manifest": "^3.4.0",
    "gatsby-plugin-react-helmet": "^4.4.0",
    "gatsby-plugin-sanity-image": "^0.8.0",
    "gatsby-plugin-sharp": "^3.4.2",
    "gatsby-plugin-sitemap": "^4.0.0",
    "gatsby-plugin-styled-components": "^4.4.0",
    "gatsby-source-filesystem": "^3.4.0",
    "gatsby-source-sanity": "^7.0.4",
    "gatsby-transformer-sharp": "^3.4.0",
    "normalize.css": "^8.0.1",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-helmet": "^6.1.0",
    "react-slick": "^0.28.1",
    "styled-components": "^5.3.0"
  },

Sanity dependencies

"dependencies": {
    "@sanity/base": "^2.10.2",
    "@sanity/components": "^2.2.6",
    "@sanity/core": "^2.10.2",
    "@sanity/default-layout": "^2.10.2",
    "@sanity/default-login": "^2.8.0",
    "@sanity/desk-tool": "^2.10.2",
    "@sanity/vision": "^2.10.0",
    "prop-types": "^15.7",
    "react": "^17.0",
    "react-dom": "^17.0",
    "sanity-plugin-order-documents": "^0.0.19"
  },
gitsebs commented 3 years ago

Facing the same issue

"react": "^17.0.2", "react-dom": "^17.0.2", "@sanity/block-content-to-react": "^3.0.0",

ryansheehan commented 3 years ago

Facing the same issue

"react": "^17.0.2", "react-dom": "^17.0.2", "@sanity/block-content-to-react": "^3.0.0",

Exact same setup and issue.

ryansheehan commented 3 years ago

Okay I figured out what I was doing wrong. Hopefully this helps you @ScholtenSeb and @vonba .

Because the links are annotations, they are actually marks not types. When defining my serializer object I had to make the change from

const serializers: Serializers = {
  types: {
    internalLink: InternalLink,
    externalLink: ExternalLink,    
  }
}

to

const serializers: Serializers = {
  marks: {
    internalLink: InternalLink,
    externalLink: ExternalLink,    
  }
}

I wrote my own typing for the Serializer object as

interface Serializers {
  types?: Record<string, (props: any) => JSX.Element | null>;
  marks?: Record<string, (props: any) => JSX.Element | null>;
  list?: React.Component;
  listItem?: React.Component;
  hardBreak?: React.Component;
  container?: React.Component;
}
vonba commented 3 years ago

Hi Ryan! Have a look at the code in my post, I'm also defining my serializer for marks, not types. Can you spot any errors there?

The error message indeed also says "unknown mark..."

So it doesn't seem like that's the issue unless I'm missing something.

On Sun, 3 Oct 2021, 13:14 Ryan Sheehan, @.***> wrote:

Okay I figured out what I was doing wrong. Hopefully this helps you @ScholtenSeb https://github.com/ScholtenSeb and @vonba https://github.com/vonba .

Because the links are annotations, they are actually marks not types. When defining my serializer object I had to make the change from

const serializers: Serializers = { types: { internalLink: InternalLink, externalLink: ExternalLink, }}

to

const serializers: Serializers = { marks: { internalLink: InternalLink, externalLink: ExternalLink, }}

I wrote my own typing for the Serializer object as

interface Serializers { types?: Record<string, (props: any) => JSX.Element | null>; marks?: Record<string, (props: any) => JSX.Element | null>; list?: React.Component; listItem?: React.Component; hardBreak?: React.Component; container?: React.Component;}

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sanity-io/block-content-to-react/issues/51#issuecomment-932999924, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHKPMHDCCE6O76N5BWY3WMLUFCMRTANCNFSM5CMBIMSQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

ryansheehan commented 3 years ago

@vonba Sorry, I overlooked your original post. Upon a closer look, I don't spot anything different aside from naming differences to what I have.

This is what I have wired. Maybe help spot any difference?

{
    type: 'array',
    name: 'body',
    title: 'Body',
    of: [
      {
        type: 'block',
        marks: {
          annotations: [
            {
              name: 'externalLink',
              type: 'object',
              title: 'External Link',              
              blockEditor: {
                icon: FaExternalLinkAlt,                  
              },
              fields: [
                {
                  name: 'url',
                  type: 'url'
                }
              ]
            },
const ExternalLink: FC<{mark: any}> = ({mark, children}) => {
  const { url} = mark;  
  return (
    <a href={url} className="link external-link">{children}</a>
  )
}

const serializers: Serializers = {
  marks: {
    externalLink: ExternalLink,    
  }
}

export const BlockContent: FC<BlockContentProps> = ({blocks, className}) => {
  return <SanityBlockContent blocks={blocks} 
    className={classnames('sanity-block-content', className)}
    serializers={serializers} 
    ignoreUnknownTypes={false}
    renderContainerOnSingleChild={true}
  />
};
vonba commented 3 years ago

They really look identical to me except that you are setting the icon in the editor, and that I'm adding the "new tab" option.

The thing that's crazy about it is that when the block data reaches the front end the custom elements are just named as random IDs. So must have something to do with how the stored data is interpreted.

vonba commented 3 years ago

It actually seems like this is how the contents comes out through the raw GraphQL query. The block below should be a link, but it's missing the url property and the mark should be "link" instead of "b4ff1a687243".

{
                      "_key": "01617238a47b",
                      "_type": "span",
                      "marks": [
                        "b4ff1a687243"
                      ],
                      "text": "Magdalena Wosinka"

},

However, the content above is still read back correctly to the editor, so the information isn't lost.

vonba commented 3 years ago

I found the issue.

A while back I found a bug where in some cases the markDefs field was missing from blocks. My solution was to add an empty markDefs field on all blocks. By doing that I am overwriting any custom marks. I didn't notice it at the time because I wasn't using any custom annotations.

Not sure if the bug I mention is still an issue in newer versions of Sanity.