sanity-io / block-content-to-html

Deprecated in favor of @portabletext/to-html
MIT License
20 stars 2 forks source link

Can you serialize without hypescript? #3

Open hdoro opened 5 years ago

hdoro commented 5 years ago

Howdy there, thanks for your time!

So, I'm building a website with components written in plain JS template literals, but the current implementation escapes the html inside of the strings:

const imageStripe = props => `(TEMPLATE HERE)`

const serializers = {
  types: {
    imageStripe, // html gets escaped, rendering my template useless
  },
  marks: {
    // this would especially useful for marks
    'mark': ({ children }) => `<mark>${children.join(' ')}</mark>`
  }
}

I understand hyperscript helps with security reasons by escaping HTML, but you could consider adding an option flag to accept plain strings without escaping them as well 😄

For now I figure this isn't possible so I'm creating my own, simple blocksToHtml, but wanted to start this discussion here :)

Edit: after realizing creating a blocksToHtml function is quite the work, I went back to this package and realized I was doing something wrong with the props I passed to my components... now I know this is just a problem of string escaping 🤗

hdoro commented 5 years ago

I managed to make it work through hyperscript's innerHTML, but that adds extra tags to my markup, which are absolutely unnecessary:

// This serializer for "imageStripe" works
const serializers = {
  types: {
    imageStripe: p => h("div", { innerHTML: imageStripe({ ...p.node }) })
  }
};
<!-- But the resulting HTML now has an extra tag -->
<div>
  <section class="image-stripe">
    <!-- Template is nested -->
  </section>
</div>

And if I try doing h('', { innerHTML: imageStripe({ ...p.node}) }), hyperscript will yell at me with: Cannot set property 'innerHTML' of null... Not the best solution, but I'll keep with it for now, please let me know if there's another way 🙌

Edit: I'm now including a %remove-this% tag as the wrapper for each component and running a HTMLstring.replace(/((<\/|<)%remove-this%>)/g, '') function to remove them from the final markup, and it works perfectly! But, again, kinda hacky and not the best solution. Maybe a section in the README would help others struggling with this.

ayyash commented 5 years ago

not sure i'm still dwelling around, but I had a similar problem though your pointer to blockToHtml made me dig a bit deeper and finally just do this:

mytype: p => {
const div = document.createElement('div');
div.innerHTML = imageStripe(...)
return div.children[0];
}

Of course some assumptions there, like there is one node, pretty sure you can have multiple child nodes and figure out a way to return them all... I don't know, I just got too excited thought of sharing it first.

raine commented 4 years ago

Thanks for the workaround @hcavalieri. My use case was rendering HTML email templates and I wanted a custom serializer to output just some HTML I copy-pasted from the interwebs. I did try converting it to hyperscript but it ended up being way too verbose.

Ended up with this:

// block-content-to-html escapes strings so the only way to render the button
// as raw html is through hyperscript
const renderCtaButtonToHtml = ({
  node: { linkUrl, text }
}: any): HTMLDivElement =>
  h('div', {
    innerHTML: `
      <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
        <tbody>
          <tr>
            <td align="left">
              <table role="presentation" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                  <tr>
                    <td><a href="${linkUrl}" target="_blank">${text}</a></td>
                  </tr>
                </tbody>
              </table>
            </td>
          </tr>
        </tbody>
      </table>`
  })