Creating an Editor Component

So I am trying to implement an editor component that adds a <span> element for an image caption. This is meant to go with the original image editor component that comes with Netlify CMS.

This is my caption.js file

import React from 'react';

const caption = {
  label: 'Image Caption',
  id: 'caption',
  fromBlock: match =>
    match && {
      caption: match[1]
    },
  toBlock: ({ caption }) =>
    `![${caption || ''}]`,
  // eslint-disable-next-line react/display-name
  toPreview: ({ caption }) => {
    return <span>{caption || ''}</span>;
  },
  pattern: /^!\[(.*)\]$/,
  fields: [
    {
      label: 'Caption',
      name: 'caption',
    },
  ],
};

export const NetlifyCmsEditorComponentCaption = caption;
export default caption;

And I have it referenced in my cms.js

/* Data from Netlify.com */
import CMS from 'netlify-cms-app'

//Editor Components
import caption from './editor-components/caption'

/* This registers templates with the corresponding
  data from netlify config file */
CMS.registerPreviewTemplate('news', NewsPostPreview)

//Register Editor Components
CMS.registerEditorComponent(caption)

It works correctly in the CMS

And shows up in my preview

But does not on my actual site

I am basing this editor component off the image example provided by netlify cms
netlify-cms-editor-component-image

But I believe there is something else going on that takes the toBlock line and implements the correct <img> output.

There is not a good example in the docs nor anything on stack overflow, github, or anywhere else I check. I really want to know how editor components actually output the correct code for the actual site. Can anyone explain this?

I figured this out and ended up with a different caption.js file.

I think the issue came down to matching field with the right regexp. But it is also possible how I named the editor component might have had a conflict with my fields so I changed those just in case.

Here is the new caption.js file

const imgcaption = {
  // Visible label
  label: 'Image Caption',
  // Internal id of the component
  id: 'imagecaption',
  // Fields the user need to fill out when adding an instance of the component
  fields: [
    {
      name: 'content',
      label: 'Caption',
      widget: 'string',
    },
  ],
  // Pattern to identify a block as being an instance of this component
  pattern: /^<span>(.*)<\/span>*$/,
  // Function to extract data elements from the regexp match
  fromBlock: function(match) {
    return {
      content: match[1],
    }
  },
  // Function to create a text block from an instance of this component
  toBlock: function(obj) {
    return `<span>${obj.content}</span>`;
  },
  // Preview output for this component. Can either be a string or a React component
  // (component gives better render performance)
  toPreview: function(obj) {
    return `<span>${obj.content}</span>`;
  }
}

export default imgcaption;

I did not realize I needed () around each data element in my pattern. At least I think this is how it works.

So pattern: /^<span>(.*)<\/span>*$/, has 1 data element I need to match to my fields. Which is represented at match[1]

In order to match the field named content to that data element, I have do it in the fromBlock function.

fromBlock: function(match) {
    return {
      content: match[1],
    }
  },

Here is an example with multiple data elements.

fields: [
    { name: "id", label: "Youtube Video ID", widget: "string" },
    { name: "width", label: "Width", widget: "string" },
    { name: "height", label: "Height", widget: "string" },
    { name: "fullScreen", label: "Allow Full Screen", widget: "boolean" },
  ],

pattern: /^<iframe width="(.*)" height="(.*)" src="https:\/\/www.youtube.com\/embed\/(\S+)" frameborder="0" (allowfullscreen)?><\/iframe>$/,

You can see there are 4 sets of () in the pattern. So they are matched up like this.

fromBlock: function (match) {
    return {
      width: match[1],
      height: match[2],
      id: match[3],
      fullScreen: match[4],
    };
  },

I hope this helps. Certainly glad to have got an editor component working on my project.

3 Likes