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 all commits
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,141 @@
# 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 displays a `message` if set, otherwise show the form element:

```jsx
return (
<div>
{ attributes.message ?
<div>Message: { attributes.message }</div> :
<div>
<p>No Message.</p>
<TextControl
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
/>
</div>
}
);
```

There is a problem with the above, if we only use the `attributes.message` check, as soon as we type in the text field it would disappear since the message would then be set to a value. So we need to pair with an additional `isSelected` parameter.

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 className={ className }>
{ attributes.message && ! isSelected ? (
<div>{ 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.

## A Better Solution

The switching between a Placeholder and input control works well with a visual element like an image or video, but for the text example in this block we can do better.

The simpler and better solution is to modify the `editor.css` to include the proper 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';

export default function Edit( { attributes, className, setAttributes } ) {
return (
<TextControl
className={ className }
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
/>
);
}
```

Next Section: [Finishing Touches](finishing.md)
@@ -0,0 +1,54 @@
# 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](/docs/designers-developers/developers/block-api/block-registration.md) 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](/docs/designers-developers/developers/filters/block-filters.md#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.

Next Section: [Block Attributes](block-attributes.md)
@@ -0,0 +1,74 @@
# Block Attributes

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

For this block tutorial, we want to allow the user to type in a message that we will display stylized in the published post. So, we need to add a **message** attribute that will hold the user message. The following code defines a **message** attribute; the attribute type is a string; the source is the text from the selector which is a `div` tag.

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

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

When the block loads it will: look at the saved content for the block, look for the div tag, take the text portion, and store the content in an `attributes.message` variable.

Note: The text portion is equivalent to `innerText` attribute of a DOM element. For more details and other examples see the [Block Attributes documentation](/docs/designers-developers/developers/block-api/block-attributes.md).

## Edit and Save

The **attributes** are passed to the `edit` and `save` functions, along with a **setAttributes** function to set the values. Additional parameters are also passed in to this functions, see [the edit/save documentation](/docs/designers-developers/developers/block-api/block-edit-save.md) 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.

```js
export default function Edit( { attributes, setAttributes } ) {
// ...
}
```

## TextControl Component

For our 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](/packages/components/src/text-control/README.md). You can 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>;
}
```

Rebuild the block using `npm run build`, reload the editor and add the block. Type a message in the editor, save, and view it in the post.

Next Section: [Code Implementation](block-code.md)
@@ -0,0 +1,67 @@
# Code Implementation

The basic block is in place, the next step is to add styles 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

Download and extract the font from the Type with Pride site, and copy it to your plugin directory naming it `gilbert-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 files that match the handles in the `wp_register_style` functions.

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

## Add CSS Style for Block

We only need to add the style to `style.css` since it will show while editing and viewing the post. Edit the style.css to add the following.

Note: the block classname is prefixed with `wp-block`. The `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;
}
```

After updating, reload the post and refresh the brwoser. If you are using a browser that supports color fonts (Firefox) then you will see it styled.

## Use Sass for Style (optional)

The wp-scripts package provides support for using the Sass/Scss languages, to generate CSS, added in @wordpress/scripts v9.1.0. See the [Sass language site](https://sass-lang.com/) to learn more about Sass.

To use Sass, you need to import a `editor.scss` or `style.scss` in the `index.js` JavaScript file and it will build and output the generated file in the build directory. Note: You need to update the enqueing functions in PHP to load from the correct location.

Add the following imports to **index.js**:

```js
import '../editor.scss';

import Edit from './edit';
import save from './save';
```

Update **gutenpride.php** to enqueue from generated file location:

```php
$editor_css = "build/index.css";
```

Next Section: [Authoring Experience](author-experience.md)