Create a custom element in EditorJs - reactjs

I added EditorJs plugin in my react js application:
import ReactDOM from "react-dom";
import React, { Component } from "react";
import EditorJs from "react-editor-js";
import { EDITOR_JS_TOOLS } from "./constants";
class ReactEditor extends Component {
render() {
return (
<EditorJs
tools={EDITOR_JS_TOOLS}
data={{
blocks: [
{
type: "header",
data: {
text: "Editor.js",
level: 2
}
},
{
type: "paragraph",
data: {
}
},
{
type: "header",
data: {
text: "Key features",
level: 3
}
},
{
type: "list",
data: {
style: "unordered",
items: [
"It is a block-styled editor",
"It returns clean data output in JSON",
"Designed to be extendable and pluggable with a simple API"
]
}
},
{
type: "header",
data: {
text: "What does it mean «block-styled editor»",
level: 3
}
},
{
type: "paragraph",
data: {
text:
'Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js <mark class="cdx-marker">workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc</mark>. Each of them is an independent contenteditable element (or more complex structure) provided by Plugin and united by Editor\'s Core.'
}
},
{
type: "paragraph",
data: {
text:
'There are dozens of ready-to-use Blocks and the simple API for creation any Block you need. For example, you can implement Blocks for Tweets, Instagram posts, surveys and polls, CTA-buttons and even games.'
}
},
{
type: "header",
data: {
text: "What does it mean clean data output",
level: 3
}
},
{
type: "paragraph",
data: {
text:
"Classic WYSIWYG-editors produce raw HTML-markup with both content data and content appearance. On the contrary, Editor.js outputs JSON object with data of each Block. You can see an example below"
}
},
{
type: "paragraph",
data: {
text:
'Given data can be used as you want: render with HTML for <code class="inline-code">Web clients</code>, render natively for <code class="inline-code">mobile apps</code>, create markup for <code class="inline-code">Facebook Instant Articles</code> or <code class="inline-code">Google AMP</code>, generate an <code class="inline-code">audio version</code> and so on.'
}
},
{
type: "paragraph",
data: {
text:
"Clean data is useful to sanitize, validate and process on the backend."
}
},
{
type: "delimiter",
data: {}
},
{
type: "paragraph",
data: {
text:
"We have been working on this project more than three years. Several large media projects help us to test and debug the Editor, to make it's core more stable. At the same time we significantly improved the API. Now, it can be used to create any plugin for any task. Hope you enjoy. 😏"
}
},
{
type: "image",
data: {
file: {
url:
"https://codex.so/upload/redactor_images/o_e48549d1855c7fc1807308dd14990126.jpg"
},
caption: "",
withBorder: true,
stretched: false,
withBackground: false
}
}
],
version: "2.12.4"
}}
/>
);
}
}
ReactDOM.render(<ReactEditor />, document.getElementById("root"));
According to the documentation i can create a custom element:
render() {
return (
<EditorJs holder="custom">
<div id="custom" />
</EditorJs>
);
}
Question: I want to add as a custom element an input: <input type="text"/>, but i don't manage even if i do:
<EditorJs holder="custom">
<input id="custom" type="text"/>
</EditorJs>
Who knows how to add this custom element in the plugin above?
demo: https://codesandbox.io/embed/react-editor-js-23opz

I found in the documentation that i can create a plugin for editor.js:
https://editorjs.io/the-first-plugin. One of example looks like this:
class SimpleImage {
static get toolbox() {
return {
title: 'Image',
icon: '<svg width="17" height="15" viewBox="0 0 336 276" xmlns="http://www.w3.org/2000/svg"><path d="M291 150V79c0-19-15-34-34-34H79c-19 0-34 15-34 34v42l67-44 81 72 56-29 42 30zm0 52l-43-30-56 30-81-67-66 39v23c0 19 15 34 34 34h178c17 0 31-13 34-29zM79 0h178c44 0 79 35 79 79v118c0 44-35 79-79 79H79c-44 0-79-35-79-79V79C0 35 35 0 79 0z"/></svg>'
};
}
render() {
return document.createElement('input');
}
save(blockContent) {
return {
url: blockContent.value
}
}
}

Related

Unable to add code blocks in Sanity CMS after I install the code-input plugin

I am learning to build a blog using Sanity CMS and React. I am new to Sanity.
I should be able to insert code snippets in my blog posts. So, I have installed the code-input plugin.
According to the link here, after I install the plugin I have to use the following code in my schema types.
I have no idea where do I insert the code.
Please help.
My folder structure is as follows:
sanityblog/blockContent.js
/**
* 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" },
],
// 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 },
},
],
};
sanityblog/schema.js
// First, we must import the schema creator
import createSchema from "part:#sanity/base/schema-creator";
// Then import schema types from any plugins that might expose them
import schemaTypes from "all:part:#sanity/base/schema-type";
// We import object and document schemas
import blockContent from "./blockContent";
import category from "./category";
import post from "./post";
import author from "./author";
// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
// We name our schema
name: "default",
// Then proceed to concatenate our document type
// to the ones provided by any plugins that are installed
types: schemaTypes.concat([
// The following are document types which will appear
// in the studio.
post,
author,
category,
// When added to this list, object types can be used as
// { type: 'typename' } in other document schemas
blockContent,
]),
});
If you installed the plugin correctly, it's now available as a schema type to be used in any of your other schemas. So, to answer your question, you can put that code anywhere you want code blocks in your Sanity studio. I'd strongly suggest going over the content modelling documentation 😉
Specifically to your question, assuming you use the sanityBlog/blockContent.js field for the content of your posts, you can add it there. Here's how that would look like:
export default {
title: "Block Content",
name: "blockContent",
type: "array",
of: [
{
title: "Block",
type: "block",
// ...annotations, styles, lists and marks you already have
},
{
type: "image",
options: { hotspot: true },
},
// Add the code block here 👇
// it'll show up as one of the blocks available in your
// Portable Text Editor
{
type: "code",
title: "Code block",
}
],
};
For specifics on the portable text / rich content field (type: "block"), refer to the block type documentation. If you want to take it one step back, refer to the general block content documentation.
Hope this helps 🙌

React - setState with certain index

I've been stuck for whole day and please help me to fix it.
I have a json data which like this :
[
{
"menu": "menu_1",
"icon": "icon_1",
"detail": {
"name": "name_1",
"phone": "phone_1"
}
},
{
"menu": "menu_2",
"icon": "icon_2",
"detail": {
"name": "name_2",
"phone": "phone_2"
}
},
{
"menu": "menu_3",
"icon": "icon_3",
"detail": {
"name": "name_3",
"phone": "phone_3"
}
}
]
I put them into the "data" state and My goal is I wanna change the "detail" state with certain index ( ex: state "data" with index 1 change the "detail" data )
Currently my code is :
this.setState({
data: {
...this.state.data,
detail:{
this.state.data[1].detail:{
"name": "billy",
"phone": "893823839"
}
}
}
})
That setState is clearly wanna change the state with certain index but fail..
How do I supposed to do?
I guess this is what you're looking for, we could replace an element inside an array using splice :
const index = 1;
this.setState({
data: [...this.state.data].splice(index, 1, {
...this.state.data[index],
details: { name: "billy", phone: "893823839" },
}),
});
Update: we could use slice also to make an immutable update with index :
this.setState({
data: [
...this.state.data.slice(0, index),
{
...this.state.data[index],
details: { name: "billy", phone: "893823839" },
},
...this.state.data.slice(index + 1, this.state.data.length),
],
});
could you try it ?
this is an example that i tested using splice:
const items = [{ id: 1 }, { id: 2 }, { id: 3 }];
const indexToBeModified = 1; // { id: 2 } ==> { foo: "foo", id: 2 }
items.splice(indexToBeModified, 1, { ...items[indexToBeModified], foo: "foo" });
console.log("items", items);
Here is a little modified example. It uses prevState to prevent any unwanted changes that may happen when directly interacting with this.state.
import React, { Component } from "react";
export default class App extends Component {
constructor() {
super();
this.state = {
data: [
{
menu: "menu_1",
icon: "icon_1",
detail: {
name: "name_1",
phone: "phone_1"
}
},
{
menu: "menu_2",
icon: "icon_2",
detail: {
name: "name_2",
phone: "phone_2"
}
},
{
menu: "menu_3",
icon: "icon_3",
detail: {
name: "name_3",
phone: "phone_3"
}
}
]
};
this.modifyData = this.modifyData.bind(this);
}
modifyData(index) {
this.setState((prevState) => {
prevState.data[index].detail={
name: "billy",
phone: "893823839"
};
return {
data: [prevState.data]
};
},()=>{console.log(this.state.data)});
}
render() {
return (
<button onClick={() => this.modifyData(0)}>Click to modify data</button>
);
}
}
Here is a code sandbox reference.

Apollo Client - How to test a component that uses multiple queries, using HOC components that use compose

I am reading over the docs for testing React/Apollo components Link. If the component has one query, it seems pretty simple to test it.
const mocks = [
{
request: {
query: GET_DOG_QUERY,
variables: {
name: 'Buck',
},
},
result: {
data: {
dog: { id: '1', name: 'Buck', breed: 'bulldog' },
},
},
},
];
it('renders without error', () => {
renderer.create(
<MockedProvider mocks={mocks} addTypename={false}>
<Dog name="Buck" />
</MockedProvider>,
);
});
My component is a little different than the one provided in the documentation.
It doesn't use the useQuery hook, instead I am opting for the HOC approach as outlined here.
I have two queries that my function uses, and so I use two graphql functions and combine them together using compose, as recommended in the docs.
My component is exported like this:
export default compose(withQueryA, withQueryB)(MyComponent);
const withQueryA = graphql(QUERY_A, {
name: "QueryA",
options: (props) => ({
variables: {
foo: props.foo,
},
}),
});
const withQueryB = graphql(QUERY_B, {
name: "QueryB ",
options: (props) => ({
variables: {
bar: props.bar,
},
}),
});
What I'm trying to do is provide the mocks object with multiple objects, each containing a request/result for the corresponding query. I just wanted to know if anyone has been testing their components in a similar way or if there is a better suggestion.
const mocks = [
{
request: {
query: QUERY_A,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
{
request: {
query: QUERY_B,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
];
I'm also confused about what to put in the result object. When I console.log what is actually returned to the component when making a query in production, it has the data plus error, fetchMore, loading, networkStatus. Do I have to put all those things in the mocks as well?
My feeling was correct. The result object should look something like this:
const mocks = [
{
request: {
query: QUERY_A,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
{
request: {
query: QUERY_B,
variables: {
foo: "bar",
},
},
result: {
data: {
...,
},
},
},
];

Highcharts windbarb out-of-memory crash

I'm trying to use the modules windbarb in a react project.
On render, chrome pauses the process with a "paused before potential out-of-memory crash" highlighting line 135 in the windbarb.js file.
this is my code:
import React, { Component } from 'react';
import Highcharts from 'highcharts'
import HighchartsReact from 'highcharts-react-official'
import WindBarbs from 'highcharts/modules/windbarb'
WindBarbs(Highcharts)
class Test extends Component {
constructor(props) {
super(props)
this.state = {
lineData: [
[1569243600000, 12.5],
[1569247200000, 12.2]
],
windData: [
[1569243600000, 29.6],
[1569247200000, 26.5]
],
highcharts: Highcharts
}
}
render(){
let options = {
series: [{
name: 'Temperature',
type: 'line',
data: this.state.lineData
}, {
name: 'Wind direction',
type: 'windbarb',
data: this.state.windData
}]
}
return(
<HighchartsReact
highcharts={this.state.highcharts}
options={options}
/>
)
}
}
export default Test
That problem is related to your data. First elements from the data array by default are used as values for windbard series which causes very complex svg rendering. As a solution you can use keys option.
series: [{
type: 'line',
data: [
[1569243600000, 12.5],
[1569247200000, 12.2]
]
}, {
type: 'windbarb',
keys: ['x', 'value'],
data: [
[1569243600000, 29.6],
[1569247200000, 26.5]
]
}]
Live demo: https://jsfiddle.net/BlackLabel/0k8vmzfd/
API Reference:
https://api.highcharts.com/highcharts/series.windbarb.data
https://api.highcharts.com/highcharts/series.windbarb.keys

React Axios Get Call to Output JSON Format

I am performing an Axios get call in a React Component to retrieve JSON info. That function is working great. Within the JSON is a label for various network ports, which are returning as an array in my axios call. These are ultimately going to be displayed as nodes on a d3 graph. My issue is that I need to output the data pulled from the get call into the following format:
nodes: [
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' }
]
So the full component for the graph to read is:
export const data = {
nodes: [
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' }
]
}
Here is the format of the Axios get I am using:
axios.get(`NetworkConstruct.json`)
.then(res => {
const names = res.data.items;
this.setState({ names });
});
Here is a sample output I am receiving (there are 11 of these):
{id: "5bc0860c-ece1-461c-bac0-b155a3cacd82", label: "80.107.0.212",
resourceTypeId: "tosca.resourceTypes.NetworkConstruct", productId:
"5bc0835c-6cfa-486e-8429-a59eaf4118bc", tenantId: "393fa8da-61fd-458c-80f9-
ce92d0ef0330", …}
The data has to be in this EXACT format or the graph won't read it. I'm guessing I'll need to do an initial map function but am stuck on how to arrange it. I cannot have any divs or quotes in my output. Is this doable? I have scoured the boards and Google for a couple of days and can't make this work yet.
Here is the object I am receiving from the GET request.
{
"id": "5bd2c6ef-6009-4b90-9156-62168f3c6293",
"resourceId": "5bd0ba82-2994-455d-8716-2adb5694d6f0",
"interface": "getGraph",
"inputs": {},
"outputs": {
"graph": {
"nodes": [
{
"id": "5bcdf06c-dd53-4335-840f-55a4b8d85a2d",
"name": "asw-lab9306b",
"ports": {
"GigabitEthernet3/0/8": "5bd1777f-0ab9-4552-962b-9e306ce378ab",
"GigabitEthernet2/0/15": "5bd1777e-119c-44e8-ba69-0d86a481c0f5",
"GigabitEthernet3/0/47": "5bd17783-be94-4aaf-8858-70e4eb3d02dc",
"GigabitEthernet2/0/13": "5bd17783-ed99-453f-a958-f764edaa8da8"
}
}
],
"links": [
{
"a": "5bd1a467-13f2-4294-a768-561187b278a8",
"z": "5bd17770-2e6c-4c37-93c8-44e3eb3db6dd",
"layer": "ETHERNET"
},
{
"a": "5bd1776e-c110-4086-87d6-a374ccee419a",
"z": "5bd17770-83ee-4e10-b5bb-19814f9f5dad",
"layer": "ETHERNET"
}
]
}
},
"state": "successful",
"reason": "",
"progress": [],
"providerData": {},
"createdAt": "2018-10-26T07:49:03.484Z",
"updatedAt": "2018-10-26T07:49:25.425Z",
"resourceStateConstraints": {},
"executionGroup": "lifecycle"
}
The info I need is the nodes ID. There are eleven of them in the full object.
You can map an array of objects to another array of objects in your format with Array.prototype.map(). Assuming that data is the list of objects from your response:
class Graph extends React.Component {
state = {
nodes: null,
};
componentDidMount() {
axios.get('the url').then(response => {
const nodes = response.data.outputs.graph.nodes;
this.setState({nodes});
});
}
render() {
const {nodes} = this.state;
if (!nodes) return 'Loading...'
return <TheD3ComponentYouUse nodes={nodes} />;
}
}

Resources