This is the second part of an article in a series on creating WordPress Gutenberg blocks. Here is part 1.  If you want to pick up where we are, here is the repository.

We left off last time with a Gutenberg hero image block that had centered text over an image.  We added the ability to edit that text and change the text color.  It started to get a little long, so the homework I left off with was to set up the overlay so that it would also be able to be changed with a color palette option in InspectorControls.  We still have yet to use the wonderful MediaUpload component.

The repo for this in all its finished glory is here if you would desire it.

Let’s do this.

How I hooked up the ColorPalette to our transparent overlay

There are many ways to skin a cat. Here is the route I chose: I will apply an opacity to our to our overlay through the css, but since the color of the background will be determined by the content editor I’ll apply that color inline.

.wp-block-firstgutyblocks-hero-image .overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /* background: rgba(150,0,0,.3); <- old*/
    opacity: .3;
}

In our index.js, I will be adding an overlayColor attribute and add that as an inline style on my overlay div:

...
attributes: {
    textString: {
        type: 'array',
        source: 'children',
        selector: 'h2',
    },
    fontColor: {
        type: 'string',
        default: 'black'
    },
    overlayColor: { // new!
        type: 'string',
        default: 'orange'
    }
},

...

edit(props) {

    const { 
        setAttributes, 
        attributes,
        className,
        focus
    } = props;
    const { fontColor, overlayColor } = props.attributes;

...
<div 
    className={className}
    style={{
        backgroundImage: `url('http://placehold.it/1440x700')`,
        backgroundSize: 'cover',
        backgroundPosition: 'center'
    }}>
    <div 
        className="overlay"
        style={{ background: overlayColor }} {/*NEW!*/}
        ></div>
    <RichText
        tagName="h2"
        className="content"
        value={attributes.textString}
        onChange={onTextChange}
        placeholder="Enter your text here!"
        style={{color: fontColor}}
        />
</div>

Orange is a temporary choice just to make sure it’s working.  And tada!  Here we have it if we reload the editor!

Hero image block now displaying a semi-transparent orange overlay.

Okay, so now let’s add another ColorPalette component in the InspectorControls and hook it up so the user could change the color:

...
// Adding an attribute handler
function onOverlayColorChange(changes) {
    setAttributes({
        overlayColor: changes
    })
}

...

//Adding another control to the InspectorControls
<InspectorControls>
    <div>
        <strong>Select a font color:</strong>
        <ColorPalette
            value={fontColor}
            onChange={onTextColorChange}
            />
    </div>
    <div>
        <strong>Select an overlay color:</strong>
        <ColorPalette
            value={overlayColor}
            onChange={onOverlayColorChange}
            />
    </div>
</InspectorControls>,

An we now can edit text, edit the text color, AND change the overlay color.

Spiffy

Uploading a background image using MediaUpload

It’s super nice that the gutenberg editor gives us a bunch of components through wp.block that we can use.  Another component that is super nice is MediaUpload that does a lot of work for us.

MediaUpload is a component that uses what’s called render props.  You don’t have to go into detail about what those are, just realize they’re a thing and they’re very similar to Higher Order Components.  (And If you want to see a really great talk about why these things exist and how React has evolved with these sorts of things, here’s a really great talk on it.)

ANYWAY!  Let’s pull in the Media Upload, and create a new attribute that will hold our picture’s url.

const { 
    registerBlockType,
} = wp.blocks;

{
    RichText,
    InspectorControls,
    ColorPalette,
    MediaUpload  // Thanks wp.editor!
} = wp.editor;

...

attributes: {
    textString: {
        type: 'array',
        source: 'children',
        selector: 'h2',
    },
    fontColor: {
        type: 'string',
        default: 'black'
    },
    overlayColor: {
        type: 'string',
        default: null // let's get rid of the annoying orange
    },
    backgroundImage: {
        type: 'string',
        default: null, // no image by default!
    }
},

Now, let’s place our MediaUpload component:

<InspectorControls>
    <div>
        <strong>Select a font color:</strong>
        <ColorPalette
            value={fontColor}
            onChange={onTextColorChange}
        />
    </div>
    <div>
        <strong>Select an overlay color:</strong>
        <ColorPalette
            value={overlayColor}
            onChange={onOverlayColorChange}
        />
    </div>
    <div>
        <strong>Select a background image:</strong>
        <MediaUpload
            onSelect={(imageObject) => console.log(imageObject)}
            type="image"
            value={backgroundImage} // make sure you destructured backgroundImage from props.attributes!
            render={({ open }) => (
                <button onClick={open}>
                    Upload Image!
                </button>
            )}
        />
    </div>
</InspectorControls>,

A few things to note here:

  • The “onSelect” attribute is where we will eventually handle the new picture and save it’s url to “backgroundImage”.  For now, we will console.log this thing to see what MediaUpload gives us after we select an image
  • The most confusing part is the render attribute.  Basically whatever is returned inside the parentheses (in this case the <button> markup) will be shown in the editor.  MediaUpload doesn’t add anything more, but it does let you have that “open” function.  Whatever element is bound to that with onClick will be hooked up to open the MediaUpload library.

So if all goes well you can load the block, click the button, upload and select and image, and then check the console…

Editor showing our gutenberg block and a console showing the image object we console logged

MediaUpload wraps our button and allows us to make a button that opens up our media library.  Once we select an image, MediaUpload returns that image object.  For now, we set that to simply console.log the object.

MediaUpload and the object it returns

If you look closely, you notice that there’s a “sizes” property on this object?  inside of that we have lots of things that are served up by WordPress…

WordPress does it’s thing and resizes your image with different dimensions it would ordinarily use with srcset.  In this case, my picture was very large so I get a full, large, medium, and thumbnail.  In our case, I’m going to use “full” since these images are supposed to be large, detailed, full resolution things.  There’s room for optimizing and file size later!

So here’s the point, we need to create a function that will handle this object and setAttributes:

function onImageSelect(imageObject) {
    setAttributes({
        backgroundImage: imageObject.sizes.full.url
    })
}

...

<div>
    <strong>Select a background image:</strong>
    <MediaUpload
        onSelect={onImageSelect}
        type="image"
        value={backgroundImage}
        render={({ open }) => (
            <button onClick={open}>
                Upload Image!
            </button>
        )}
    />
</div>

...
//We need to make sure our div styles use this image!
<div
    className={className}
    style={{
        backgroundImage: `url(${backgroundImage})`,
        backgroundSize: 'cover',
        backgroundPosition: 'center'
    }}
>

Now, instead of logging the image when you select it, onImageSelect will set the attribute with the url of our image.  You should see the image automatically change when you select it.

Editor showing a hero image block with image showing in background of the block.

Edit: One thing I forgot to mention

I forgot to make this explicit when I first posted this.  We need to make sure that this background image shows up when we hit update! Here is what my save() method looks like now.

save(props) {

    const { attributes, className } = props;
    const { fontColor, backgroundImage } = props.attributes;

    return (
        <div
            className={className}
            style={{
                backgroundImage: `url(${backgroundImage})`,
                backgroundSize: 'cover',
                backgroundPosition: 'center'
            }}>
            <div className="overlay"></div>
            <h2 class="content" style={{ color: fontColor }}>{attributes.textString}</h2>
        </div>
    );
}

Summary

That was a a lot, but I hope that through this process this gives you ideas how to wire up your own custom block.  In these past two posts we

  • Structured the markup of a block to create an image hero
  • Learned how to use InspectorControl, ColorPalette, and MediaUpload
  • Used both css and inline styles

Thoughts? Comments?  Please post below or hit me up on twitter: @jschof

This is the 4th article in a series on creating WordPress Gutenberg blocks.  You may want to check out there other articles here, here, and here.  

Our goal here is to create a full width, customizable hero image Gutenberg block.  We aim to learn how to use the InspectorControls, ColorPalette, and MediaUpload components and use those to create a configurable block.

We’ll start off with the work from the previous article.  The completed repo for the last article is located here in the article3/styles branch.  You can pull that down to pick up right where we are now.  I have also pushed a finished hero block branch here in case you want to see it in all its glory- article4/hero-block.

Editing some of the details about our Gutenberg block first…

Let’s start by editing some of the meta information about our block.  This isn’t strictly necessary, but I’d like the title of the block to now be “Image Hero Block” so I know what it is.  I’m also changing the icon because I want it to look more like an image.

...

registerBlockType('firstgutyblocks/hero-image', {
    title: "Hero Image Block",
    icon: 'format-image',
    category: 'common',

...

Now keep in mind that since I’ve updated the string to ‘firstgutyblocks/hero-image’ I need to make sure that the php functions for our plugins are also updated.  They will be paying attention to that namespace/blockname that we have here.  So I’ve updated my firstgutyblocks.php to this:

function firstgutyblocks_hero_image_editor_assets() {
    wp_enqueue_script(
        'firstgutyblocks/hero-image',
        plugins_url( 'build/index.build.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element' )
    );
    wp_enqueue_style(
		'firstgutyblocks/hero-image-editor-style',
        plugins_url( 'src/editor.css', __FILE__ ),
        array( 'wp-edit-blocks' )
	);
};

add_action( 'enqueue_block_editor_assets', 'firstgutyblocks_hero_image_editor_assets');

function firstgutyblocks_hero_image_assets() {
    wp_enqueue_style(
		'firstgutyblocks/hero-image',
        plugins_url( 'src/view.css', __FILE__ ),
        array( 'wp-blocks' )
	);
}
add_action( 'enqueue_block_assets', 'firstgutyblocks_hero_image_assets');

Now, if you have ‘npm run watch’ running in the background, you should be able to load up the editor and see a newly-named block with that image icon in the block options list.

WordPress Gutenberg editor menu open and displaying "Hero Image Block" as an option

Now let’s make markup for our hero image block

We are going to make a block in which the user can upload an image as a background.  We’ll also let them select colors for text and for the transparent overlay.  We’ll also let them select a padding height to determine the hero height.

First, let’s clear out our editor.css and view.css files so they are empty.

Second, we’re going to update the block by doing the following in the edit method:

edit(props) {

    const { 
        setAttributes, 
        attributes,
        className
    } = props;

    function onTextChange(changes) {
        setAttributes({
            textString: changes
        });
    }

    return (
        <div className={className}>
            {/* Adding an overlay element */}
            <div className="overlay"></div>
            <RichText
                tagName="h2"
                className="content" // adding a class we can target
                value={attributes.textString}
                onChange={onTextChange}
                placeholder="Enter your text here!"
                />
        </div>
    );
},

save(props) {

    const { attributes, className } = props;

    return (
        <div className={className}>
            <div className="overlay"></div>
            {/* the class also needs to be added to the h2 for RichText */}
            <h2 class="content">{attributes.textString}</h2>
        </div>
    );
}

We already had an editable rich text field.  Now we have a div that will act as our transparent overlay, and we’ve added classes to target with our css.  Also, remember to make these changes in your save method!

If you save and refresh your editor you can see that our markup is in place.  I went to the preview just to check the markup is the way I want it.

Markup for our gutenberg block is showing in the correct spots
Beautiful

Styling the hero image block

Let’s add a background image to our ‘wp-block-firstgutyblocks-hero-image’ div.  You have choices here- you can do styles through css, or you can place styles inline in the jsx.  There are many, many fights raging over whether we should ever place styles inline.  There are some really cool projects that allow you to basically keep all styles in our javascript.  I basically ask myself “Is this a style that the user will want to edit themselves?  Or is it static?”  If it is user-changeable then I go for inline.

Hence, we will make our background image inline.  To make sure it’s working, I’m going to place an inline style.  Here is the code for my edit method:

return (
    <div 
        className={className}
        style={{
            backgroundImage: `url('http://placehold.it/1440x700')`,
            backgroundSize: 'cover',
            backgroundPosition: 'center'
        }}>
        <div className="overlay"></div> {/* Adding an overlay element */}
        <RichText
            tagName="h2"
            className="content" // adding a class we can target
            value={attributes.textString}
            onChange={onTextChange}
            placeholder="Enter your text here!"
            />
    </div>
);

!!!Make sure you do this in your save method container div as well!!! I would load the block in the editor to make sure that the background image is showing up in both the editing block and the saved block.

If you aren’t familiar with how those inline styles are happening, here’s a pretty good overview.  Basically, you create an object that looks a bit like markup, but some of the property names have to be camel-cased.  We will be using this a lot in our blocks because many things the user wants control over are styles.

But the point here is that we now have a place that javascript determines the background image.

Let’s do a few more styles to make things look a little prettier…

/* view.css */
.wp-block-firstgutyblocks-hero-image {
    padding: 4em 0;
    text-align: center;
}

Now we’re starting to see a hero block emerge:

A Gutenberg block in the editor showing the hero block styling

Just one more thing: let’s create the styles we need to get that overlay div working.

/* view.css */
.wp-block-firstgutyblocks-hero-image {
    position: relative;
    padding: 4em 0;
    text-align: center;
}

.wp-block-firstgutyblocks-hero-image .overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(150,0,0,.3);
}

.wp-block-firstgutyblocks-hero-image .content {
    position: relative;
    z-index: 1;
}

So we made the container div the position relative element for our block.  We then position the overlay absolutely on the parent and make sure it covers the entire parent.  This will give that overlay look- and for now we’re adding a random rgba color to show it’s working.

We also need to position relative the content string so that we can z-index it and lift it above the overlay.

So far so good!

The hero image block has a transparent overlay and properly positioned text.

Adding a settings panel for our block using InspectorControls

Alright, so now we’re going to let the user choose two things: the overlay color and the text color.  First, let us import a block called InspectorControls.  We’re going to add this as part of our edit method only.  The idea is that when the block is selected, these options will show on the right side in the block settings.  So let’s add the following…

const { 
    registerBlockType,
} = wp.blocks;

const {
    RichText,
    InspectorControls // New component!
} = wp.editor;

...

edit(props) {
    const { 
        setAttributes, 
        attributes,
        className,
        focus // this is "true" when the user clicks on the block
    } = props;

...

return ([
    <InspectorControls>
        This text will show when the box is selected
    </InspectorControls>,
    <div 
        className={className}
        style={{
            backgroundImage: `url('http://placehold.it/1440x700')`,
            backgroundSize: 'cover',
            backgroundPosition: 'center'
        }}>
        <div className="overlay"></div> {/* Adding an overlay element */}
        <RichText
            tagName="h2"
            className="content" // adding a class we can target
            value={attributes.textString}
            onChange={onTextChange}
            placeholder="Enter your text here!"
            />
    </div>
]);

There’s a lot going on in the return part of the code above.

  1. React can render an array of things.  It’s easiest to return an array that has the InspectorControls separate from the actual block
  2. Anything inside of the InspectorControls elements will show in the sidebar in the editor.

If you refresh the block in the editor and click on the block, in the sidebar you should see your text.

Adding actual settings to the settings panel

Whew… this is getting long.

Anyway- we have our settings panel set up.  Let’s import another pre-made color tool so we don’t have to work too much.  Let’s import ColorPalette and use it just to show what it is.

const { 
    registerBlockType,
} = wp.blocks;

const {
    RichText,
    InspectorControls,
    ColorPalette
} = wp.editor;

...

<InspectorControls>
    <div>
        <strong>Select a font color:</strong>
        <ColorPalette />
    </div>
</InspectorControls>,

We should see when we reload the editor and click on the block:

So no we actually need to wire this color palette to the block and make it change the font color.  We will first: make a fontColor attribute, second: hook up an onChange function to the colorPalette to make it change the color, and third: create an inline style for the text that is based on the fontColor attribute.

attributes: {
    textString: {
        type: 'array',
        source: 'children',
        selector: 'h2',
    },
    fontColor: { // NEW attribute!
        type: 'string',
        default: 'black'
    }
},

...

// in the edit method, we are peeling off this attribute from props
const { 
    setAttributes, 
    attributes,
    className,
    focus
} = props;
const { fontColor } = props.attributes; {/* new! */}

...

//create a handler that will set the color when you click on the ColorPalette
function onTextColorChange(changes) {
    setAttributes({
        fontColor: changes
    })
}

...

return ([
    <InspectorControls>
        <div>
            <strong>Select a font color:</strong>
            <ColorPalette
                value={fontColor} {/* new! */}
                onChange={onTextColorChange} {/* new! */}
                />
        </div>
    </InspectorControls>,
    <div 
        className={className}
        style={{
            backgroundImage: `url('http://placehold.it/1440x700')`,
            backgroundSize: 'cover',
            backgroundPosition: 'center'
        }}>
        <div className="overlay"></div>
        <RichText
            tagName="h2"
            className="content"
            value={attributes.textString}
            onChange={onTextChange}
            placeholder="Enter your text here!"
            style={{color: fontColor}}  {/* new! */}
            />
    </div>
]);

...

//Make sure to update the save method text styles:
save(props) {

    const { attributes, className } = props;
    const { fontColor } = props.attributes;

    return (
        <div 
            className={className}
            style={{
                backgroundImage: `url('http://placehold.it/1440x700')`,
                backgroundSize: 'cover',
                backgroundPosition: 'center'
            }}>
            <div className="overlay"></div>
            <h2 class="content" style={{ color:fontColor }}>{attributes.textString}</h2>
        </div>
    );
}

To be continued…

This is getting too long to keep to one post.  I will be writing a part 2 where we allow editors to pick not only the text color, but also the gradient color.  We’ll also go over how to implement a MediaUpload and display custom images.

Here’s your homework: See if you can implement a second ColorPalette that changes the transparent overlay. (You will need to create another attribute.)  This will be a good test to see if you have absorbed all that went into the hero block so far.

Edit: Part 2 is now up

This is the third part of a WordPress Gutenberg Blocks series. You can see Part 1 and part 2.  The finished repo for this tutorial is located here.

We’ve created a simple block that allows a user to edit an h2 element in a truly WYSIWYG style.  We’ve talked about how WordPress Gutenberg does attributes and how that enables us to create interactive elements.

Now, it’s time to include CSS in our block.  The Gutenberg editor allows us to enqueue two “new” styles: the editor styles and the view styles.  So there are really three stylesheets you should be thinking about when you are developing a block

editor.cssview.cssstyle.css
A css file only used in the editorA css file that is used both in the editor and in the viewThe regular styles file that is loaded in a theme for the view only
Added to WordPress using the enqueue_block_editor_assets hookAdded to WordPress using the enqueue_block_assets hookAdded either by default if you used the default style.css in the theme folder, or by using the function wp_enqueue_style in your theme folder
Is used specifically to style aspects of the block that deal with editingUsed for styling that should be present for overall layout and presentationUsed for specific theme styles and looks
Example: When you hover over a picture in the gallery while editing, a “delete this picture” button appears.  This a case where it is editor-specificExample: In a gallery you want the pictures to be laid out 3-up with a 16 px gutterExample, specific colors, fonts, heading styles, etc…

And keep in mind that the way I think about it is partially a matter of preference and there are many grey areas.  I have heard on many occasions, though that stuff in the theme are for presentation, stuff in a plugin are for functionality.  Editor.css and view.css are in the plugin, so technically they should be solely for functionality… (shrug).

Adding Gutenberg block styles through WordPress hooks

In the ‘src’ file add ‘view.css’.  Since our “My First RichText Block!” is actually an h2 element, let’s add an h2 style to see that our styles are working:

h2 {
    background: orange;
    color: white;
}

So now that we have our ugly styles, how do we let WordPress know to include them?  We return to our firstgutyblocks.php file and enqueue the file.  Add the following to the bottom of this file:

function firstgutyblocks_hello_world_assets() {
    wp_enqueue_style(
		'firstgutyblocks/hello-world',
        plugins_url( 'src/view.css', __FILE__ ),
        array( 'wp-blocks' )
	);
}
add_action( 'enqueue_block_assets', 'firstgutyblocks_hello_world_assets' );

Now you should see those styles in the editor and the view.  But yet again, there’s another problem!

Hello world block with styling that is also styling every other h2 in the Gutenberg editor...

We are affecting things outside of our block because our CSS is not targeting our block class.  So let’s talk about how to handle classes in our Gutenberg Block markup.

Controlling class names with Gutenberg Blocks

If you inspect our block as it is, there actually is a class being automatically applied to our block:

Gutenberg will automatically place ‘wp-block-{name used to register block in js}’ on the containing element.  This is all well and good if we want to target just that outer container, so let’s change our css:

.wp-block-firstgutyblocks-hello-world {
    background: orange;
    color: white;
}

Now, we should see the styles being only applied to our block.  Sweet, sweet victory.

But, let’s say, you wanted to have an h2 inside of a div, and that outer div had the classname?  We can start manually placing the classname by changing our editing and saving methods.  The key is that Gutenberg gives you the block class name in the props it send edit() and save().  The prop you will now use is “className”

edit(props) {

    const { 
        setAttributes, 
        attributes,
        className // The class name as a string!
    } = props;

    function onTextChange(changes) {
        setAttributes({
            textString: changes
        });
    }

    return (
        // We've added a container div
        // and we're placing our styles on that manually
        <div className={className}>
            <RichText
                tagName="h2"
                value={attributes.textString}
                onChange={onTextChange}
                placeholder="Enter your text here!"
                />
        </div>
    );
},

save(props) {

    const { attributes, className } = props;

    return (
        <div className={className}>
            <h2>{attributes.textString}</h2>
        </div>
    );
}

One thing is different now, though.  If you load up the block in the editor again, you will have an orange background but black text.  If you update, you will have an orange background and white text.

The reason for this is that you aren’t targeting the h2 color specifically, and the Gutenberg editor has styles that color headings black.

So here’s a suggestion for how to fix that:

.wp-block-firstgutyblocks-hello-world {
    background: orange;
}
.wp-block-firstgutyblocks-hello-world h2 {
    color: white;
}

Enqueueing editor-specific styles

The stylesheet we just added will always be used in the editor and in the view.  Sometimes, though, there will be elements that are used in the editor only that need to be styled.  So how to we enqueue those stylesheets?

Let’s create a stylesheet named ‘editor.css’ with the following:

.wp-block-firstgutyblocks-hello-world {
    position: relative;
}

.wp-block-firstgutyblocks-hello-world:before {
    position: absolute;
    top: 0;
    right: 1em;
    content: 'Editor mode!';
    color: navy;
    font-weight: bolder;
}

This will place a pseudo-element only when we’re editing the block.  In our php file we will also have to edit firstgutyblocks_hello_world_editor_assests():

function firstgutyblocks_hello_world_editor_assets() {
    wp_enqueue_script(
        'firstgutyblocks/hello-world',
        plugins_url( 'build/index.build.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element' )
    );
    wp_enqueue_style(
		'firstgutyblocks/hello-world-editor-style',
        plugins_url( 'src/editor.css', __FILE__ ),
        array( 'wp-edit-blocks' )
	);
};

add_action( 'enqueue_block_editor_assets', 'firstgutyblocks_hello_world_editor_assets');

It is important to name wp_enqueue_style differently (in our case ‘hello-world-editor-style’) to make sure there aren’t conflicts in enqueuing.  Also, be careful to change the dependency from ‘wp-blocks’ to ‘wp-edit-blocks’.

Hopefully you will see:

Hello word block with "editor mode" styling

So when is this useful?  I’ve used it a couple times where elements I created elments that only appear in the editor.  For example, when I created a gallery, I had buttons that allowed the editor to move images left/right or remove them.  These buttons need some sort of love and styling, but only occur in the editor.

Recap

We’ve gone over how to enqueue view and editor stylesheets.  We also used the className prop to manually place classNames within our blocks.

Next up we need to look at RichText more in depth and include InspectorControls and an editing Toolbar.

This is the second part of a WordPress Gutenberg Blocks series.  To follow this I’m assuming you understand part 1.  The repo for part 1 is located here  if you want to pick up where we left off.  The completed source code here is available in that repo in the branch ‘article2/richtext‘

Outline:

We left off last time with a very boring block.  We did the minimum to get something to show up in the editor (and that was enough for one article), but it didn’t show us why Gutenberg is going to be so fantastic at bringing true WYSIWYG to the WordPress editing experience.  Our goals now are:

  1. Import a RichText component users can directly edit
  2. Hook in this component using Gutenberg’s “attributes”
  3. Deal with common errors in developing blocks

But before we start let’s make a minor adjustment that will save time.  Before, we had to run “npm run build” every time we made changes to our javascript.  Let’s create a command that will watch our files and automatically run our build when it notices file changes.  Head over to our package.json and add the following:

...
"scripts": {
  "build": "webpack",
  "watch": "webpack --watch",
  "test": "echo \"Error: no test specified\" && exit 1"
},
...

We’re going to enter “npm run watch” and I’ll have it running for the rest of this article.

Importing the RichText Gutenberg component

Let’s change a few things in our index.js file:

const { registerBlockType } = wp.blocks;

registerBlockType('firstgutyblocks/hello-world', {
    title: "My First RichText Block!",
    icon: 'welcome-write-blog',
    category: 'common',

    edit() {
        return (
            <div>Only the editor will show this</div>
        );
    },

    save() {
        return (
            <div>Only the front end will show this</div>
        );
    }
})

So we’re changing the title, the icon, and the edit and save methods.  If you were to run this in the editor, you would again have static text.  One thing to point out- edit() will only be run in the editor.  This is the living, breathing, react block that can have user interaction built in.  When you hit “publish” or “update”, the editor will then run save() and spit out only what’s in that method.  It won’t be a living react component, but simple html.

So edit() is used to create the block and modify it.  save() spits out simple html to the post.

Let’s import the RichText component form wp.editor.

const { 
    registerBlockType,
} = wp.blocks;

const {
    RichText
} = wp.editor;

Again, this is using object destructuring.  We are taking registerBlockType and RichText from wp.blocks and wp-editor respectively and assigning them to their own variable so we don’t have to type out wp.editor.RichText every time we need it.

So what is the wp.blocks?  Remember back in our php file?


function firstgutyblocks_hello_world_editor_assets() {
    wp_enqueue_script(
        'firstgutyblocks/hello-world',
        plugins_url( 'build/index.build.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element')
    );
};

In wp_enqueue_script we pass in an array.  That array declares the javascript dependencies that we need to do our block, and wp.blocks is thusly added to the page for us to use!

RichText is a pre-built component for Gutenberg blocks.  It’s generally the way that you’ll create a field that the user can change the text in.  Here is the RichText component documentation. You should check it out, because the prebuilt stuff for Gutenberg normally has a readme.md file outlining the intended uses of that thing.

So enough with the background.  Let’s call this component in our edit() method :

const { 
    registerBlockType,
} = wp.blocks;

const {
    RichText
} = wp.editor;

registerBlockType('firstgutyblocks/hello-world', {
    title: "My First RichText Block!",
    icon: 'welcome-write-blog',
    category: 'common',

    edit() {
        return (
            <RichText
                value='The stuff inside the input'
                />
        );
    },

    save() {
        return (
            <div>Only the front end will show this</div>
        );
    }
})

Make sure that you’ve saved and webpack built your file with no errors.  Now load that block up in the editor!

When you do, you’ll notice that the value attribute is what shows up inside of the text box.  You’ll also notice that if you try editing the component, you definitely can, but you’ll get errors in the console.

Lovely.

Right now, RichText is an uncontrolled input.  This means, we haven’t hooked our input up to update react’s state.  This should be familiar to you if you’ve built some react apps.  If not, take a look at what it means to be controlled.

So RichText right now is complaining that it needs to know what to do when someone changes the input.  We need to give RichText a function that instructs it on how to update the “state” whenever there’s a change.  Gutenberg block doesn’t really have state the way a normal react app would, but instead has something called block “attributes.”

Setting up and updating attributes

We are going to add an attribute called “textString” to the block:

const { 
    registerBlockType,
} = wp.blocks;

const {
    RichText
} = wp.editor;

registerBlockType('firstgutyblocks/hello-world', {
    title: "My First RichText Block!",
    icon: 'welcome-write-blog',
    category: 'common',

    attributes: {
        textString: {
            type: 'array',
            source: 'children',
            selector: 'h2',
        }
    },

    edit() {
        return (
            <RichText
                value='The stuff inside the input'
                />
        );
    },

    save() {
        return (
            <div>Only the front end will show this</div>
        );
    }
})
  • “attributes” is similar to setting initial state in a normal React component. To change something, you set attributes and the block will react to those changes.
  • Attributes are encoded into the markup when you save.   This way, when you load the page back up in the editor, the editor remembers what values each attribute had when you last saved the page.  This is the reason for things like “selectors”.  It’s giving Gutenberg information about where the actual data lies inside of the markup.  In this case, it’s saying that “all the data for this attribute will be children of the h2 tag.”
  • Why is it an array?  Well, with RichText, technically you’re not just going to have a string there.  You might add bolded text, italics, and line breaks, which might have dom elements like span tags or strong tags.  React doesn’t think of this as a string anymore, but an array of elements.  We have to let Guty know that the children of the h2 tag might be a bunch of dom elements.
  • You can assign a type to the attributes, and also some other options like default values.  See more here…
  • To change attributes you use the setAttributes method, very much like setState in react.

TL;DR?  You need to declare attributes that will store your field information from a block.  This is like state from React.

We’re going to add a function “onTextChange” that takes the new RichText changes, and sets them in the attributes.  There are a lot of changes we’re going to make in the edit() method:

    // props are passed to edit by default
    // props contains things like setAttributes and attributes
    edit(props) {

        // we are peeling off the things we need
        const { setAttributes, attributes } = props;

        // This function is called when RichText changes
        // By default the new string is passed to the function
        // not an event object like react normally would do
        function onTextChange(changes) {
            // works very much like setState
            setAttributes({
                textString: changes
            });
        }

        return (
            <RichText
                tagName="h2"
                value={attributes.textString}
                onChange={onTextChange}
                placeholder="Enter your text here!"
                />
        );
    },

I’ve added tagName here so that the text field will appear as an <h2> element.  I’ve also added a placeholder because it’s a nice thing to do 🙂

If you refresh your editor, you should be able to add “My First RichText Block!” to your post or page.  You should be able to edit it, and notice that the text should be showing like an h2 element.

Dealing with editor errors

We’re not quite done yet.

If we hit “update” or “publish” and view the page, you will see the words “Only the front end will show this”.  This is because we never updated the save() method, and this method determines what’s actually saved to the page.  Let’s change that.

// again, props are automatically passed to save and edit
save(props) {

    const { attributes } = props;

    // We want the text to be an h2 element
    // and we place the textString value just
    // like we would in a normal react app
    return (
        <h2>{attributes.textString}</h2>
    );
}

Once that’s saved, reload the editor.  You will get an error that is very common while developing Guty blocks:

If you check the console you may get a message like:

You’ll notice that the “Actual” in this message was the old markup.  We saved our block previously with this markup and that’s what’s stored in the database currently.  But our block has been recently changed, and Gutenberg is expecting the block to include h2 tags now.  If the actual markup structure and the expected don’t match, the block explodes and you will have to remove this instance of the block and add a new instance back in.

This process is probably the most irritating part of Guty blocks.  I hope that they find a good solution to this.

After you’ve removed your old block, added the block back in, and saved, you should now see your block show up on viewing the page with the correct h2 tags.

Review

We have edited our block to include a prebuilt WordPress component, and hooked that component into the attributes of the block.  We also looked at how to handle markup differences when you load a saved block.

Hopefully, you could start playing around with this now and add your own fields that users could edit.  You could try adding multiple fields with different element tags, or maybe play around with some of the options in the documentation.  There’s also a simpler text editing field called PlainText you could try.

So now what?

These fields are going to be a huge part of making gutenberg blocks, but there are some essentials that we still need to go over:

  • What ways can we add styles?
  • How do we add images?
  • How do we create those fancy tool bars above the blocks and in the advanced settings panel?
  • How do we manage multiple blocks?

More to come!