Giter Site home page Giter Site logo

block-content-to-react's People

Contributors

bjoerge avatar dependabot[bot] avatar hdoro avatar jonespen avatar kmelve avatar rexxars avatar vramdal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

block-content-to-react's Issues

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

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;
});

Output wrapped in an empty div container

The default container is a div. This creates an empty div that wrapping every element rendered. Sometimes we have invalid HTML markup because the contents are rendered inside a Heading tag. Heading tag can only contain inline elements: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#phrasing_content

Validator used: https://validator.w3.org/nu

Here is the code:
https://github.com/sanity-io/block-content-to-hyperscript/blob/d9d5adac36d50c0c012f7bb4099fb6f3fa67b382/src/serializers.js#L156

Should the default container value be just a Fragment for React apps?

I know we can just set the container prop to a Fragment. I am wondering if there are any other implications I am unable to see?

Is there a way to just return from a serializer?

I have a case where I want to serialze a special style inside the block type. But I want to return default rendering of all other styles. I dont want to have to write the serializer for all styles inside the block

How can I do this?

Improve error handling in BlockContent

The title says it all, I was curious if it would be a good idea to be able to either set an ENV variable or send a prop to the block renderer, so if NODE_ENV !== "production", only write missing serializer types errors to the console, instead of throwing. I am working in Next.js, and these errors cause the app to crash if BlockContent throws an error on server-side. We cannot add all the serializer types at once.

Typescript support

Would be great to get Enums for block types, like in Contentful. Also, typings for the props of the render nodes would be useful.

Can't create list in sanity

I can't get list items from the editor/schema

--mySchema
export default {
title: "Block Content",
name: "blockContent",
type: "array",
of: [
{
title: "Block",
type: "block",
// Styles let you set what your user can mark up blocks with. These
// correspond with HTML tags, but you can set any title or value
// you want and decide how you want to deal with it where you want to
// use your content.
styles: [
{ title: "Normal", value: "normal" },
{ title: "H1", value: "h1" },
{ title: "H2", value: "h2" },
{ title: "H3", value: "h3" },
{ title: "H4", value: "h4" },
{ title: "Quote", value: "blockquote" },
],
lists: [
{ title: "Bullet", value: "bullet" },
{ title: "Numbered", value: "number" }
],
// Marks let you mark up inline text in the block editor.
marks: {
// Decorators usually describe a single property – e.g. a typographic
// preference or highlighting by editors.
decorators: [
{ title: "Strong", value: "strong" },
{ title: "Emphasis", value: "em" },
],
// Annotations can be any object structure – e.g. a link or a footnote.
annotations: [
{
title: "URL",
name: "link",
type: "object",
fields: [
{
title: "URL",
name: "href",
type: "url",
},
],
},
],
},
},
// You can add additional types here. Note that you can't use
// primitive types such as 'string' and 'number' in the same array
// as a block type.
{
type: "image",
options: { hotspot: true },
},
],
};

editor
Screenshot 2021-06-15 at 17 47 21

response
Screenshot 2021-06-15 at 17 46 49

tried with a lot of variations of serializer like here:
#29
#33

but it just doesn't read the list items.

Issue with Ordered Lists

I'm using the default serializer with a few types for custom blocks

When using the list option in the editor, more specifically an ordered list, I can see that the content in the editor is numbered correctly.

When it comes to rendering this out on the frontend though, everything has the number 1 beside it.

The code shows that each list item is wrapped in an <ol> as you can see from this screenshot.
Screenshot 2020-12-19 at 08 17 40

Is there anything I should be adding to add to the serializer to get these to group up into the same list?

BlockContent displays only plain text

Ok, so I am working on a blog for my portfolio website, and I just got it working until I realized that when I style the body of my blog posts in Sanity Studio, those styles do not transfer to the actual website. All I see in the website is plain text. This might be a dumb question, but I have no Idea what i'm doing wrong.

Here is my SinglePost.js file:

import { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import client from "../../client";
import BlockContent from "@sanity/block-content-to-react";
import Header from "../Header";

export default function SinglePost() {
    const [singlePost, setSinglePost] = useState([])
    const [isLoading, setIsLoading] = useState(true)
    const { slug } = useParams()

    useEffect(() => {
        client
          .fetch(
            `*[slug.current == "${slug}"] {
            title,
            body,
            mainImage {
              asset -> {
                _id,
                url
              },
              alt
            }
          }`
          )
          .then((data) => setSinglePost(data[0]))
        setIsLoading(false)
    }, [slug])

    const serializers = {
        types: {
          code: (props) => (
            <pre data-language={props.node.language}>
              <code>{props.node.code}</code>
            </pre>
          ),
        },
      }

    return (
        <div className = "bg-gray-100 dark:bg-zinc-900">
            <Header />
            {isLoading ? ( <h1>Loading...</h1> ) : (
                <section className = "p-5 pb-20 lg:mx-28 md:mx-16 sm:mx-8">
                    <h1 className = "title mb-20">{singlePost.title}</h1>
                    <div className = "flex items-center justify-center">
                        {singlePost.mainImage && singlePost.mainImage.asset && (
                            <img src = {singlePost.mainImage.asset.url} alt = {singlePost.title} title = {singlePost.title} className = "rounded-xl shadow-xl dark:shadow-gray-100/10" />
                            )}
                    </div>
                    <p className = "paragraph mt-5 mb-5">By Brandon Pyle</p>
                    <div className="">
                        <BlockContent serializers={serializers} blocks={singlePost.body} projectId="2hp9gld0" dataset="production" />
                    </div>
                    <button>
                        <Link to = "/blog" className = "button">Read more articles</Link>
                    </button>
                </section>
            )}
        </div>
    )
}

Here is my blockContent.js file:

/**
 * This is the schema definition for the rich text fields used for
 * for this blog studio. When you import it in schemas.js it can be
 * reused in other parts of the studio with:
 *  {
 *    name: 'someName',
 *    title: 'Some title',
 *    type: 'blockContent'
 *  }
 */
export default {
  title: 'Block Content',
  name: 'blockContent',
  type: 'array',
  of: [
    {
      title: 'Block',
      type: 'block',
      // Styles let you set what your user can mark up blocks with. These
      // correspond with HTML tags, but you can set any title or value
      // you want and decide how you want to deal with it where you want to
      // use your content.
      styles: [
        {title: 'Normal', value: 'normal'},
        {title: 'H1', value: 'h1'},
        {title: 'H2', value: 'h2'},
        {title: 'H3', value: 'h3'},
        {title: 'H4', value: 'h4'},
        {title: 'Quote', value: 'blockquote'},
      ],
      lists: [{title: 'Bullet', value: 'bullet'}],
      // Marks let you mark up inline text in the block editor.
      marks: {
        // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
        decorators: [
          {title: 'Strong', value: 'strong'},
          {title: 'Emphasis', value: 'em'},
          {title: 'Code', value: 'code'},
          {title: 'Highlight', value: 'highlight'},
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          {
            title: 'URL',
            name: 'link',
            type: 'object',
            fields: [
              {
                title: 'URL',
                name: 'href',
                type: 'url',
              },
            ],
          },
        ],
      },
    },
    // You can add additional types here. Note that you can't use
    // primitive types such as 'string' and 'number' in the same array
    // as a block type.
    {
      type: 'image',
      options: {hotspot: true},
    },
  ],
}

You can find the complete source code here: https://github.com/bpyle02/portfolio
If you would like to see a live example of the error, check out this blog post I made that is meant to have different headings, bullet lists, etc.: https://brandonpyle.netlify.app/blog/how-to-properly-write-a-github-readme

Using with refs causes multiple re-renderers

const BlockRenderer = props =>  (
      <animated.div style={captionAnimation} ref={captionRef}>
        <p>{props.children}</p>
      </animated.div>
  );
 
. . .

<BlockContent
     blocks={section._rawContent}
     serializers={{
         types: { block: BlockRenderer},
     }}
/>

Seems like when I try and add an animated.div to the BlockRenderer it gets re-rendered multiple times. this seems to happen because of the ref I am passing. if I set a count on BlockRenderer, it is called multiple times on the page even though the content is not changing. wrapping the component in React.memo() also did not seem to help. Is there a way to pass a ref to the BlockContent component to use in the serializer/BlockRenderer?

Change default value of `renderContainerOnSingleChild` to `true`

I was very confused / surprised by the renderer not rendering my configured container all of a sudden. It took me a while to realise that the reason was that the block content only contained one element. I also nearly missed the option renderContainerOnSingleChild.

To have differing element hierarchies between content with one element and with multiple elements seems to be a special requirement rather then a good default. I suggest therefore to change the default of renderContainerOnSingleChild to true.

It would be a breaking change, though :-/

Thanks for considering :-)

Question: Table within text

I am looking for some guidance. (I could not find a sample with this use case.)

I am looking to include table data (rows, columns) within text. (In my case it is a price list.)

Imagine a product page with like:

Our product comes in three different styles.

| Style  | Price |
|--------|-------|
| Small  | $10   |
| Medium | $20   |
| Large  | $30   |

More text...

My understanding is that Portable Text has the List type but not Table type, correct?

Could this be edited with Sanity Studio?

If not, I believe one solution would be to add a new type in Sanity like "ProductStyle" with "Name" and "Price" properties.

Could I reference this from Portable Text?

Something like this:

Our product comes in three different styles.

<REFERENCE PRICE TABLE HERE>

More text...

How would I extend "block-content-to-react" to render the table?

Classname not passed when only single child

EDIT: It's working as expected. I was passing it in a different component. Please delete this issue if possible

<BlockContent blocks={content} projectId={process.env.NEXT_PUBLIC_SANITY_PROJECT_ID} dataset={process.env.NEXT_PUBLIC_SANITY_DATASET} className={markdownStyles.markdown} renderContainerOnSingleChild={true} />

The aboce is my code. The className works fine when there are multiple children but when I pass a single child the className is not utilized. The renderContainerOnSingleChild doesn't seem to change anything.

Documentation

There is a lot documentation on how to build the sanity cms (https://www.sanity.io/docs) but I coudn't find sufficient documentation on how to use the fetched data on the client side, which describes how to use sanity-io/block-content-to-react library with serializers e.g. types, marks, list, listItem to render custom react components.

Is there a hidden documentation I missed?

Apply truncate function on content

Hi, I have very simple data text (a description) and want to apply lodash truncate function on it.
But on some descriptions I have line-breaks which make my content a little more complex from a sanity point of view.
If I use a "block serializer"

<BlockContent
  blocks={work.descriptionRaw}
  serializers={{
    types: {
      block: (props) => {
        console.log(props.children);
        return <p>{props.children}</p>;
      },
    },
  }}
/>;

I can see some of the texts contain the following object which seems to represent a line break
{ "$$typeof": Symbol("react.element"), type: HardBreakSerializer(), key: "hb-1", … }

Therefore if I apply truncate function on it, it displays [object Object] for those objects being misinterpreted. What can I do to go around this problem ? Thanks !

Link urls being re-used

I know this package is deprecated now so I will try and move to the new package to see if it fixes this but we have a strange issue where we have a simple text of links and the 3rd url is re-used in the 6th link. The strange thing is it only does it in gatsby production/build mode and not dev. We're on the latest 3.0.0 version.

Thought I would report it anyway in case someone has seen this before. In the screenshot below for example the url for the 3rd link "Help Desk" is showing up re-used in the final 6th link "Spring Show Gallery" in prod/build only. I've tried debugging the data and it looks correct.

image

Pass projectId and dataset to the block renderer (only after using my own extended serializer)

When trying to build my gatsby site, i am getting this error after using block-content-to-react in more places than before. I dont get it when building locally.

upgraded to latest version of block-content-to-react without solving it. I dont even get the error message.

3:02:19 AM: error Building static HTML failed for path "/malte-brix-es-muss-gerechtigkeit-her/"
3:02:19 AM: 
3:02:19 AM:   40 |
3:02:19 AM:   41 |   if (!projectId || !dataset) {
3:02:19 AM: > 42 |     throw new Error(materializeError);
3:02:19 AM:      | ^
3:02:19 AM:   43 |   }
3:02:19 AM:   44 |
3:02:19 AM:   45 |   var ref = asset._ref;
3:02:19 AM: 
3:02:19 AM:   WebpackError: You must either:
3:02:19 AM:   
3:02:19 AM:   - getImageUrl.js:42 
3:02:19 AM:     [BrixLangeKanzleipage]/[@sanity]/block-content-to-hyperscript/lib/getImageUr    l.js:42:1
3:02:19 AM:   
3:02:19 AM:   - serializers.js:127 
3:02:19 AM:     [BrixLangeKanzleipage]/[@sanity]/block-content-to-hyperscript/lib/serializer    s.js:127:1
3:02:19 AM:   
3:02:19 AM:   - construct.js:3 
3:02:19 AM:     [BrixLangeKanzleipage]/[@babel]/runtime/helpers/construct.js:3:32
3:02:19 AM:   
3:02:19 AM:   - construct.js:13 
3:02:19 AM:     [BrixLangeKanzleipage]/[@babel]/runtime/helpers/construct.js:13:1
3:02:19 AM:   
3:02:19 AM:   - static-entry.js:263 
3:02:19 AM:     BrixLangeKanzleipage/.cache/static-entry.js:263:20

Feature request: Override react-native styles

I apologize if I missed something, but reading through the code, I don't see anything that lets me override the text styles for react-native.

I know it's possible to completely replace the element used during serialization but that's not exactly what I want. The default serializers are good! I just need to update the font.

Let me know if you're accepting PRs or thoughts. Would love to get a good solution in here is possible. Thanks for making it work in react-native in the first place!

BlockContent component tries to render a link using the key property as the mark name

I am pulling content to Gatsby from Sanity using GraphQL. Content arrives without issues and I can use inline images, header tags, block quotes etc. However, when a block contains a link, I get the following error:

Unknown mark type "68344811ed4b", please specify a serializer for it in the `serializers.marks` prop 
    at SpanSerializer (http://localhost:8000/commons.js:10953:29)

"68344811ed4b" corresponds to the key of the link.

This is the content I am passing to the BlockContent component:

content: Array(1)
0:
  children: Array(2)
    0: {_key: "48b95fb74a12", _type: "span", marks: Array(1), text: "Here is the link"}
    1: {_key: "4ac794989d8d", _type: "span", marks: Array(0), text: ""}
  length: 2
  __proto__: Array(0)
  markDefs: Array(2)
    0: {_key: "a02fe6d7d7ca", _type: "link"}
    1: {_key: "68344811ed4b", _type: "link", href: "http://exmple.com"}
  length: 2
  __proto__: Array(0)
  style: "normal"
  _key: "9cb5d7dcd417"
  _type: "block"

If I create a custom serializer with the name of the key (serializers.marks.68344811ed4b) this gets passed an item of type span. This seems to be true for any link in any Sanity document.

I am using v 2.0.7 of @sanity/block-content-to-react.

Failed `blocks` prop type

Versions

@sanity/block-content-to-react    2.0.6
react                             16.8.0-alpha.1

Issue

I've a pretty standard setup:

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

const Footer = ({ footerCards }) => (
  {footerCards.map(footerCard => (
    <div key={footerCard._id}>
      <FooterCard title={footerCard.title}>
        <BlockContent blocks={footerCard.information} />
      </FooterCard>
    </div>
  ))}
);

export default Footer;

But I receive a warning in the console:

Warning: Failed prop type: The prop `blocks` is marked as required in `SanityBlockContent`, but its value is `undefined`.

But everything else except this error seems to work fine and value of footerCard.information isn't undefined.

Images do not render when using block serializers

With this implementation, my content is rendering as I would expect it to:

<BlockContent
  projectId={process.env.NEXT_PUBLIC_PROJECT_ID}
  dataset={process.env.NEXT_PUBLIC_DATASET}
  blocks={post?.body}
/>

However, the addition of the following serializer makes it so the embedded images in the block content do not render:

const serializers = {
  block: (props): JSX.Element => {
    const { children, node } = props;
    const { style } = node;
    if (style === "h1") {
      return (
        <Typography className="mb-4" variant="h1">
          {children}
        </Typography>
      );
    }
    if (style === "h2") {
      return (
        <Typography className="mb-4" variant="h2">
          {children}
        </Typography>
      );
    }
    if (style === "h3") {
      return (
        <Typography className="mb-4" variant="h3">
          {children}
        </Typography>
      );
    }
    if (style === "h4") {
      return (
        <Typography className="mb-4" variant="h4">
          {children}
        </Typography>
      );
    }
    if (style === "h5") {
      return (
        <Typography className="mb-4" variant="h5">
          {children}
        </Typography>
      );
    }
    if (style === "h6") {
      return (
        <Typography className="mb-4" variant="h6">
          {children}
        </Typography>
      );
    }
    return BlockContent.defaultSerializers.types.block(props);
  }
};

I am thinking that return BlockContent.defaultSerializers.types.block(props); does not properly render images, but it's not clear from the docs how I would do so.

List items being interpreted as lists?

I am using block-content-to-react for some basic rich text. Somewhere in the pipeline it is turning what should be:

<ol>
   <li>Alpha<li>
   <li>Bravo<li>
   <li>Charlie<li>
</ol>

Into

<ol>
   <li>Alpha<li>
</ol>
<ol>
   <li>Bravo<li>
</ol>
<ol>
   <li>Charlie<li>
</ol>

Which is affecting the numberd list and intended spacing for the elements.

Here is what I have in sanity and what I expect:
In Sanity

...but here in staging it breaks what should be a list into multiple lists:
In Prod

When encountering unknown types and no serializer for that type exists, the component crashes

Versions

@sanity/block-content-to-react    2.0.1
react                             16.3.2

Issue

When the BlockContent encounters an unknown type defined in Sanity, the component crashes and effectively crashes the page. I don't know if this applies to marks and annotations as well.

It would be nice to have some way of telling the component to ignore unknown fields, and for several reasons:

  • When introducing new fields in an array-of-block field, it's very handy to deploy this beforehand in production and prepare all content before deploying frontend(s) to handle the new content.
  • When working in a dev-environment you are also effectively crashing other developers application when introducing new types in the array-of-block field. One workaround here is to work on a separate document, but you get the idea.

You could potentially add a switch for this in the component. I guess it would be up to you to decide if you should make this behaviour default as well. As we've already discussed, one could use the __DEV__ to define the behaviour, but then you would still encounter problems when working on a multi-developer setup as you would be breaking the other developers documents.

Custom mark rendered as random id instead of mark name

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"
  },

Pass in additional props to serialisers from render level

It's more of a question, but wanted to see if it's possible and if we could turn it into a feature.

Currently each mark serialiser is getting only props from a mark, but I need to use additional props that will be passed to <PortableText />.

Example:

export const PortableText = createPortableTextComponent({
  ...sanityConfig,
  serializers: {
    marks: {
       someSerializer(props, propsFromPortableText) {
           return <span>{props.children} {propsFromPortableText.something}</span>
       }
    },
  },
});

<PortableText blocks={blocks} extraProps={{ something: 50 }} />

Any ideas?

Upgrade to 1.3.9 causes error

I upgrade a bunch of dependencies in my gatsby application, including block-content-to-react from 1.3.5 to 1.3.9 and now I get this error whenever I try to render a block:
screen shot 2018-05-30 at 10 39 49 pm

I had a dig into source, and this line appears to be the culprit:

var internals = require('@sanity/block-content-to-hyperscript/internals');

mergeSerializers is expect to be a property of that import, but the contents of that file are simply:

module.exports = {
  blocksToNodes: require('./lib/blocksToNodes'),
  getImageUrl: require('./lib/getImageUrl'),
  getSerializers: require('./lib/serializers')
}

Upgrading react native breaks test suite

If I upgrade to latest react-native (at the moment 0.57.7), the test suite breaks due to a lot of snapshot mismatches.

Seems to be mostly caused by the same issue, <Text doesnt receive expected props:

● 001-empty-block

    expect(value).toMatchSnapshot()

    Received value does not match stored snapshot "001-empty-block 1".

    - Snapshot
    + Received

    @@ -3,11 +3,7 @@
          Object {
            "marginBottom": 16,
          }
        }
      >
    -   <Text
    -     accessible={true}
    -     allowFontScaling={true}
    -     ellipsizeMode="tail"
    -   />
    +   <Text />
      </View>

      59 |       .toJSON()
      60 |
    > 61 |     expect(tree).toMatchSnapshot()
         |                  ^
      62 |   })
      63 | })
      64 |

      at Object.toMatchSnapshot (test/rn/BlockContent.test.js:61:18)

"Unknown block type 'text'" error when rendering text from block content field

Versions

@sanity/block-content-to-react    2.0.7
react                             17.0.1

Issue

I'm just getting started with Sanity (+ NextJS) so I'm hoping I haven't made a rookie error here. I've started a project from scratch and this part of it deals with content modelling for a simple blog. I'm struggling with an error when trying to output the contents of a custom block field from a blog post.

Here is my (simplified) post document definition:

export default {
    title: 'Post',
    name: 'post',
    type: 'document',
    fields: [
        {
            title: 'Title',
            name: 'title',
            type: 'string',
            validation: Rule => Rule.required(),
        },
        {
            title: 'Content',
            name: 'contentBlocks',
            type: 'contentBlocks',
            validation: Rule => Rule.required(),
        },
    ],
};

Here is the definition for my custom contentBlocks field type (slightly simplified):

export default {
    title: 'Content Blocks',
    name: 'contentBlocks',
    type: 'array',
    of: [
        {
            title: 'Text',
            name: 'text',
            type: 'block',
        },
        {
            title: 'Image',
            name: 'image',
            type: 'image',
            fields: [
                {
                    title: 'Caption',
                    name: 'caption',
                    type: 'string',
                },
            ],
        },
        {
            title: 'Embed',
            name: 'embed',
            type: 'object',
            fields: [
                {
                    title: 'URL',
                    name: 'url',
                    type: 'url',
                    validation: Rule => Rule.required(),
                },
                {
                    title: 'Caption',
                    name: 'caption',
                    type: 'string',
                },
            ],
        },
    ],
};

And my simple Post component looks like this (post is injected by getStaticProps):

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

const Post = ({ post }) => {
    return (
        <article>
            <h1>{post.title}</h1>
            <BlockContent
                blocks={post.contentBlocks}
                imageOptions={{ w: 320, h: 240, fit: 'max' }}
                {...client.config()}
            />
        </article>
    );
};

However, upon requesting a post document path, I get the following error:

Error: Unknown block type "text", please specify a serializer for it in the `serializers.types` prop

So it seems the BlockContent component is receiving a block of type text (even though, as you can see, none are defined in the field spec) and doesn't know what to do with it. If I inspect the contents of post.contentBlocks, you can see the errant type in there (this is for an example post containing a line of text, an image block, followed by another line of text):

[
  {
    _key: 'fa67a6cc016b',
    _type: 'text',
    children: [ [Object] ],
    markDefs: [],
    style: 'normal'
  },
  {
    _key: '75c4937c4a94',
    _type: 'image',
    asset: {
      _ref: 'image-fe769ba1ea3b199479d317c59a4ac999a4489507-1296x1296-jpg',
      _type: 'reference'
    }
  },
  {
    _key: 'cd3522b721fc',
    _type: 'text',
    children: [ [Object] ],
    markDefs: [],
    style: 'normal'
  }
]

I can solve this error by manually mapping the text type to the standard serializer for block:

BlockContent.defaultSerializers.types.text = BlockContent.defaultSerializers.types.block;

Despite the fix however I still don't understand why this is happening, but I've not found anyone else with the same issue so I'm pretty sure I'm doing something stupid.

I should also note that I've tested existing documents and new documents in case some previous definition was persisting, but the result is the same.

Any suggestions would be much appreciated! Please let me know if I can provide any more information. Thanks!

Unknown props should be passed as attributes to the container, or don't use a container at all

Right now, SanityBlockContent handles the className attribute, but not, for example handle style attribute (very common attribute, surprised me className was accepted but not style). It's confusing that in cases of single nodes, <SanityBlockContent> does not render a container <div> element, but when multiple nodes it does, and when it does render a container, unknown attributes to <SanityBlockContent> are not passed along to the container to render them as attributes.

Please do one of the following:

  • Just render to a fragment and leave container-handling to the user. This is a breaking change, obviously, altering element nesting significantly.
  • Render a container if and with unknown attributes are passed along to <SanityBlockContent>. Breaking change in the sense that attributes that were specified but were ignored will no longer be ignored.

Empty alt not added to images in default serializer

I'm using the @sanity/block-content-to-react package like this:

<SanityBlockContent
  blocks={c._rawBlockContent}
  imageOptions={{ w: 600, fit: 'max' }}
  renderContainerOnSingleChild
  projectId={process.env.GATSBY_SANITY_PROJECT_ID}
  dataset={process.env.GATSBY_SANITY_DATASET}
  className="mt-5 prose"
/>

The image in the _rawBlockContent looks like this (it's using the blockContent that gets generate when you run sanity init and choose the blog schema):

{
  "_key": "a0fd6af0192c",
  "_type": "image",
  "asset": {
    "_ref": "image-5b0f778989438587159b584d5258a65c73f0217b-1800x1200-png",
    "_type": "reference"
  },
  "crop": {
    "_type": "sanity.imageCrop",
    "bottom": 0.15053948435618247,
    "left": 0,
    "right": 0,
    "top": 0.17204512497849422
  },
  "hotspot": {
    "_type": "sanity.imageHotspot",
    "height": 0.6774153906653233,
    "width": 1,
    "x": 0.5,
    "y": 0.5107528203111559
  }
}

And the outputted HTML looks like this:

<figure>
  <img src="https://cdn.sanity.io/images/vxhsnaeh/production/5b0f778989438587159b584d5258a65c73f0217b-1800x1200.png?rect=0,206,1800,813&amp;w=600&amp;fit=max">
</figure>

As most of the images I would be using are purely decorative, I was happy to use alt="", but this doesn't seem to be added by default.

Writing a custom serializer works, but I need to bring in the @sanity/image-url and @sanity/client packages to build the image URL which I'd rather not do.

import { urlFor } from '../utils/sanity-client';

function ImageRenderer({ node }) {
  return <img src={urlFor(node.asset._ref).width(600).url()} alt="" />;
}

const serializers = {
  types: {
    image: ImageRenderer,
  },
};
<img src="https://cdn.sanity.io/images/vxhsnaeh/production/5b0f778989438587159b584d5258a65c73f0217b-1800x1200.png?w=600" alt="">

Is there a better way to handle this?

highlight not working

i add some customization in my sanity portable text. i just want to show the highlighted text but in my web project it did not show highlighted but the em and strong tag is working in portable text

How can i use Katex?

Hello everyone. I try to using Sanity Latex Input. Sanity studio ok but in frontend(nextjs) i cant show latex content with @sanity/block-content-to-react. How can i do this?

Error: Unknown block type "latex", please specify a serializer for it in the serializers.types prop.
and

Unknown block type "undefined", please specify a serializer for it in the serializers.types prop

how can i serialize Latex?

thanks a lot.

How to serialize list in block

I cannot seem to figure out how to serialize for lists in block

Example ul block as _rawBody.
image

This is my serializer.

const serializer = {
    types: {

      image: props => (
        <figure className="has-text-centered">
          <img
            src={urlFor(props.node.asset)
              .width(600)
              .url()}
            alt={props.node.alt}
            style={{ borderRadius: "5px" }}

          />

          <figcaption>{props.node.caption}</figcaption>
        </figure>
      ),
      block(props) {
        switch (props.node.style) {
          case 'h1':
            return <h1 className="">{props.children}</h1>

          case 'h2':
            return <h2 className="">{props.children}</h2>

          case 'h3':
            return <h3 className="">{props.children}</h3>

          case 'h4':
            return <h4 className="">{props.children}</h4>
          case 'li':
            return <h4 className="">{props.children}</h4>

          case 'blockquote':
            return <blockquote className="">{props.children}</blockquote>
          case 'normal':
            if (props.listItem) return <strong>{props.children}</strong>
            else return <p className="is-family-secondary is-size-5">{props.children}</p>
          default:
            return <p className="is-family-secondary">{props.children}</p>
        }
      }
    }
  };

"textBlock" doesn't seem to work in blockTypeHandlers

I try to overwrite and add a class to the paragraph-element with this code:

<BlockContent
  blockTypeHandlers={{
    textBlock: {
      p: ({ children = [] }) => <p className="lede">{children}</p>,
    },
  }}
  blocks={text}
/>

But it only returns the default <p>...</p> syntax. I've tried several variations, with and without parameter destructuring.

Invalid Hook Call when deploying Sanity

I'm not sure what happened, but I was trying to get my BlockContent component to work because it isn't displaying any styles (only plain text), and now I can't deploy my sanity project! It runs fine except when I try to deploy it.

I am getting this error:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
    at resolveDispatcher (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/base/node_modules/react/cjs/react.development.js:1476:13)    
    at useMemo (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/base/node_modules/react/cjs/react.development.js:1531:20)
    at Document (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/base/lib/components/Document.js:49:40)
    at processChild (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3353:14)
    at resolve (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3270:5)
    at ReactDOMServerRenderer.render (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3753:22)
    at ReactDOMServerRenderer.read (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3690:29)
    at Object.renderToStaticMarkup (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:4314:27)
    at buildStaticAssets (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/core/lib/actions/build/buildStaticAssets.js:162:118)
    at async _default (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/core/lib/actions/deploy/deployAction.js:93:9)

and here is my package.json file:

{
  "name": "brandonsblog",
  "private": true,
  "version": "1.0.0",
  "description": "",
  "main": "package.json",
  "author": "Brandon Pyle <[email protected]>",
  "license": "UNLICENSED",
  "scripts": {
    "start": "sanity start",
    "build": "sanity build"
  },
  "keywords": [
    "sanity"
  ],
  "dependencies": {
    "@sanity/base": "^2.23.2",
    "@sanity/block-content-to-react": "^3.0.0",
    "@sanity/code-input": "^2.23.2",
    "@sanity/core": "^2.23.2",
    "@sanity/default-layout": "^2.23.1",
    "@sanity/default-login": "^2.23.1",
    "@sanity/desk-tool": "^2.23.2",
    "@sanity/vision": "^2.23.1",
    "prop-types": "^15.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "sanity-plugin-markdown": "^2.1.0",
    "styled-components": "^5.2.0"
  },
  "devDependencies": {},
  "repository": {
    "type": "git",
    "url": "https://github.com/bpyle02/my-portfolio-new.git"
  }
}

You can find my code here: https://github.com/bpyle02/portfolio
And the live website here: https://brandonpyle.netlify.app/blog/how-to-properly-write-a-github-readme

How to render empty <p></p> as linebreak?

I'm using empty lines for better readability

스크린샷 2021-09-22 오후 10 58 53

스크린샷 2021-09-22 오후 11 00 00

These empty lines are ignored.

스크린샷 2021-09-22 오후 11 01 33

I tried using \n / empty space / and  

I guess the serializer just renders them as strings

React Native: Hotspot / crop not working for images

This may apply to plain React as well.

Hotspot & crop settings aren't getting translated into image URL parameters for block content images. It looks like it should be happening, but there is some translation in internals happening to the image/asset objects that doesn't allow the crop and hotspot properties to get translated.

Example image node:
{_key: "8e61aa8bb377"_type: "image"asset: {_ref: "image-208d3bbfdb69bf70d8101c5d7917e87534e82fc6-2400x3000-jpg", _type: "reference"}, crop: {_type: "sanity.imageCrop", bottom: 0, left: 0, right: 0, top: 0}, hotspot: {_type: "sanity.imageHotspot", height: 0.1439999999999996, width: 0.7633333333333336, x: 0.3816666666666668, y: 0.6746666666666663}, __proto__: Object }

Turns into this URL: https://cdn.sanity.io/images/[mine]/staging/208d3bbfdb69bf70d8101c5d7917e87534e82fc6-2400x3000.jpg

Whereas, a non-Block Content image object:
__typename: "Image" asset: __typename: "SanityImageAsset" url: "https://cdn.sanity.io/images/[mine]/staging/e90e33423f8ff21a275dd8b475ab5b5702db638b-1578x876.png" __proto__: Object crop: __typename: "SanityImageCrop" bottom: 0 left: 0 right: 0 top: 0 __proto__: Object hotspot: __typename: "SanityImageHotspot" height: 0.9571509678525129 width: 0.29369797859690766 x: 0.45362663495838296 y: 0.47857548392625643 __proto__: Object

using imageUrlBuilder from @sanity/image-url translates the rect and focal point URL parameters correctly.

Custom type renderers are not reused, but mounted/unmounted excessively

I see a lot of unmounting/re-mounting of my component that is used in a custom serializer. In my mind, the component should not be unmounted/remounted on every props update, but instead just updated with new props.

Given the following app (or see sandbox):

import ReactDOM from 'react-dom';
import React, {Component} from "react";
import BlockContent from "@sanity/block-content-to-react";

class Sometype extends Component {
    
    componentDidMount() {
        console.log("Sometype.componentDidMount - this should only happen once", arguments);
    }

    componentWillReceiveProps(nextProps) {
        console.log("Sometype.componentWillReceiveProps - this should happen on every click, but never happens", arguments);
    }

    render() {
        return <p onClick={this.props.clickHandler}>Click me! {this.props.children}</p>
    }
}

const block = {
    "_key":"58cf8aa1fc74",
    "_type":"sometype",
    "something":{someProperty: "someValue"}
};

class App extends Component {
    constructor() {
        super();
        this.state = {clicks: 0};
        this.serializers = {
            types: {
                sometype: props => (
                    <Sometype clickHandler={() => this.setState({clicks: this.state.clicks + 1})}>
                        {props.node.something.someProperty + " " + this.state.clicks}
                    </Sometype>
                )
            }
        };
    }


    render() {
        return (
            <div className="App">
                <BlockContent key={'blockcontent-1'} blocks={[block]} serializers={this.serializers}/>
            </div>
        );
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

The app uses BlockContent with a map of a single serializer for type sometype. The serializer renders a Sometype component.

In the browser, when clicking any content from Sometype, the state of the container component is updated, thus triggering a re-render.

At this point, I would expect Sometype.componentWillUpdate to be called, with the new updated props. Instead, the Sometype instance is unmounted, and a new instance is mounted, thus never calling componentWillUpdate. I'm not sure this violates any spec of a React component, but it is certainly uncommon behaviour.

Compare to an app not using BlockContent, where Sometype.componentWillUpdate is called on props update.

By modifying block-content-to-hyperscript/lib/serializers.js so that it returns an object instead of a function (thus hardcoding React.createElement instead of passing it as a parameter to the function), and modifying BlockContent to used the exported object instead of calling the function, the problem goes away, and Sometype.componentWillUpdate is called as expected.

This article describes a somewhat similar problem, but I am unable to see how it can apply to this problem.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.