-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Docs: Create a Block tutorial (#22831)
* Init Create a Block tutorial * Fix typo * Add Sass support info h/t @zzap * Fix typos * Add alternative code editor options * Add Chrome info for developer tools * Add clarification around message attribute * Update finishing touches with additional resources * Fix links * Fix typo * Various Updates per @gziolo review - Add clarification around TextControl use - Fix classname back to root element - Rephrase on how to better stylize text - Simplify quick start, emphasize activation - Add CLI prompts for questions * Add clarification around installs - Call out package managers is an alternative - Be clear about what is needed per platform * Tweak installation instructions * Remove package manager part, refer to Node site * Add version and LTS info to node * Add version and optional to Sass section * Connect devenv to plugin using wp-env * Numerous updates based on reviews 🙌 * Add details around wp-env, and alternatives * Add Mac recommendation to use Homebrew * Add further clarification for Mac/Homebrew * Add headings for plugin sections, and two paths * Switch recommended install to nvm for Mac/Linux * Add docs to TOC, nav links * Delist tutorial, so we can merge and iterate
- Loading branch information
Showing
9 changed files
with
772 additions
and
0 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
docs/designers-developers/developers/tutorials/create-block/author-experience.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
54 changes: 54 additions & 0 deletions
54
docs/designers-developers/developers/tutorials/create-block/block-anatomy.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
74 changes: 74 additions & 0 deletions
74
docs/designers-developers/developers/tutorials/create-block/block-attributes.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
67 changes: 67 additions & 0 deletions
67
docs/designers-developers/developers/tutorials/create-block/block-code.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Oops, something went wrong.