Hi,
I’m trying to create a custom markdown widget but have encountered multiple issues.
Aim
I wanted to be able to create a custom editorwidget that would output the following into the markdown.
output mdx
# Header
some markdown text
<MyWidget data='{}'>
some children elements
</MyWidget>
However, i can not get my regular expression that detects this pattern in my test to work for multiple return lines, especially when used in conjunction with multiple custom widgets. It can trick you into thinking its working, but its not. So when testing you have to have a bunch of widgets on the page to capture the weird behaviour.
Current Solution
First off, I can get the custom widgets to work with everything on one line.
<MyWidget data='{}'>some children elements</MyWidget>
Though note that following will break. So must have line between.
<MyWidget data='{}'>some children elements</MyWidget>
<MyWidget data='{}'>some children elements</MyWidget>
My test looks something like the following. Notice I have MySecond widget before and after the MyWidget. This is because i noticed in some scenarios, either MyWidget or MySecondWidget would not be parsed by the markdown editor. I found out eventually that if a widget fails (with a sent invalid data to remark error) it would break the other widget. I also discovered that this would depend on the order the widgets were registered with the CMS.
<MySecondWidget></MySecondWidget>
<MyWidget data='{}'>some children elements</MyWidget>
<MyWidget data='{}'>some children elements</MyWidget>
<MySecondWidget></MySecondWidget>
This however goes to hell
So the above works when my regex doesn’t have any modifers g|m|s and is on a single line.
However it all goes to hell when you have the following layout, and use //gms in the pattern.
<MySecondWidget></MySecondWidget>
<MyWidget data='{}'>
some children elements
</MyWidget>
<MyWidget data='{}'>
some children elements
</MyWidget>
<MySecondWidget>
some text.
</MySecondWidget>
You will essentially get the following errors from one of the components, and subsequent components will be ignored. I.e. the editor will just render the markdown and not display the input form.
"Sent invalid data to remark"
"Error: Incorrectly eaten value: please report this warning
It basically errors out when you add the m modifier to the regex. (which you kinda need i think to support multi line layout) I’m not a reg ex expert, but it seems that way.
My RegEx
I’ve currently got two regex patterns i’m testing, (see full example code below)
//pattern: /(?:^<AR data=')(.[^']+)(?:.*?>)(.*?)(?:<\/AR>$)/m,
pattern: /^<AR.*?\/AR>$/,
I’ve tried various combinations of regEx paramters, g,m and s.
pattern: /^<AR.*?\/AR>$/g,
pattern: /^<AR.*?\/AR>$/gs,
pattern: /^<AR.*?\/AR>$/gsm, << this one with 'm' definately breaks things.
Issues
Issue I encountered that i beleive are related to trying to support multiline
- When component is on anything but the first element in the markdown block it logs a “Sent invalid data to remark” (warning). Custom widget: Incorrectly eaten value bug · Issue #4669 · netlify/netlify-cms · GitHub
- When attempting to add multiple custom components to the page, they somehow interfere with each other. I expect this is to do with the pattern matching.
HeroEditoComponent.tsx
import React from ‘react’;
export default {
// Internal id of the component
id: "HeroBlockmdx",
// Visible label
label: "Hero Block",
// Fields the user need to fill out when adding an instance of the component
fields: [
{name: 'id', label: 'Asset ID', widget: 'string'},
{name: 'type', label: 'Asset Type', widget: 'string'}],
// Pattern to identify a block as being an instance of this component
// (?:^ the ^ was suggested to help with matching multiple components on the page.
// ...(.?)... Lazy match child elements of the block.
// ms not using g i think helped correctly match when multiple components on the page.
//pattern: /(?:^<HeroBlock data=')(.[^']+)(?:.?>)(.*?)(?:<\/HeroBlock>$)/,
pattern: /^<HeroBlock.*?\/HeroBlock>$/,
// Function to extract data elements from the regexp match
fromBlock: function (match) {
let data = match[0];
console.log("fromBlock(match) : " + data);
const r = RegExp(/(?:^<HeroBlock data=')(.[^']+)(?:.?>)(.*?)(?:<\/HeroBlock>$)/gms)
data = r.exec(data);
data = data[1];
data = JSON.parse((decodeURI(data)));
console.log("fromBlock(data) : " + JSON.stringify(data));
// Some validation.
if (data == null) {
data = {id: "none", type: "none"}
} else {
!data.hasOwnProperty("id") ? data.id = "none" : null;
!data.hasOwnProperty("type") ? data.type = "none" : null;
}
console.log("fromBlock() data validated : " + JSON.stringify(data));
return data;
},
// Function to create a text block from an instance of this component
toBlock: function (obj) {
const data = JSON.stringify({
id: obj.id || "none",
type: obj.type || "none",
});
console.log("toBlock() Data:" + JSON.stringify(obj));
//return '<div></div>'
return `<HeroBlock data='${encodeURI(data)}'>sdf</HeroBlock>`
},
// Preview output for this component. Can either be a string or a React component
// (component gives better render performance)
toPreview: function (obj) {
console.log("toPreview: "+JSON.stringify(obj));
return (
// <HeroBlock data={obj}></HeroBlock>
<div>{JSON.stringify(obj)}</div>
);
}
}
So if someone has a working example with multi lines support that would be great. I should note i’ve seen several examples on the bug portal. none of with really worked for me.