In this tutorial, I’ll cover how you can create a content generator extension for WordPress Gutenberg. With this extension plugin, we will be able to generate random “lorem ipsum” content within Gutenberg editor. Dummy content can be very useful in some cases.
What are we going to learn?
I think it’s always better to know what benefit a tutorial will provide (in terms of knowledge) before following it. After covering this tutorial, you’ll have some basic understanding of the following:
- Gutenberg Block filters: Some basic understanding of gutenberg block filters
- Gutenberg Block Transformation API: Introduction to gutenberg block transformation API.
- Regular Expressions: Introduction to regular expressions.
Overview
When following a tutorial, it’s better to know upfront what to expect at the end.
The goal is for a user to insert a command in a paragraph block, and our plugin should transform that to dummy content based on the command.
Let’s review all commands we’re going to create in this tutorial
Command 1: lorem<length>
A simple command that will transform the paragraph block into lorem ipsum dummy content according to the given length.
Command 2: loremlist<list-length>
A simple command that will generate lists based on the given length, containing dummy content with 50 characters each.
Command 3: loremlist<list-length>-<character-length>
Command extending the previous command, but also including each list character length.
User interaction
Planning user interaction is the most crucial part when you’re developing something, the same goes for Gutenberg block. Here’s how the user should generate dummy content using our plugin.
user inserts a paragraph block
↓
user types a command and hit enter
↓
paragraph block transforms into dummy content based on the command
Getting Started
In order to get started, use this starter template “CakeWP/gutenberg-extension-starter-template” for quickly creating Gutenberg based plugins.
Introduction to gutenberg block transform API
Before we proceed with this tutorial, It’s important for you to have some basic knowledge about Gutenberg block transform API.
You can read more about Gutenberg block transform API here.
We’re not going to cover the API in-depth in this tutorial, Let’s leave that for future tutorials.
Basically, The Block Transforms API allows us to transform blocks from one block to another. It also transforms blocks from different entities, such as shortcodes, files, regular expressions, and raw DOM nodes.
There are total of 6 types of transformations available (at the time of this tutorial).
Introduction to gutenberg block filters
We can supercharge/extend Gutenberg blocks with Gutenberg block filters. Gutenberg Block filters basically allow us to add more functionalities to the existing Gutenberg block.
You can read more about Gutenberg block filters here.
There are different filters available to extend Gutenberg blocks. Here is a list of some available block filters.
blocks.getSaveContent.extraProps
: Allows you to add additional attributes to a block frontend wrapper element.blocks.getBlockDefaultClassName
: Allows you to add a default classname for a certain block.editor.BlockEdit
: Allows you to modify a certain block edit component.blocks.registerBlockType
: Allows you to modify block settings at the time of registeration.
Creating utility methods
We need some utility methods that will help us generate random dummy content, For the sake of simplicity, we are not going to call an external API in this tutorial. Instead, we will generate dummy content based on random words.
Let’s create a directory named “utils” that’ll store our dummy words in JSON format, as well as some utility functions.
Download the following words.json in the utils directory. It contains a bunch of random words.
Now, let’s create some utility methods, that will use this JSON file to generate dummy content. Create “functions.js” in utils directory
// src/utils/functions.js
import words from "../words.json";
export const generateRandomContent = (length) => {
let content = "";
while (content.length < length) {
let randomWords = words[Math.floor(Math.random() * words.length)];
content += ` ${randomWords}`;
}
return content.trim().substring(0, length);
};
export const generateRandomLists = (listItemLength, characterLength = 50) => {
let lists = [];
// for each list.
for (let i = 0; i < listItemLength; i++) {
const listContent =
"<li>" + generateRandomContent(characterLength) + "</li>";
lists.push(listContent);
}
return lists.join(" ");
};
So far, we have 2 simple utility methods. Let me explain what each one does:
generateRandomContent
(length)
: Generates random content based on the argument length.generateRandomLists
(listItemLength,
characterLength
=
: Generates a dummy list content based on 2 arguments. Here’s what each argument does.50
)listItemLength
: Specifies length of list items needed.
(optional): Specifies length of dummy content required in each list item. Default to 50 characters if not provided.characterLength
Extending gutenberg core paragraph block
Now that we’ve some basic knowledge covered about Gutenberg block transformation API and Gutenberg block filters.
We need to extend the Gutenberg core paragraph to include a new transformation method. We’re going to use a block filter blocks.registerBlockType
to extend the Gutenberg paragraph block.
// src/index.js
import { addFilter } from "@wordpress/hooks";
Gutenberg “addFilter” accepts 3 arguments, i.e hook name, namespace, callback, and priority (optional).
// src/index.js
addFilter(
"blocks.registerBlockType",
"gutenberghub/content-generator",
(settings, blockName) => {
if ("core/paragraph" !== blockName) {
return settings;
}
// extend settings for paragraph block here.
return settings;
}
);
We need to also ensure that all other block setting still persists after our filter.
The filter above will be applied to all Gutenberg blocks. Therefore we need to narrow down with an additional condition that ensures that our filter only applies to the Gutenberg paragraph block.
As we’ve set up a basic filter that can manipulate Gutenberg block settings. It’s time to create all commands.
Command 1: lorem<length>
Let’s work for the first and most basic command lorem<length>
.
Creating the regex
we need a regex that matches the lorem<length>
command. We need to capture both in separate groups.
Here’s what the final regex looks like:
/^(lorem\s?)(\d+)$/i
Here’s a visual explanation of the regex above.
Adding transformation
Let’s now add a new transformation method to the paragraph block.
// src/index.js
addFilter(
"blocks.registerBlockType",
"gutenberghub/content-generator",
(settings, blockName) => {
if ("core/paragraph" !== blockName) {
return settings;
}
return {
...settings,
transforms: {
...settings.transforms,
from: [
...settings.transforms.from,
{
type: "enter",
regExp: /^(lorem\s?)(\d+)$/i,
transform: ({ content }) => {
// obtaining required content length from 2nd captured group
const requiredContentLength = Number(
content.match(/^(lorem\s?)(\d+)$/i)[2]
);
// using our utility method.
const generatedContent = generateRandomContent(
requiredContentLength
);
return createBlock("core/paragraph", {
content: generatedContent,
});
},
},
],
},
};
}
);
Command 2: loremlist<list-length>
Now, let’s work on creating our second command loremlist<list-length>
.
Creating the regex
Like the previous command, we need a regex that matches this command in 2 separate groups
Here’s what the final regex looks like:
/^(loremlist\s?)(\d+)$/i
Here’s a visual explanation of the regex above.
Adding transformation
Let’s add another transformation method for this command in our block filter.
// src/index.js
addFilter(
"blocks.registerBlockType",
"gutenberghub/content-generator",
(settings, blockName) => {
if ("core/paragraph" !== blockName) {
return settings;
}
return {
...settings,
transforms: {
...settings.transforms,
from: [
...settings.transforms.from,
{
type: "enter",
regExp: /^(lorem\s?)(\d+)$/i,
transform: ({ content }) => {
// obtaining required content length from 2nd captured group
const requiredContentLength = Number(
content.match(/^(lorem\s?)(\d+)$/i)[2]
);
// using our utility method.
const generatedContent = generateRandomContent(
requiredContentLength
);
return createBlock("core/paragraph", {
content: generatedContent,
});
},
},
// loremlist transformation
{
type: "enter",
regExp: /^(loremlist\s?)(\d+)$/i,
transform: ({ content }) => {
const listItemLength = Number(
content.match(/^(loremlist\s?)(\d+)$/i)[2]
);
const generatedContent = generateRandomLists(listItemLength);
return createBlock("core/list", {
values: generatedContent,
});
},
},
],
},
};
}
);
Command 3: loremlist<list-length>-<character-length>
<character-length>
This command is basically an extended version of the previous command. For this particular command, we need to capture 4 separate regex groups.
Creating the regex
We need a regex that matches 4 separate groups
Here’s what final regex looks like
/^(loremlist\s?)(\d+)(-)(\d+)$/i
Here’s a visual explanation of the regex above.
Adding transformation
Now, adding another transformation method for this particular command
addFilter(
"blocks.registerBlockType",
"gutenberghub/content-generator",
(settings, blockName) => {
if ("core/paragraph" !== blockName) {
return settings;
}
return {
...settings,
transforms: {
...settings.transforms,
from: [
...settings.transforms.from,
{
type: "enter",
regExp: /^(lorem\s?)(\d+)$/i,
transform: ({ content }) => {
// obtaining required content length from 2nd captured group
const requiredContentLength = Number(
content.match(/^(lorem\s?)(\d+)$/i)[2]
);
// using our utility method.
const generatedContent = generateRandomContent(
requiredContentLength
);
return createBlock("core/paragraph", {
content: generatedContent,
});
},
},
{
type: "enter",
regExp: /^(loremlist\s?)(\d+)$/i,
transform: ({ content }) => {
const listItemLength = Number(
content.match(/^(loremlist\s?)(\d+)$/i)[2]
);
const generatedContent = generateRandomLists(listItemLength);
return createBlock("core/list", {
values: generatedContent,
});
},
},
// loremlist extended transformation
{
type: "enter",
regExp: /^(loremlist\s?)(\d+)(-)(\d+)$/i,
transform: ({ content }) => {
const matches = content.match(/^(loremlist\s?)(\d+)(-)(\d+)$/i);
const contentLength = Number(matches[4]);
const listItemLength = Number(matches[2]);
const generatedContent = generateRandomLists(
listItemLength,
contentLength
);
return createBlock("core/list", {
values: generatedContent,
});
},
},
],
},
};
}
);
Let’s wrap up this tutorial. If you’ve any questions, Feel free to comment below.
Leave a Reply