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

Blocks API: Add default implementation for save setting #14510

Merged
merged 2 commits into from
Mar 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ For most blocks, the return value of `save` should be an [instance of WordPress

_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.RawHTML`. 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.

For [dynamic blocks](/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md), 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.
For [dynamic blocks](/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md), the return value of `save` could represent a cached copy of the block's content to be shown only in case the plugin implementing the block is ever disabled.

If left unspecified, the default implementation will 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ registerBlockType( 'my-plugin/latest-post', {
post.title.rendered
);
} ),

save: function() {
// Rendering in PHP
return null;
},
} );
```
{% ESNext %}
Expand Down Expand Up @@ -79,16 +74,11 @@ registerBlockType( 'my-plugin/latest-post', {
{ post.title.rendered }
</a>;
} ),

save() {
// Rendering in PHP
return null;
},
} );
```
{% end %}

Because it is a dynamic block it also needs a server component. The rendering can be added using the `render_callback` property when using the `register_block_type` function.
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 rendering can be added using the `render_callback` property when using the `register_block_type` function.

```php
<?php
Expand Down Expand Up @@ -118,8 +108,8 @@ register_block_type( 'my-plugin/latest-post', array(

There are a few things to notice:

* The edit function still shows a representation of the block in the editor's context (this could be very different from the rendered version, it's up to the block's author)
* The save function just returns null because the rendering is performed server-side.
* The `edit` function still shows a representation of the block in the editor's context (this could be very different from the rendered version, it's up to the block's author)
* The built-in `save` function just returns `null` because the rendering is performed server-side.
* The server-side rendering is a function taking the block attributes and the block inner content as arguments, and returning the markup (quite similar to shortcodes)

## Live rendering in Gutenberg editor
Expand Down Expand Up @@ -151,11 +141,6 @@ registerBlockType( 'my-plugin/latest-post', {
})
);
},

save: function() {
// Rendering in PHP
return null;
},
} );
```
{% ESNext %}
Expand All @@ -179,11 +164,6 @@ registerBlockType( 'my-plugin/latest-post', {
/>
);
},

save() {
// Rendering in PHP
return null;
},
} );
```
{% end %}
Expand Down
5 changes: 0 additions & 5 deletions packages/block-library/src/archives/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,4 @@ export const settings = {
},

edit,

save() {
// Handled by PHP.
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/calendar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,4 @@ export const settings = {
},

edit,

save() {
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/categories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,4 @@ export const settings = {
},

edit,

save() {
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/latest-comments/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,4 @@ export const settings = {
},

edit,

save() {
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/latest-posts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,4 @@ export const settings = {
},

edit,

save() {
return null;
},
};
5 changes: 0 additions & 5 deletions packages/block-library/src/legacy-widget/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,4 @@ export const settings = {
},

edit,

save() {
// Handled by PHP.
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/rss/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,4 @@ export const settings = {
},

edit,

save() {
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/search/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,4 @@ export const settings = {
keywords: [ __( 'find' ) ],

edit,

save() {
return null;
},
};
4 changes: 0 additions & 4 deletions packages/block-library/src/tag-cloud/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,4 @@ export const settings = {
},

edit,

save() {
return null;
},
};
3 changes: 2 additions & 1 deletion packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { isValidIcon, normalizeIconObject } from './utils';
* @property {?string[]} keywords Additional keywords to produce
* block as inserter search result.
* @property {?Object} attributes Block attributes.
* @property {Function} save Serialize behavior of a block,
* @property {?Function} save Serialize behavior of a block,
* returning an element describing
* structure of the block's post
* content markup.
Expand Down Expand Up @@ -66,6 +66,7 @@ export function unstable__bootstrapServerSideBlockDefinitions( definitions ) { /
export function registerBlockType( name, settings ) {
settings = {
name,
save: () => null,
...get( serverSideBlockDefinitions, name ),
...settings,
};
Expand Down
21 changes: 12 additions & 9 deletions packages/blocks/src/api/test/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
/**
* External dependencies
*/
import { noop } from 'lodash';
import { noop, omit } from 'lodash';

/**
* WordPress dependencies
*/
import { addFilter, removeFilter } from '@wordpress/hooks';
import { addFilter, removeAllFilters } from '@wordpress/hooks';

/**
* Internal dependencies
Expand Down Expand Up @@ -106,12 +106,6 @@ describe( 'blocks', () => {
expect( block ).toBeUndefined();
} );

it( 'should reject blocks without a save function', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively (or even in place of the "filtered" case below), it could test that save was passed as a non-function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I will add it as well.

Copy link
Member Author

@gziolo gziolo Mar 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why I used the approach with a filter was the fact that the existing check verifies if this is still an object after filtering. Maybe I should add another validation and error messaging to check that in test instead and update the removed test as you suggested.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured out it's going to be easier to open a follow-up which improves error handling together with updates to unit tests: #14529.

const block = registerBlockType( 'my-plugin/fancy-block-5' );
expect( console ).toHaveErroredWith( 'The "save" property must be specified and must be a valid function.' );
expect( block ).toBeUndefined();
} );

it( 'should reject blocks with an invalid edit function', () => {
const blockType = { save: noop, edit: 'not-a-function', category: 'common', title: 'block title' },
block = registerBlockType( 'my-plugin/fancy-block-6', blockType );
Expand Down Expand Up @@ -309,7 +303,7 @@ describe( 'blocks', () => {

describe( 'applyFilters', () => {
afterEach( () => {
removeFilter( 'blocks.registerBlockType', 'core/blocks/without-title' );
removeAllFilters( 'blocks.registerBlockType' );
} );

it( 'should reject valid blocks when they become invalid after executing filter', () => {
Expand All @@ -323,6 +317,15 @@ describe( 'blocks', () => {
expect( console ).toHaveErroredWith( 'The block "my-plugin/fancy-block-12" must have a title.' );
expect( block ).toBeUndefined();
} );

it( 'should reject valid blocks when they become invalid after executing filter which removes save property', () => {
addFilter( 'blocks.registerBlockType', 'core/blocks/without-save', ( settings ) => {
return omit( settings, 'save' );
} );
const block = registerBlockType( 'my-plugin/fancy-block-13', defaultBlockSettings );
expect( console ).toHaveErroredWith( 'The "save" property must be specified and must be a valid function.' );
expect( block ).toBeUndefined();
} );
} );
} );

Expand Down