diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md b/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md index 1129c3fcfc7aa..78a8a5cd50aa5 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md @@ -5,6 +5,28 @@ In the previous step, the block had applied its own styles by an inline `style` The editor will automatically generate a class name for each block type to simplify styling. It can be accessed from the object argument passed to the edit and save functions. In step 2, we will create a stylesheet to use that class name. {% codetabs %} +{% ESNext %} +```jsx +import { registerBlockType } from '@wordpress/blocks'; + +registerBlockType( 'gutenberg-examples/example-02-stylesheets', { + title: 'Example: Stylesheets', + + icon: 'universal-access-alt', + + category: 'layout', + + example: {}, + + edit( { className } ) { + return

Hello World, step 2 (from the editor, in green).

; + }, + + save() { + return

Hello World, step 2 (from the frontend, in red).

; + } +} ); +``` {% ES5 %} ```js ( function( blocks, element ) { @@ -35,28 +57,6 @@ The editor will automatically generate a class name for each block type to simpl window.wp.element ) ); ``` -{% ESNext %} -```js -import { registerBlockType } from '@wordpress/blocks'; - -registerBlockType( 'gutenberg-examples/example-02-stylesheets', { - title: 'Example: Stylesheets', - - icon: 'universal-access-alt', - - category: 'layout', - - example: {}, - - edit( { className } ) { - return

Hello World, step 2 (from the editor, in green).

; - }, - - save() { - return

Hello World, step 2 (from the frontend, in red).

; - } -} ); -``` {% end %} The class name is generated using the block's name prefixed with `wp-block-`, replacing the `/` namespace separator with a single `-`. diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md index 149ac8cc7760d..423a3b6aa8248 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md @@ -11,6 +11,85 @@ When the user selects a block, a number of control buttons may be shown in a too You can also customize the toolbar to include controls specific to your block type. If the return value of your block type's `edit` function includes a `BlockControls` element, those controls will be shown in the selected block's toolbar. {% codetabs %} +{% ESNext %} +```jsx +import { registerBlockType } from '@wordpress/blocks'; + +import { + RichText, + AlignmentToolbar, + BlockControls, +} from '@wordpress/block-editor'; + +registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { + title: 'Example: Controls (esnext)', + icon: 'universal-access-alt', + category: 'layout', + attributes: { + content: { + type: 'array', + source: 'children', + selector: 'p', + }, + alignment: { + type: 'string', + default: 'none', + }, + }, + example: { + attributes: { + content: 'Hello World', + alignment: 'right', + }, + }, + edit: ( props ) => { + const { + attributes: { + content, + alignment, + }, + className, + } = props; + + const onChangeContent = ( newContent ) => { + props.setAttributes( { content: newContent } ); + }; + + const onChangeAlignment = ( newAlignment ) => { + props.setAttributes( { alignment: newAlignment === undefined ? 'none' : newAlignment } ); + }; + + return ( +
+ { + + + + } + +
+ ); + }, + save: ( props ) => { + return ( + + ); + }, +} ); +``` {% ES5 %} ```js ( function( blocks, editor, element ) { @@ -93,85 +172,6 @@ You can also customize the toolbar to include controls specific to your block ty window.wp.element ) ); ``` -{% ESNext %} -```js -import { registerBlockType } from '@wordpress/blocks'; - -import { - RichText, - AlignmentToolbar, - BlockControls, -} from '@wordpress/block-editor'; - -registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { - title: 'Example: Controls (esnext)', - icon: 'universal-access-alt', - category: 'layout', - attributes: { - content: { - type: 'array', - source: 'children', - selector: 'p', - }, - alignment: { - type: 'string', - default: 'none', - }, - }, - example: { - attributes: { - content: 'Hello World', - alignment: 'right', - }, - }, - edit: ( props ) => { - const { - attributes: { - content, - alignment, - }, - className, - } = props; - - const onChangeContent = ( newContent ) => { - props.setAttributes( { content: newContent } ); - }; - - const onChangeAlignment = ( newAlignment ) => { - props.setAttributes( { alignment: newAlignment === undefined ? 'none' : newAlignment } ); - }; - - return ( -
- { - - - - } - -
- ); - }, - save: ( props ) => { - return ( - - ); - }, -} ); -``` {% end %} Note that `BlockControls` is only visible when the block is currently selected and in visual editing mode. `BlockControls` are not shown when editing a block in HTML editing mode. diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md b/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md index d73c1f2f7232e..8d8566af4b532 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md @@ -18,6 +18,38 @@ Block attributes can be used for any content or setting you want to save for tha The following code example shows how to create a dynamic block that shows only the last post as a link. {% codetabs %} +{% ESNext %} +```jsx +import { registerBlockType } from '@wordpress/blocks'; +import { withSelect } from '@wordpress/data'; + +registerBlockType( 'gutenberg-examples/example-dynamic', { + title: 'Example: last post', + icon: 'megaphone', + category: 'widgets', + + edit: withSelect( ( select ) => { + return { + posts: select( 'core' ).getEntityRecords( 'postType', 'post' ) + }; + } )( ( { posts, className } ) => { + + if ( ! posts ) { + return 'Loading...'; + } + + if ( posts && posts.length === 0 ) { + return 'No posts'; + } + + let post = posts[ 0 ]; + + return + { post.title.rendered } + ; + } ), +} ); +``` {% ES5 %} ```js ( function( blocks, element, data ) { @@ -26,7 +58,7 @@ The following code example shows how to create a dynamic block that shows only t registerBlockType = blocks.registerBlockType, withSelect = data.withSelect; - registerBlockType( 'gutenberg-examples/example-05-dynamic', { + registerBlockType( 'gutenberg-examples/example-dynamic', { title: 'Example: last post', icon: 'megaphone', category: 'widgets', @@ -60,38 +92,6 @@ The following code example shows how to create a dynamic block that shows only t window.wp.data, ) ); ``` -{% ESNext %} -```js -import { registerBlockType } from '@wordpress/blocks'; -import { withSelect } from '@wordpress/data'; - -registerBlockType( 'gutenberg-examples/example-05-dynamic', { - title: 'Example: last post', - icon: 'megaphone', - category: 'widgets', - - edit: withSelect( ( select ) => { - return { - posts: select( 'core' ).getEntityRecords( 'postType', 'post' ) - }; - } )( ( { posts, className } ) => { - - if ( ! posts ) { - return 'Loading...'; - } - - if ( posts && posts.length === 0 ) { - return 'No posts'; - } - - let post = posts[ 0 ]; - - return - { post.title.rendered } - ; - } ), -} ); -``` {% end %} Because it is a dynamic block it doesn't need to override the default `save` implementation on the client. Instead, it needs a server component. The contents in the front of your site depend on the function called by the `render_callback` property of `register_block_type`. @@ -99,11 +99,11 @@ Because it is a dynamic block it doesn't need to override the default `save` imp ```php 1, 'post_status' => 'publish', @@ -120,20 +120,24 @@ function gutenberg_examples_05_dynamic_render_callback( $attributes, $content ) ); } -function gutenberg_examples_05_dynamic() { +function gutenberg_examples_dynamic() { + // automatically load dependencies and version + $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php'); + wp_register_script( - 'gutenberg-examples-05', - plugins_url( 'block.js', __FILE__ ), - array( 'wp-blocks', 'wp-element', 'wp-data' ) + 'gutenberg-examples-dynamic', + plugins_url( 'build/block.js', __FILE__ ), + $asset_file['dependencies'], + $asset_file['version'] ); - register_block_type( 'gutenberg-examples/example-05-dynamic', array( - 'editor_script' => 'gutenberg-examples-05', - 'render_callback' => 'gutenberg_examples_05_dynamic_render_callback' + register_block_type( 'gutenberg-examples/example-dynamic', array( + 'editor_script' => 'gutenberg-examples-dynamic', + 'render_callback' => 'gutenberg_examples_dynamic_render_callback' ) ); } -add_action( 'init', 'gutenberg_examples_05_dynamic' ); +add_action( 'init', 'gutenberg_examples_dynamic' ); ``` @@ -150,6 +154,26 @@ Gutenberg 2.8 added the [``](/packages/components/src/server-s *Server-side render is meant as a fallback; client-side rendering in JavaScript is always preferred (client rendering is faster and allows better editor manipulation).* {% codetabs %} +{% ESNext %} +```jsx +import { registerBlockType } from '@wordpress/blocks'; +import ServerSideRender from '@wordpress/server-side-render'; + +registerBlockType( 'gutenberg-examples/example-dynamic', { + title: 'Example: last post', + icon: 'megaphone', + category: 'widgets', + + edit: function( props ) { + return ( + + ); + }, +} ); +``` {% ES5 %} ```js ( function( blocks, element, serverSideRender ) { @@ -158,7 +182,7 @@ Gutenberg 2.8 added the [``](/packages/components/src/server-s registerBlockType = blocks.registerBlockType, ServerSideRender = serverSideRender; - registerBlockType( 'gutenberg-examples/example-05-dynamic', { + registerBlockType( 'gutenberg-examples/example-dynamic', { title: 'Example: last post', icon: 'megaphone', category: 'widgets', @@ -167,7 +191,7 @@ Gutenberg 2.8 added the [``](/packages/components/src/server-s return ( el(ServerSideRender, { - block: "gutenberg-examples/example-05-dynamic", + block: "gutenberg-examples/example-dynamic", attributes: props.attributes } ) ); @@ -179,26 +203,6 @@ Gutenberg 2.8 added the [``](/packages/components/src/server-s window.wp.serverSideRender, ) ); ``` -{% ESNext %} -```js -import { registerBlockType } from '@wordpress/blocks'; -import ServerSideRender from '@wordpress/server-side-render'; - -registerBlockType( 'gutenberg-examples/example-05-dynamic', { - title: 'Example: last post', - icon: 'megaphone', - category: 'widgets', - - edit: function( props ) { - return ( - - ); - }, -} ); -``` {% end %} -Note that this code uses the `wp-components` package but not `wp-data`. Make sure to update the dependencies in the PHP code. You can use wp-scripts and ESNext setup for auto dependencies (see the [gutenberg-examples repo](https://github.com/WordPress/gutenberg-examples/tree/master/01-basic-esnext) for PHP code setup). +Note that this code uses the `wp-server-side-render` package but not `wp-data`. Make sure to update the dependencies in the PHP code. You can use wp-scripts and ESNext setup for auto dependencies (see the [gutenberg-examples repo](https://github.com/WordPress/gutenberg-examples/tree/master/01-basic-esnext) for PHP code setup). diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md b/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md index 8b6a10921c2ad..d3000cc9c4c99 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md @@ -26,26 +26,25 @@ In the code snippet above, when loading the editor, the `content` value will be Earlier examples used the `createElement` function to create DOM nodes, but it's also possible to encapsulate this behavior into "components". This abstraction helps you share common behaviors and hide complexity in self-contained units. -There are a number of [components available](/docs/designers-developers/developers/packages/packages-editor.md#components) to use in implementing your blocks. You can see one such component in the code below: the [`RichText` component](/docs/designers-developers/developers/packages/packages-editor.md#richtext). +There are a number of [components available](/docs/designers-developers/developers/packages/packages-editor.md#components) to use in implementing your blocks. You can see one such component in the code below: the [`RichText` component](/docs/designers-developers/developers/packages/packages-editor.md#richtext) is part of the `wp-editor` package. The `RichText` component can be considered as a super-powered `textarea` element, enabling rich content editing including bold, italics, hyperlinks, etc. -To use the `RichText` component, add `wp-editor` to the dependency array of registered script handles when calling `wp_register_script`. +To use the `RichText` component, and using ES5 code, remember to add `wp-editor` to the dependency array of registered script handles when calling `wp_register_script`. ```php +// automatically load dependencies and version +$asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php'); + wp_register_script( - 'gutenberg-examples-03', - plugins_url( 'block.js', __FILE__ ), - array( - 'wp-blocks', - 'wp-element', - 'wp-editor' // Note the addition of wp-editor to the dependencies - ), - filemtime( plugin_dir_path( __FILE__ ) . 'block.js' ) + 'gutenberg-examples-03-esnext', + plugins_url( 'build/index.js', __FILE__ ), + $asset_file['dependencies'], + $asset_file['version'] ); ``` -Do not forget to also update the `editor_script` handle in `register_block_type` to `gutenberg-examples-03`. +Do not forget to also update the `editor_script` handle in `register_block_type` to `gutenberg-examples-03-esnext`. Implementing this behavior as a component enables you as the block implementer to be much more granular about editable fields. Your block may not need `RichText` at all, or it may need many independent `RichText` elements, each operating on a subset of the overall block state. @@ -54,6 +53,46 @@ Because `RichText` allows for nested nodes, you'll most often use it in conjunct Here is the complete block definition for Example 03. {% codetabs %} +{% ESNext %} +```jsx +import { registerBlockType } from '@wordpress/blocks'; +import { RichText } from '@wordpress/block-editor'; + +registerBlockType( 'gutenberg-examples/example-03-editable-esnext', { + title: 'Example: Editable (esnext)', + icon: 'universal-access-alt', + category: 'layout', + attributes: { + content: { + type: 'array', + source: 'children', + selector: 'p', + }, + }, + example: { + attributes: { + content: 'Hello World', + }, + }, + edit: ( props ) => { + const { attributes: { content }, setAttributes, className } = props; + const onChangeContent = ( newContent ) => { + setAttributes( { content: newContent } ); + }; + return ( + + ); + }, + save: ( props ) => { + return ; + }, +} ); +``` {% ES5 %} ```js ( function( blocks, editor, element ) { @@ -106,44 +145,4 @@ Here is the complete block definition for Example 03. window.wp.element ) ); ``` -{% ESNext %} -```js -import { registerBlockType } from '@wordpress/blocks'; -import { RichText } from '@wordpress/block-editor'; - -registerBlockType( 'gutenberg-examples/example-03-editable-esnext', { - title: 'Example: Editable (esnext)', - icon: 'universal-access-alt', - category: 'layout', - attributes: { - content: { - type: 'array', - source: 'children', - selector: 'p', - }, - }, - example: { - attributes: { - content: 'Hello World', - }, - }, - edit: ( props ) => { - const { attributes: { content }, setAttributes, className } = props; - const onChangeContent = ( newContent ) => { - setAttributes( { content: newContent } ); - }; - return ( - - ); - }, - save: ( props ) => { - return ; - }, -} ); -``` {% end %} diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/readme.md b/docs/designers-developers/developers/tutorials/block-tutorial/readme.md index a3ee5191dbca7..42d72444e898e 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/readme.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/readme.md @@ -2,8 +2,8 @@ The purpose of this tutorial is to step through the fundamentals of creating a new block type. Beginning with the simplest possible example, each new section will incrementally build upon the last to include more of the common functionality you could expect to need when implementing your own block types. -To follow along with this tutorial, you can [download the accompanying WordPress plugin](https://github.com/WordPress/gutenberg-examples) which includes all of the examples for you to try on your own site. At each step along the way, you should feel free to experiment by modifying the examples with your own ideas and observing the effects they have on the block's behavior. +To follow along with this tutorial, you can [download the accompanying WordPress plugin](https://github.com/WordPress/gutenberg-examples) which includes all of the examples for you to try on your own site. At each step along the way, experiment by modifying the examples with your own ideas, and observe the effects they have on the block's behavior. -Code snippets are provided both for "classic" JavaScript (ECMAScript 5, or "ES5"), as well as newer versions of the language standard (ES2015 and newer, or "ESNext"). You can change between them using tabs found above each code example. When choosing to author your blocks with ESNext, you need to run [the JavaScript build step](/docs/designers-developers/developers/tutorials/javascript/js-build-setup/) in order to support older browsers. +Code snippets are provided in two formats "ES5" and "ESNext". ES5 refers to "classic" JavaScript (ECMAScript 5), while ESNext refers to the next versions of the language standard, plus JSX syntax. You can change between them using tabs found above each code example. Using ESNext, does require you to run [the JavaScript build step](/docs/designers-developers/developers/tutorials/javascript/js-build-setup/) to compile your code to a browser compatible format. -Note that it is not required to use ESNext to create a new block, and you are welcome to use classic JavaScript if you so choose. +Note that it is not required to use ESNext to create blocks or extend the editor, you can use classic JavaScript. However, once familiar with ESNext, developers find it is easier to read and write, thus most code examples you'll find use the ESNext syntax. diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md index 81794c30be53f..0654857525f14 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md @@ -6,7 +6,9 @@ Blocks containing static content are implemented entirely in JavaScript using th ## Enqueuing Block Scripts -While the block's editor behaviors are implemented in JavaScript, you'll need to register your block server-side to ensure that the script is enqueued when the editor loads. Register scripts and styles using [`wp_register_script`](https://developer.wordpress.org/reference/functions/wp_register_script/) and [`wp_register_style`](https://developer.wordpress.org/reference/functions/wp_register_style/), then assign these as handles associated with your block using the `script`, `style`, `editor_script`, and `editor_style` block type registration settings. The `editor_`-prefixed handles will only be enqueued in the context of the editor, while `script` and `style` will be enqueued both in the editor and when viewing a post on the front of your site. +While the block's editor behaviors are implemented in JavaScript, you'll need to register your block server-side to ensure that the script is enqueued when the editor loads. Register scripts and styles using [`wp_register_script`](https://developer.wordpress.org/reference/functions/wp_register_script/) and [`wp_register_style`](https://developer.wordpress.org/reference/functions/wp_register_style/), then assign these as handles associated with your block using the `script`, `style`, `editor_script`, and `editor_style` block type registration settings. + +The `editor_script` and `editor_style` files will only be enqueued in the editor, while the `script` and `style` will be enqueued both in the editor and when viewing a post on the front of your site. ```php 'gutenberg-examples-01', + register_block_type( 'gutenberg-examples/example-01-basic-esnext', array( + 'editor_script' => 'gutenberg-examples-01-esnext', ) ); } add_action( 'init', 'gutenberg_examples_01_register_block' ); ``` -Note the two script dependencies: +Note the above example, shows using the [wp-scripts build step](/docs/designers-developers/developers/tutorials/javascript/js-build-setup/) that automatically sets dependencies and versions the file. + +If you were using the ES5 code, you would specify `array( 'wp-blocks', 'wp-element' )` as the dependency array. See the [example 01](https://github.com/WordPress/gutenberg-examples/blob/master/01-basic/index.php) in Gutenberg Examples repository for full syntax. - __`wp-blocks`__ includes block type registration and related functions - __`wp-element`__ includes the [WordPress Element abstraction](/packages/element/README.md) for describing the structure of your blocks -If you were to use a component from the `wp-editor` package, for example the RichText component, you would also need to add `wp-editor` to the dependency list. ## Registering the Block With the script enqueued, let's look at the implementation of the block itself: {% codetabs %} +{% ESNext %} +```jsx +import { registerBlockType } from '@wordpress/blocks'; + +const blockStyle = { + backgroundColor: '#900', + color: '#fff', + padding: '20px', +}; + +registerBlockType( 'gutenberg-examples/example-01-basic-esnext', { + title: 'Example: Basic (esnext)', + icon: 'universal-access-alt', + category: 'layout', + example: {}, + edit() { + return
Hello World, step 1 (from the editor).
; + }, + save() { + return
Hello World, step 1 (from the frontend).
; + }, +} ); +``` {% ES5 %} ```js ( function( blocks, element ) { @@ -76,29 +107,6 @@ With the script enqueued, let's look at the implementation of the block itself: window.wp.element ) ); ``` -{% ESNext %} -```js -import { registerBlockType } from '@wordpress/blocks'; - -const blockStyle = { - backgroundColor: '#900', - color: '#fff', - padding: '20px', -}; - -registerBlockType( 'gutenberg-examples/example-01-basic-esnext', { - title: 'Example: Basic (esnext)', - icon: 'universal-access-alt', - category: 'layout', - example: {}, - edit() { - return
Hello World, step 1 (from the editor).
; - }, - save() { - return
Hello World, step 1 (from the frontend).
; - }, -} ); -``` {% end %} _By now you should be able to see `Hello World, step 1 (from the editor).` in the admin side and `Hello World, step 1 (from the frontend).` on the frontend side._ diff --git a/docs/designers-developers/developers/tutorials/javascript/versions-and-building.md b/docs/designers-developers/developers/tutorials/javascript/versions-and-building.md index 86900a89b8619..f0593324b09a0 100644 --- a/docs/designers-developers/developers/tutorials/javascript/versions-and-building.md +++ b/docs/designers-developers/developers/tutorials/javascript/versions-and-building.md @@ -6,7 +6,8 @@ ES5 code is compatible with WordPress's minimum [target for browser support](htt "ESNext" doesn't refer to a specific version of JavaScript, but is "dynamic" and refers to the next language definitions, whatever they might be. Because some browsers won't support these features yet (because they're new or proposed), an extra build step is required to transform the code to a syntax that works in all browsers. Webpack and babel are the tools that perform this transformation step. -Additionally, the ESNext code examples in the handbook include [JSX syntax](https://reactjs.org/docs/introducing-jsx.html), a syntax that blends HTML and JavaScript. It makes it easier to read and write markup code, but likewise requires the build step using Webpack and Babel to transform into compatible code. +Additionally, the ESNext code examples in the handbook include [JSX syntax](https://reactjs.org/docs/introducing-jsx.html), a syntax that blends HTML and JavaScript. JSX makes it easier to read and write markup code, but does require a build step to transform into compatible code. -For simplicity, the JavaScript tutorial uses the ES5 definition, without JSX. This code can run straight in your browser and does not require an additional build step. In many cases, it will be perfectly fine to follow the same approach to quickly start experimenting with your plugin or theme. As soon as your codebase grows to hundreds of lines it might be a good idea to get familiar with the [JavaScript Build Setup documentation](/docs/designers-developers/developers/tutorials/javascript/js-build-setup.md) for setting up a development environment to use ESNext syntax. +For simplicity, the JavaScript tutorial uses the ES5 definition, without JSX. This code can run straight in your browser and does not require an additional build step. In many cases, it is perfectly fine to follow the same approach for simple plugins or experimenting. As your codebase grows in complexity it might be a good idea to switch to ESNext. You will find the majority of code and documentation across the block editor uses ESNext. +See the [JavaScript Build Setup documentation](/docs/designers-developers/developers/tutorials/javascript/js-build-setup.md) for setting up a development environment using ESNext syntax.