Netlify-cms: Custom editor component doesn't work

Created on 11 Oct 2018  Â·  6Comments  Â·  Source: netlify/netlify-cms

Describe the bug
I've added custom editor component:

javascript CMS.registerEditorComponent({ id: "youtube", label: "Youtube", fields: [{name: 'id', label: 'Youtube Video ID', widget: 'string'}], pattern: /^<div class="yo-youtube"><iframe src="\/\/www.youtube.com\/embed\/(.*)" frameborder="0" allowfullscreen><\/iframe><\/div>/, fromBlock: function(match) { console.log(match) return { id: match[1] }; }, toBlock: function(obj) { return ( `<div class="yo-youtube"><iframe src="//www.youtube.com/embed/${obj.id}" frameborder="0" allowfullscreen></iframe></div>` ); }, toPreview: function(obj) { return ( `<div class="yo-youtube"><iframe src="//www.youtube.com/embed/${obj.id}" frameborder="0" allowfullscreen></iframe></div>` ); } });

I works well when I add widget in markdown. But if I save and reopen entity or switch to markdown and back to rich text , it is displayed as html, not as widget

Expected behavior
It should be displayed as widget, not as html

Screenshots
screenshot

Applicable Versions:

  • Netlify CMS version: 2.1.2
  • Git provider: GitHub
  • OS: Macos High sierra
  • Browser version chrome 69
  • Node.JS version: 9.11

Most helpful comment

this is really weird
if i replace span with div in example above, it breaks

I guess this is because netlify breaks up the source into chunks ("lines") and might be creating separate chunks for each block level html element (and not for inline elements like span, img…). The regex that is using your specified pattern matches only individual chunks, not the whole body, so as soon as your regex would have to match across chunks, it will not work anymore.

The reason given is that this is for performance reasons – which I understand, but it might be nice to disable this performance-tweak on a per-component basis? One could still be smart about regexing by optimizing how to check for multiple matches…

This is a major deal breaker, since this makes it impossible to use netlify-cms for any serious sites – custom blocks fail for simple things like multi column setups…

1044 Same issue

All 6 comments

I think this is the same problem as https://github.com/netlify/netlify-cms/issues/1044

Basically only simple text paragraphs without markdown or inline HTML can be matched by editor components. At present custom components are only really suited for hooking into simple shortcode-like syntax.

text paragraphs without markdown or inline HTML can be matched by editor components.

Hmm...here is an example with inline HTML and it works like a charm

CMS.registerEditorComponent({
      id: "sidenote",
      label: "Sidenote",
      fields: [{
        name: 'element',
        label: 'In-Text Element',
        widget: 'string'
      }, {
        name: 'sidenote',
        label: 'Sidenote',
        widget: 'markdown'
      }],
      pattern: /^<span class="sidenote"><span class="sidenote__toggle">(.*)<\/span><span class="sidenote__note">(.*)<\/span><\/span>/,
      fromBlock: function(match) {
        return {
          element: match[1],
          sidenote: match[2],
        };
      },
      toBlock: function(obj) {
        return (
          '<span class="sidenote"><span class="sidenote__toggle">' + obj.element + '</span><span class="sidenote__note">' + obj.sidenote + '</span></span>'
        );
      },
      toPreview: function(obj) {
        return (
          '<span class="sidenote"><span class="sidenote__toggle">' + obj.element + '</span><span class="sidenote__note">' + obj.sidenote + '</span></span>'
        );
      },
    });

this is really weird
if i replace span with div in example above, it breaks

this is really weird
if i replace span with div in example above, it breaks

I guess this is because netlify breaks up the source into chunks ("lines") and might be creating separate chunks for each block level html element (and not for inline elements like span, img…). The regex that is using your specified pattern matches only individual chunks, not the whole body, so as soon as your regex would have to match across chunks, it will not work anymore.

The reason given is that this is for performance reasons – which I understand, but it might be nice to disable this performance-tweak on a per-component basis? One could still be smart about regexing by optimizing how to check for multiple matches…

This is a major deal breaker, since this makes it impossible to use netlify-cms for any serious sites – custom blocks fail for simple things like multi column setups…

1044 Same issue

Thanks for clarification!

found a solution to that. I create my editor component in React and stringify it.
the stringified component also has to match the 'pattern' regex. here's how:

import React from 'react';
import { renderToString } from 'react-dom/server';
import CMS from 'netlify-cms-app';

const TravelQuote = (props) => {
  const authorIsAnonymous = authorName && authorName.toLowerCase() === 'anonymous';
  const authorImg = props.authorPic ? props.authorPic : ""; // src needs to exist *
  // * the React components automatically removes the src attr if 
  // its attribute is undefined when it creates it.
  // With no src though, the component structur won't match the regex.

  return (
      `<div class="travel-quote">
        <p class="travel-quote__copy">{props.quote}</p>
        <span class="travel-quote__author">{props.authorName}</span>
        <img class="travel-quote__author-pic" src={authorImg} alt={props.authorName} />
      </div>`
  )
}

// create string from component to create the regex 
// for CMS.registerEditorComponent 'pattern'
const patternString = renderToString(
  `
<TravelQuote 
    quote='(.*)'
    authorName='(.*)' 
    authorPic='(.*)'/>
`
);

// create the regex
const travelQuoteRegex = new RegExp(patternString.replace(/\//g, '\\/'));

CMS.registerEditorComponent({
  id: "travelQuote",
  label: "Travel quote",
  fields: [
    { name: 'quote', label: 'Quote', widget: 'string' },
    { name: 'authorName', label: 'Author name', widget: 'string' },
    { name: 'authorPic', label: 'Author picture', widget: 'image' },
  ],
  pattern: travelQuoteRegex,
  fromBlock: function(match) {
    return {
      quote: match[1],
      authorName: match[2],
      authorPic: match[3],
    };
  },
  toBlock: function(obj) {
    return renderToString(
      `<TravelQuote 
        quote={obj.quote} 
        authorName={obj.authorName} 
        authorPic={obj.authorPic} />`
    )
  },
  toPreview: function(obj) {
    return renderToString(
      `<TravelQuote 
        quote={obj.quote} 
        authorName={obj.authorName} 
        authorPic={obj.authorPic} />`
    )
  },
});

// Note: renderToString() works only if the project is runned with the Production
// environment, with gatsby build and gatsby serve.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

TomPichaud picture TomPichaud  Â·  3Comments

Narno picture Narno  Â·  3Comments

zebapy picture zebapy  Â·  3Comments

BerkeleyTrue picture BerkeleyTrue  Â·  3Comments

ghost picture ghost  Â·  3Comments