From d6d15a2d9bdd1f3fb820246a308d595f6ee10b25 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 24 Mar 2017 11:21:52 -0400 Subject: [PATCH 1/7] Add blocks and elements documentation --- blocks/README.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++ blocks/index.js | 6 +- element/README.md | 65 ++++++++++++++++++ 3 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 blocks/README.md create mode 100644 element/README.md diff --git a/blocks/README.md b/blocks/README.md new file mode 100644 index 0000000000000..1460d1a4c07ec --- /dev/null +++ b/blocks/README.md @@ -0,0 +1,166 @@ +# Blocks + +Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage. The idea combines concepts of what in WordPress today we achieve with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. + +For more context, refer to [_What Are Little Blocks Made Of?_](https://make.wordpress.org/design/2017/01/25/what-are-little-blocks-made-of/) from the [Make WordPress Design](https://make.wordpress.org/design/) blog. + +The following documentation outlines steps you as a developer will need to follow to add your own custom blocks to WordPress's editor interfaces. + +## Getting Started + +If you're not already accustomed to working with JavaScript in your WordPress plugins, you may first want to reference the guide on [_Including CSS & JavaScript_](https://developer.wordpress.org/themes/basics/including-css-javascript/) in the Theme Handbook. + +At a minimum, you will need to enqueue scripts for your block as part of a `block_enqueue_scripts` action callback, with a dependency on the `wp-blocks` script handle: + +```php + + +``` + +Refer to the [official React Quick Start guide](https://facebook.github.io/react/docs/hello-world.html) for a more thorough walkthrough, in most cases substituting `React` and `ReactDOM` with `wp.element` in code examples. + +## Why React? + +At the risk of igniting debate surrounding any single "best" front-end framework, the choice to use any tool should be motivated specifically to serve the requirements of the system. In modeling the concept of a [block](../blocks/README.md), we observe the following technical requirements: + +- An understanding of a block in terms of its underlying values (in the [Gravatar example](../blocks/README.md#example), an email address) +- A means to describe the UI of a block given these values + +At its most basic, React provides a simple input / output mechanism. __Given a set of inputs ("props"), a developer describes the output to be shown on the page.__ This is most elegantly observed in its [function components](https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components). React serves the role of reconciling the desired output with the current state of the page. + +The offerings of any framework necessarily become more complex as these requirements increase; many front-end frameworks prescribe ideas around page routing, retrieving and updating data, and managing layout. React is not immune to this, but the introduced complexity is rarely caused by React itself, but instead managing an arrangement of supporting tools. By moving these concerns out of sight to the internals of the system (WordPress core code), we can minimize the responsibilities of plugin authors to a small, clear set of touch points. + +## JSX + +While not at all a requirement to use React, [JSX](https://facebook.github.io/react/docs/introducing-jsx.html) is a recommended syntax extension to compose elements more expressively. Through a build process, JSX is converted back to the `createElement` syntax you see earlier in this document. + +If you've configured [Babel](http://babeljs.io/) for your project, you can opt in to JSX syntax by specifying the `pragma` option of the [`transform-react-jsx` plugin](https://www.npmjs.com/package/babel-plugin-transform-react-jsx) in your [`.babelrc` configuration](http://babeljs.io/docs/usage/babelrc/). + +```json +{ + "plugins": [ + [ "transform-react-jsx", { + "pragma": "wp.element.createElement" + } ] + ] +} +``` From 3760dc92b1f4c48dde77124e4e09d632a2f06fab Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 24 Mar 2017 13:07:51 -0400 Subject: [PATCH 2/7] Replace Gravatar example with random image Demonstrate markup attributes retrieval --- blocks/README.md | 97 ++++++++++++++++++++++++++--------------------- element/README.md | 3 +- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/blocks/README.md b/blocks/README.md index 1460d1a4c07ec..895f3df8371ff 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -26,60 +26,72 @@ The following sections will describe what you'll need to include in `block.js` t ## Example -Let's imagine you wanted to define a block to enable you or other users on your site to insert a [Gravatar](http://en.gravatar.com/) image. The URL for a Gravatar image is determined by applying an [md5 hash](https://en.wikipedia.org/wiki/MD5) on an email address and appending it as a path to `gravatar.com/avatar/[hash]`. Of course, it's not a very interesting example if the user can't customize the block, so we'll assume it's a requirement to provide an email address of their choosing. +Let's imagine you wanted to define a block to show a randomly generated image in a post's content using [lorempixel.com](http://lorempixel.com/). The service provides a choice of category and you'd like to offer this as an option when editing the post. -Take a step back and consider the ideal workflow for adding a Gravatar. After inserting a new "Gravatar" block, as a user I'd expect it to be shown in some empty state, with an option to add an email address in a [text input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). Upon entering an email address, a preview of the image should be shown next to the input. At this point, you might realize that while you'd want some controls to be shown when editing content, the markup included in the published post might not appear the same (your visitors should not see an input field when reading your content). This leads to the first requirement of describing a block: __you will need to provide implementations both for what's to be shown in an editor and what's to be saved with the published content__. You needn't worry about redundant effort here, as concepts of [Elements](../elements/README.md) and componentization provide an avenue for sharing common behaviors. +Take a step back and consider the ideal workflow for adding a new random image. After inserting the block, as a user I'd expect it to be shown in some empty state, with an option to choose a category in a select dropdown. Upon confirming my selection, a preview of the image should be shown next to the dropdown. At this point, you might realize that while you'd want some controls to be shown when editing content, the markup included in the published post might not appear the same (your visitors should not see a dropdown field when reading your content). This leads to the first requirement of describing a block: __you will need to provide implementations both for what's to be shown in an editor and what's to be saved with the published content__. You needn't worry about redundant effort here, as concepts of [Elements](../elements/README.md) and componentization provide an avenue for sharing common behaviors. -Now that we've considered user interaction, you should think about the underlying values that determine the markup generated by your block. In our example, the output is affected only when the email address changes. Put another way: __the output of a block is a function of its attributes__. The email address, a simple string, is the only thing we require to be able to generate the image we want to include in the published content. We call these underlying values of a block instance its _attributes_. +Now that we've considered user interaction, you should think about the underlying values that determine the markup generated by your block. In our example, the output is affected only when the category changes. Put another way: __the output of a block is a function of its attributes__. The category, a simple string, is the only thing we require to be able to generate the image we want to include in the published content. We call these underlying values of a block instance its _attributes_. -With these concepts in mind, let's explore an implementation of our Gravatar block: +With these concepts in mind, let's explore an implementation of our random image block: ```php Date: Fri, 24 Mar 2017 13:10:37 -0400 Subject: [PATCH 3/7] Prescribe comment metadata attribute behavior --- blocks/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/blocks/README.md b/blocks/README.md index 895f3df8371ff..b5e1f1500be8f 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -124,6 +124,7 @@ Registers a new block provided a unique slug and an object defining its behavior - `save( attributes: Object ): WPElement` - Returns an element describing the markup of a block to be saved in the published content. This function is called before save and when switching to an editor's HTML view. - `tagName: string` - An alternative to defining `edit` and `save` behaviors, if passed a [tag name](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#Parameters), delegates default behavior of an editable field on that node type. Typically used for simple, text-heavy elements (paragraphs, headings) to avoid excess meta storage of text. - `controls: string[]` - Slugs for controls to be made available to block. See also: [`wp.blocks.registerControl`](#wpblocksregistercontrol-slug-string-settings-object-) +- `encodeAttributes( attributes: Object ): Object` - Called before save, this function allows you to control which attributes are to be saved in the block comment metadata. By default, all attribute values not defined in the block's `attributes` property are serialized to the comment metadata. ### `wp.blocks.registerControl( slug: string, settings: Object )` From 46689f49d55ffffaed29d2e1e6e3915a23753b0f Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 24 Mar 2017 13:16:18 -0400 Subject: [PATCH 4/7] Rename "name" as "title", use consistently --- blocks/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/blocks/README.md b/blocks/README.md index b5e1f1500be8f..16fe7757c59de 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -59,6 +59,8 @@ function RandomImage( props ) { } wp.blocks.registerBlock( 'myplugin/random-image', { + title: 'Random Image', + attributes: { category: query.attr( 'img', 'alt' ) }, @@ -119,7 +121,7 @@ In the random image block above, we've given the `alt` attribute of the image a Registers a new block provided a unique slug and an object defining its behavior. Once registered, the block is made available as an option to any editor interface where blocks are implemented. -- `name: string` - A human-readable [localized](https://codex.wordpress.org/I18n_for_WordPress_Developers#Handling_JavaScript_files) label for the block. Shown in the block picker. +- `title: string` - A human-readable [localized](https://codex.wordpress.org/I18n_for_WordPress_Developers#Handling_JavaScript_files) label for the block. Shown in the block picker. - `edit( attributes: Object, setAttributes: Function ): WPElement` - Returns an element describing the markup of a block to be shown in the editor. A block can update its own state in response to events using the `setAttributes` function, passing an object of properties to be applied as a partial update. - `save( attributes: Object ): WPElement` - Returns an element describing the markup of a block to be saved in the published content. This function is called before save and when switching to an editor's HTML view. - `tagName: string` - An alternative to defining `edit` and `save` behaviors, if passed a [tag name](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#Parameters), delegates default behavior of an editable field on that node type. Typically used for simple, text-heavy elements (paragraphs, headings) to avoid excess meta storage of text. @@ -130,6 +132,7 @@ Registers a new block provided a unique slug and an object defining its behavior Registers a new block-level control. Controls appear in a block's toolbar when it receives focus if it is included in the block's `controls` option. +- `title: string` - A human-readable [localized](https://codex.wordpress.org/I18n_for_WordPress_Developers#Handling_JavaScript_files) label for the control. Shown in help tooltips. - `icon: string | WPElement` - Slug of the [Dashicon](https://developer.wordpress.org/resource/dashicons/#awards) to be shown in the control's button, or an element if you choose to render your own SVG. - `onClick( attributes: Object, setAttributes: Function )` - Click behavior for control. Use this to change or toggle an attribute of the block. - `isVisible( attributes: Object ): boolean` - Called when a block receives focus or changes. Return `false` to prevent the control's button from being shown. If this option is not defined for a control, the button will always be shown. From 18c427ac86b6d7b9d2491f5a5cb2c11add51dcd1 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 24 Mar 2017 13:16:29 -0400 Subject: [PATCH 5/7] Document icon usage for blocks --- blocks/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blocks/README.md b/blocks/README.md index 16fe7757c59de..42364b5ff4d27 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -61,6 +61,8 @@ function RandomImage( props ) { wp.blocks.registerBlock( 'myplugin/random-image', { title: 'Random Image', + icon: 'format-image', + attributes: { category: query.attr( 'img', 'alt' ) }, @@ -122,6 +124,7 @@ In the random image block above, we've given the `alt` attribute of the image a Registers a new block provided a unique slug and an object defining its behavior. Once registered, the block is made available as an option to any editor interface where blocks are implemented. - `title: string` - A human-readable [localized](https://codex.wordpress.org/I18n_for_WordPress_Developers#Handling_JavaScript_files) label for the block. Shown in the block picker. +- `icon: string | WPElement` - Slug of the [Dashicon](https://developer.wordpress.org/resource/dashicons/#awards) to be shown in the control's button, or an element if you choose to render your own SVG. - `edit( attributes: Object, setAttributes: Function ): WPElement` - Returns an element describing the markup of a block to be shown in the editor. A block can update its own state in response to events using the `setAttributes` function, passing an object of properties to be applied as a partial update. - `save( attributes: Object ): WPElement` - Returns an element describing the markup of a block to be saved in the published content. This function is called before save and when switching to an editor's HTML view. - `tagName: string` - An alternative to defining `edit` and `save` behaviors, if passed a [tag name](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#Parameters), delegates default behavior of an editable field on that node type. Typically used for simple, text-heavy elements (paragraphs, headings) to avoid excess meta storage of text. From 4dde9a9a4ff27dce8f929c7f26d547dbefafc6d8 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 24 Mar 2017 13:22:27 -0400 Subject: [PATCH 6/7] Clarify expected encodeAttributes behavior --- blocks/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/README.md b/blocks/README.md index 42364b5ff4d27..6d0973c4062e7 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -129,7 +129,7 @@ Registers a new block provided a unique slug and an object defining its behavior - `save( attributes: Object ): WPElement` - Returns an element describing the markup of a block to be saved in the published content. This function is called before save and when switching to an editor's HTML view. - `tagName: string` - An alternative to defining `edit` and `save` behaviors, if passed a [tag name](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#Parameters), delegates default behavior of an editable field on that node type. Typically used for simple, text-heavy elements (paragraphs, headings) to avoid excess meta storage of text. - `controls: string[]` - Slugs for controls to be made available to block. See also: [`wp.blocks.registerControl`](#wpblocksregistercontrol-slug-string-settings-object-) -- `encodeAttributes( attributes: Object ): Object` - Called before save, this function allows you to control which attributes are to be saved in the block comment metadata. By default, all attribute values not defined in the block's `attributes` property are serialized to the comment metadata. +- `encodeAttributes( attributes: Object ): Object` - Called when save markup is generated, this function allows you to control which attributes are to be encoded in the block comment metadata. By default, all attribute values not defined in the block's `attributes` property are serialized to the comment metadata. If defined, this function should return the subset of attributes to encode, or `null` to bypass default behavior. ### `wp.blocks.registerControl( slug: string, settings: Object )` From 1058509dd36d573300e5ca9fb3247b6d8f0d61f1 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 24 Mar 2017 13:24:18 -0400 Subject: [PATCH 7/7] Remove tagName from supported block properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needed primarily as solution to excess meta storage. Could still be useful as convenience for core block types, but let’s revisit it when deemed necessary. --- blocks/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/blocks/README.md b/blocks/README.md index 6d0973c4062e7..f7d740219955b 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -127,7 +127,6 @@ Registers a new block provided a unique slug and an object defining its behavior - `icon: string | WPElement` - Slug of the [Dashicon](https://developer.wordpress.org/resource/dashicons/#awards) to be shown in the control's button, or an element if you choose to render your own SVG. - `edit( attributes: Object, setAttributes: Function ): WPElement` - Returns an element describing the markup of a block to be shown in the editor. A block can update its own state in response to events using the `setAttributes` function, passing an object of properties to be applied as a partial update. - `save( attributes: Object ): WPElement` - Returns an element describing the markup of a block to be saved in the published content. This function is called before save and when switching to an editor's HTML view. -- `tagName: string` - An alternative to defining `edit` and `save` behaviors, if passed a [tag name](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#Parameters), delegates default behavior of an editable field on that node type. Typically used for simple, text-heavy elements (paragraphs, headings) to avoid excess meta storage of text. - `controls: string[]` - Slugs for controls to be made available to block. See also: [`wp.blocks.registerControl`](#wpblocksregistercontrol-slug-string-settings-object-) - `encodeAttributes( attributes: Object ): Object` - Called when save markup is generated, this function allows you to control which attributes are to be encoded in the block comment metadata. By default, all attribute values not defined in the block's `attributes` property are serialized to the comment metadata. If defined, this function should return the subset of attributes to encode, or `null` to bypass default behavior.