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

missing markDefs on `gatsby-source-sanity` graphql API breaks BlockContent? #43

Closed Twoy519 closed 3 years ago

Twoy519 commented 3 years ago

Hi! I commented on an issue I found on gatsby-source-sanity as well but I think this repo is a better place for this question?

I'm using sanity on a gatsby project and therefore working with graphql. I am attempting to render Portable Text with BlockContent but it was breaking. I believe this is due to the fact that markDefs aren't supported by the graphql api as escalated here, but this component expects markDefs to be present. When they are not, I hit the error detailed below.

So here is the component I am using:

import { graphql } from 'gatsby';
import React from 'react';
import BlockContent from '@sanity/block-content-to-react';

const serializers = {
  types: {
    block: (props) => BlockContent.defaultSerializers.types.block(props),
  },
};

export default function TestPage({ data }) {
  const { portableTextDescription } = data.item;

  // portableTextDescription.map((i) => {
  //   i.markDefs = [];
  //   return i;
  // });

  return (
    <>
      <BlockContent
        blocks={portableTextDescription}
        serializers={serializers}
      />
    </>
  );
}

export const query = graphql`
  query TestItem {
    item: sanityItem(
      slug: { current: { eq: "slug-for-desired-product" } }
    ) {
      portableTextDescription {
        _key
        _type
        children {
          _key
          _type
          marks
          text
          __typename
        }
        style
        list
        _rawChildren
        __typename
      }
    }
  }
`;

This hits an error:

TypeError: Cannot read property `find` of undefined in node_modules/@sanity/block-content-to-hyperscript/lib/buildMarksTree.js:52

If you uncomment lines 14:17 where I manually add markDefs to each item in the Portable Text array, the bug is fixed and everything renders correctly:

portableTextDescription.map((i) => {
  i.markDefs = [];
  return i;
});
Twoy519 commented 3 years ago

I think this is preventing me from using external links as well. I'm following along your tutorial but the graphql API not having the markDefs for formatting is preventing this from working.

"markDefs": [
        {
          "_key": "d0c3457cdde3",
          "_type": "link",
          "blank": true,
          "href": "https://examplesite.com"
        }
      ],
jamesanderegg commented 3 years ago

@Twoy519 Did you find a fix for this issue? I saw someone suggested appending the markdefs?

brendenmoore commented 3 years ago

I'm having the same issue. Does anyone know if there is a fix for this?

Twoy519 commented 3 years ago

I got this working by:

1) pull in the _rawPortableTextDescription

2) you have to write all the custom formatting for links and stuff in the serializer marks yourself. Really....really annoying the default BlockContent thing doesn't handle this stuff more automatically. I hated this.

graphql:

item: sanityItem(slug: { current: { eq: $slug } }) {
  name
  id
  _rawPortableTextDescription
}

React Component:

import BlockContent from '@sanity/block-content-to-react';

export default function SingleItemPage({ data }) {
  const { item } = data;

  const serializers = {
    types: {
      block: (props) => BlockContent.defaultSerializers.types.block(props),
      span: (props) => BlockContent.defaultSerializers.types.span(props),
    },
    marks: {
      link: ({ mark, children }) => {
        const { blank, href } = mark;
        return blank ? (
          <a
            href={href}
            target="_blank"
            rel="noreferrer noopener"
            style={{ textDecoration: 'underline' }}
          >
            {children}
          </a>
        ) : (
          <a href={href} style={{ textDecoration: 'underline' }}>
            {children}
          </a>
        );
      },
      internalLink: ({ mark, children }) => {
        const { slug = {} } = mark;
        const href = `/${slug.current}`;
        return (
          <a href={href} style={{ textDecoration: 'underline' }}>
            {children}
          </a>
        );
      },
    },
  };

  return (
    <BlockContent
        blocks={item._rawPortableTextDescription}
        serializers={serializers}
      />
  )
brendenmoore commented 3 years ago

Thanks for getting back to me! I was actually just about to post that I had figured it out.

Here's something else I found along the way: react-portable-text

It uses the sanity block text to react package under the hood but makes the syntax easier for defining elements.

It also needs the _rawField. It's working great for me so far!

Twoy519 commented 3 years ago

Oh nice! Thanks I’ll try this out

On Mar 20, 2021, at 6:09 PM, Brenden Moore @.***> wrote:

 Thanks for getting back to me! I was actually just about to post that I had figured it out.

Here's something else I found along the way: react-portable-text

It uses the sanity block text to react package under the hood but makes the syntax easier for defining elements.

It also needs the _rawField. It's working great for me so far!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

fderen-dev commented 3 years ago

@Twoy519 _raw version of object returns markDefs array

byebyers commented 2 years ago

That worked for me @fderen-dev