Skip to content

Commit

Permalink
Block API: Add hook for deprecated raw HTML support
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Feb 1, 2018
1 parent f3f5a9f commit 687af56
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
2 changes: 2 additions & 0 deletions blocks/api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export function getSaveElement( blockType, attributes ) {
saveElement = save( { attributes } );
}

saveElement = applyFilters( 'blocks.getSaveContent.saveElement', saveElement, blockType, attributes );

const addExtraContainerProps = ( element ) => {
if ( ! element || ! isObject( element ) ) {
return element;
Expand Down
54 changes: 54 additions & 0 deletions blocks/hooks/deprecated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { includes } from 'lodash';

/**
* WordPress dependencies
*/
import { Component, DangerousHTML } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';

class DangerousHTMLWithWarning extends Component {
constructor() {
super( ...arguments );

// Disable reason: We're intentionally logging a console warning
// advising the developer to upgrade usage.

// eslint-disable-next-line no-console
console.warn(
'Deprecated: String returns from block `save` is unsupported. ' +
'Use `wp.element.DangerousHTML` component instead.\n\n' +
'See: https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/#save'
);
}

render() {
const { children } = this.props;

return <DangerousHTML>{ children }</DangerousHTML>;
}
}

/**
* Override save element for a block, providing support for deprecated HTML
* return value, logging a warning advising the developer to use the preferred
* DangerousHTML component instead.
*
* @param {(string|Object|Array)} element Original block save return.
*
* @returns {(string|Object|Array)} Dangerously shimmed block save.
*/
export function shimDangerousHTML( element ) {
// Still support string return from save, but in the same way any component
// could render a string, it should be escaped. Therefore, only shim usage
// which had included some HTML expected to be unescaped.
if ( typeof element === 'string' && includes( '<', element ) ) {
element = <DangerousHTMLWithWarning children={ element } />;
}

return element;
}

addFilter( 'blocks.getSaveContent.saveElement', 'core/deprecated/save-props', shimDangerousHTML );
1 change: 1 addition & 0 deletions blocks/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
*/
import './anchor';
import './custom-class-name';
import './deprecated';
import './generated-class-name';
import './matchers';
32 changes: 27 additions & 5 deletions docs/block-edit-save.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,46 @@ edit( { attributes, setAttributes, className, focus } ) {

The `save` function defines the way in which the different attributes should be combined into the final markup, which is then serialized by Gutenberg into `post_content`.

{% codetabs %}
{% ES5 %}
```js
// Defining the save interface
save() {
return wp.element.createElement( 'hr' );
}
```
{% ESNext %}
```jsx
save() {
return <hr />;
}
```
{% end %}

For most blocks, the return value of `save` should be an [instance of WordPress Element](https://github.com/WordPress/gutenberg/blob/master/element/README.md) representing how the block is to appear on the front of the site.

This function can return a `null` value, in which case the block is considered to be _dynamic_—that means that only an HTML comment with attributes is serialized and the server has to provide the render function. (This is the equivalent to purely dynamic shortcodes, with the advantage that the grammar parsing it is assertive and they can remain invisible in contexts that are unable to compute them on the server, instead of showing gibberish as text.)
_Note:_ While it is possible to return a string value from `save`, it _will be escaped_. If the string includes HTML markup, the markup will be shown on the front of the site verbatim, not as the equivalent HTML node content. If you must return raw HTML from `save`, use `wp.element.DangerousHTML`. As the name implies, this is prone to [cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting) and therefore is discouraged in favor of a WordPress Element hierarchy whenever possible.

`save` can also receive properties.
For [dynamic blocks](https://wordpress.org/gutenberg/handbook/blocks/creating-dynamic-blocks/), the return value of `save` could either represent a cached copy of the block's content to be shown only in case the plugin implementing the block is ever disabled. Alternatively, return a `null` (empty) value to save no markup in post content for the dynamic block, instead deferring this to always be calculated when the block is shown on the front of the site.

### attributes

It operates the same way as it does on `edit` and allows to save certain attributes directly to the markup, so they don't have to be computed on the server. This is how most _static_ blocks are expected to work.
As with `edit`, the `save` function also receives an object argument including attributes which can be inserted into the markup.

{% codetabs %}
{% ES5 %}
```js
// Defining the edit interface
save( props ) {
return wp.element.createElement(
'div',
null,
props.attributes.content
);
}
```
{% ESNext %}
```jsx
save( { attributes } ) {
return <div>{ attributes.content }</div>;
}
```
{% end %}

0 comments on commit 687af56

Please sign in to comment.