Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Create a Block tutorial #22831

Merged
merged 25 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@

# Authoring Experience

## Background

One of the primary tenets of Gutenberg is as a WYSIWYG editor, what you see in the editor, should be as close to what you get when published. Keep this in mind when building blocks.

## Placeholder

The state when a block has been inserted, but no data has been entered yet, is called a placeholder. There is a `Placeholder` component built that gives us a standard look. You can see example placeholders in use with the image and embed blocks.

To use the Placeholder, wrap the `<TextControl>` component so it becomes a child element of the `<Placeholder>` component. Try it out in your code. After updating, you might have something like:

```jsx
import { Placeholder, TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

export default function Edit( { attributes, className, setAttributes } ) {
return (
<div className={ className }>
<Placeholder
label="Gutenpride Block"
instructions="Add your message"
>
<TextControl
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
/>
</Placeholder>
</div>
);
}
```

## isSelected Ternary Function

The placeholder looks ok, for a simple text message it may or may not be what you are looking for. However, the placeholder can be useful if you are replacing the block after what is typed in, similar to the embed blocks.

For this we can use a ternary function, to display content based on a value being set or not. A ternary function is an inline if-else statement, using the syntax:

```js
( clause ) ? ( doIfTrue ) : ( doIfFalse )
```

This can be used inside a block to control what shows when a parameter is set or not. A simple case that checks if the `message` is set might look like:

```jsx
return (
<div>
{ attributes.message ?
<div> Message: { attributes.message }</div> :
<div> No Message <TextField/> </div>
mkaz marked this conversation as resolved.
Show resolved Hide resolved
}
);
```

If we only used the above check, as soon as we type anything, the textfield would disappear since the message would be set. So we need to pair with the `isSelected` parameter.
mkaz marked this conversation as resolved.
Show resolved Hide resolved

The `isSelected` parameter is passed in to the `edit` function and is set to true if the block is selected in the editor (currently editing) otherwise set to false (editing elsewhere).

Using that parameter, we can use the logic:

```js
attributes.message && ! isSelected
```

If the message is set and `!isSelected`, meaning we are not editing the block, the focus is elsewhere, then display the message not the text field.

All so this combined together here's what the edit function looks like this:

```jsx
import { Placeholder, TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

export default function Edit( { attributes, className, isSelected, setAttributes } ) {
return (
<div>
{ attributes.message && !isSelected ?
<div className={ className }>
mkaz marked this conversation as resolved.
Show resolved Hide resolved
{ attributes.message }
</div> :
<Placeholder
label="Gutenpride Block"
instructions="Add your message"
>
<TextControl
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
/>
</Placeholder>
}
</div>
);
}
```

With that in place, rebuild and reload and when you are not editing the message is displayed as it would be for the view, when you click into the block you see the text field.

## An Better Solution
mkaz marked this conversation as resolved.
Show resolved Hide resolved

Replacing the Placeholder and TextControl when it is selected or not is jarring and not an ideal situation for this block. This was mainly used to illustrate what can be done depending on your block. It is important to think about the author's experience using the block.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be rephrased a bit. My first reaction was: if it’s jarring why I even had to think about it. Can we start with thinking about the best experience, emphasis that placeholders are usually the way to go, it’s one of principles of blocks but here we can simplify it with CSS?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced the placeholder with how images and embeds handle it, which works well for images, but not for text. The goal was to show how people might think about the problem seeing other references.

I rephrase and we see if that is better.


The simpler and better solution is to modify the editor.css to include the proper style for the textfield, this will give the stylized text while typing.

Update `editor.css` to:

```css
.wp-block-create-block-gutenpride input[type="text"] {
font-family: Gilbert;
font-size: 64px;
}
```

The edit function can simply be:

```jsx
import { TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
mkaz marked this conversation as resolved.
Show resolved Hide resolved

export default function Edit( { attributes, className, setAttributes } ) {
return (
<TextControl
className={ className }
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
/>
);
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

# Anatomy of a Gutenberg Block

At its simplest, a block in Gutenberg is a JavaScript object with a specific set of properties. Here is the complete code for registering a block:

```js
import { registerBlockType } from '@wordpress/blocks';

registerBlockType( 'create-block/gutenpride', {
title: 'Gutenpride',
description: 'Example block.',
category: 'widgets',
icon: 'smiley',
supports: {
// Removes support for an HTML mode.
html: false,
},

edit: ( ) => {
return (
<div> Hello in Editor. </div>
);
},

save: ( ) => {
return (
<div> Hello in Save.</div>
);
},
} );
```

The first parameter in the **registerBlockType** function is the block name, this should match exactly to the name registered in the PHP file.

The second parameter to the function is the block object. See the [block registration documentation](https://developer.wordpress.org/block-editor/developers/block-api/block-registration/) for full details.

The **title** is the title of the block shown in the Inserter.

The **icon** is the icon shown in the Inserter. The icon property expects any Dashicon name as a string, see [list of available icons](https://developer.wordpress.org/resource/dashicons/). You can also provide an SVG object, but for now it's easiest to just pick a Dashicon name.

The **category** specified is a string and must be one of: \"common, formatting, layout, widgets, or embed\". You can create your own custom category name, [see documentation for details](https://developer.wordpress.org/block-editor/designers-developers/developers/filters/block-filters/#managing-block-categories). For this tutorial, I specified "widgets" as the category.

The last two block object properties are **edit** and **save**, these are the key parts of a block. Both properties should be defined as functions.

The results of the edit function is what the editor will render to the editor page when the block is inserted.

The results of the save function is what the editor will insert into the **post_content** field when the post is saved. The post_content field is the field in the WordPress database used to store the content of the post.

## Internationalization

If you look at the generated `src/index.js` file, the block title and description are wrapped in a function that looks like this:

```js
__('Gutenpride', 'create_block')
```

This is an internationalization wrapper that allows for the string "Gutenpride" to be translated. The second parameter, "create_block" is called the text domain and gives context for where the string is from. The JavaScript internationalization, often abbreviated i18n, matches the core WordPress internationalization process. See the [I18n for WordPress documentation](https://codex.wordpress.org/I18n_for_WordPress_Developers) for more details.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

# Block Attributes

Attributes are the way a block stores data, they define how a block is parsed to extract data from the saved content.

We will add a **message** attribute that will be the variable to hold the user message. The following code defines a **message** attribute of type string with the value derived from the source, defined as the text of a `div` tag.

```js
attributes: {
message: {
type: 'string',
source: 'text',
selector: 'div',
},
},
```

Add this to the `index.js` file within the `registerBlockType` function in `index.js`, `attributes` are at the same level as the title and description fields.

To repeat, when the block loads it will look at the saved content for the block, look for the div tag, take the text portion — the part in between the open and close div tags — and store the content in an `attributes.message` variable.

For more details and other examples see the [Block Attributes documentation](https://developer.wordpress.org/block-editor/developers/block-api/block-attributes/).

## Edit and Save

The **attributes** are passed to the `edit` and `save` functions, along with a **setAttributes** parameters for setting the values after the user enters. Additional parameters are also passed in to this functions, see [the edit/save documentation](https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/) for more details.

The attributes is a JavaScript object containing the values of each attribute, or default values if defined. The setAttributes is a function to update an attribute. If you are familiar with React, this is similar to state and setState.

## TextControl Component

For this example block, the component we are going to use is the **TextControl** component, it is similar to an HTML text input field. You can see [documentation for TextControl component](https://developer.wordpress.org/block-editor/components/text-control/) and a complete list of components in the handbook. You can also browse an [interactive set of components in this Storybook](https://wordpress.github.io/gutenberg/).

The component is added similar to an HTML tag, setting a label, the `value` is set to the `attributes.message` and the `onChange` function uses the `setAttributes` to update the url attribute value.

The save function will simply write the `attributes.message` as a div tag since that is how we defined it to be parsed.

Update the edit.js and save.js files to the following, replacing the existing functions.

**edit.js**

```js
import { TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

export default function Edit( { attributes, className, setAttributes } ) {
return (
<div className={ className }>
<TextControl
label={ __( "Message", "create-block" ) }
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
/>
</div>
);
}
```


**save.js**

```jsx
export default function Save( { attributes, className } ) {
return (
<div className={ className }>
{ attributes.message }
</div>
);
}
```

With that code in place, rebuild the block using `npm run build`, reload the editor and add the block. You should be able to type a message in the editor, and on save, view it in the post.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

# Code Implementation

The basic block is in place, the next step is to add some style to the block. Feel free to style and adjust for your own preference, the main lesson is showing how to create and load external resources. For this example I'm going to load the colorized gilbert font from [Type with Pride](https://www.typewithpride.com/).

Note: The color may not work with all browsers until they support the proper color font properly, but the font itself still loads and styles. See [colorfonts.wtf](https://www.colorfonts.wtf/) for browser support and details on color fonts.

## Load Font File

I downloaded and extracted the font from the Type with Pride site, and copied it to my plugin directory naming it `gilber-color.otf`. To load the font file, we need to add CSS using standard WordPress enqueue, [see Including CSS & JavaScript documentation](https://developer.wordpress.org/themes/basics/including-css-javascript/).

In the `gutenpride.php` file, the enqueue process is already setup from the generated script, so `editor.css` and `style.css` files are loaded using:

```php
register_block_type( 'create-block/gutenpride', array(
'editor_script' => 'create-block-gutenpride-block-editor',
'editor_style' => 'create-block-gutenpride-block-editor',
'style' => 'create-block-gutenpride-block',
) );
```
The `editor_style` and `style` parameters refer to the handles in the `wp_register_style` functions that match the files to the handles.

Note: The `style` CSS will load on both the editor and front-end — published post view — the `editor_style` loads only within the editor, and after the style css.

## Add CSS Style for Block

The style.css will be loaded in both contexts, the editor and the front-end, so we only need to add the style in one spot and it will show while editing and viewing the post.

Edit the style.css to the following, note the classname given to a block is prefixed with `wp-block` and then adds the block name converting any `/` to `-` so in this case the block name `create-block/gutenpride` is converted to the classname `.wp-block-create-block-gutenpride`.

```css
@font-face {
font-family: Gilbert;
src: url(gilbert-color.otf);
font-weight: bold;
}

.wp-block-create-block-gutenpride {
font-family: Gilbert;
font-size: 64px;
}
```

With that updated, you can reload the post, for CSS changes you don't need to rebuild, so refresh and if you are using a browser that supports color fonts (Firefox) then you will see it styled.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

# Create Block: Developer Environment

The three main pieces needed for the development environment are:

1. Node/NPM Development Tools
2. WordPress Development Site
3. Code Editor

## Development Tools

The tools needed for development are **Node** and **NPM**. **Nodejs** is a runtime environment that allows running JavaScript outside of the browser. NPM is the Node Package Manager, it is used for installing dependencies and running scripts. The script `npx` is installed with `npm` and is used to run packages not yet installed, we will use this to bootstrap a block.

The tools are used to take the JavaScript we are going to write, which is in a syntax that the browser can not run, and transpile it into a syntax it can. This is called the build step.

You can [download Nodejs](https://nodejs.org/) directly from the main website and install. It also packaged for most package managers.

On Mac, I recommend using [Homebrew package tool](https://brew.sh/) and install is: `brew install node`js

On Windows, you can use Chocolatey package manager and install using: `choco install nodejs`

NPM usually comes bundled with the above installs. On Ubuntu, or Debian, NPM is bundled separately and you can install using: `apt install nodejs npm`

However you install Nodejs, the important part is being able to use them in your terminal. Open a terminal command-line and be able to run `node -v` and `npm -v` to confirm they are installed.


## WordPress Development Site

There are several ways to run WordPress locally on your own computer, or you could even develop on a cloud hosted computer, though this may be slower.

The WordPress [wp-env package](https://www.npmjs.com/package/@wordpress/env) lets you set up a local WordPress environment for building and testing plugins and themes, without any additional configuration.

The `wp-env` package requires Docker to be installed. There are instructions available for installing Docker on [Windows 10 Pro](https://docs.docker.com/docker-for-windows/install/), [all other versions of Windows](https://docs.docker.com/toolbox/toolbox_install_windows/), [macOS](https://docs.docker.com/docker-for-mac/install/), and [Linux](https://docs.docker.com/v17.12/install/linux/docker-ce/ubuntu/#install-using-the-convenience-script).


After confirming that the prerequisites are installed, you can install wp-env globally from the command-line running:

```bash
npm -g install @wordpress/env
```

### Alternatives

If you are just starting out, using [Local by Flywheel](https://localbyflywheel.com/) might be easier, since it does not require the additional Docker install and setup. Local is a single application you download and install and tends to be much simpler than alternatives. You will need to know where the plugin directory is installed. If you create a site called `mywp` typically the plugin directory is installed at `~\Local Sites\mywp\app\public\wp-content\plugins`

You can use [WampServer](http://www.wampserver.com/en/) or
[MAMP](https://www.mamp.info/) environments, both are quite similar to
Local, combining a web server, PHP, and database. However these tools
are not WordPress specific, so if you are not already using them, you might as
well use Local.

You can also work remotely on a server, this might be easy to setup the server since most hosts have a WordPress already installed. However, this may require development time since it may require syncing files, or editing the directly on the server.

The important part is having a WordPress site installed, and know where and how to update files in the plugins directory.

## Code Editor

[Visual Studio Code](https://code.visualstudio.com/) is a popular code editor for JavaScript development. It works quite well, open-source, actively maintained by Microsoft, and a vibrant community providing plugins and extensions; it is becoming the defacto standard for web development.

You can use any editor you're comfortable with, it is more a personal preference. The development setup for WordPress block editor is a common JavaScript environment and most editors have plugins and suppport. The key is having a way to open, edit, and save text files.
Loading