diff --git a/blocks/api/factory.js b/blocks/api/factory.js
index 004cfb115c35bf..ed1f03763e894b 100644
--- a/blocks/api/factory.js
+++ b/blocks/api/factory.js
@@ -19,18 +19,18 @@ import { getBlockType } from './registration';
/**
* Returns a block object given its type and attributes.
*
- * @param {String} name Block name
- * @param {Object} attributes Block attributes
- * @return {Object} Block object
+ * @param {String} name Block name
+ * @param {Object} blockAttributes Block attributes
+ * @return {Object} Block object
*/
-export function createBlock( name, attributes = {} ) {
+export function createBlock( name, blockAttributes = {} ) {
// Get the type definition associated with a registered block.
const blockType = getBlockType( name );
// Ensure attributes contains only values defined by block type, and merge
// default values for missing attributes.
- attributes = reduce( blockType.attributes, ( result, source, key ) => {
- const value = attributes[ key ];
+ const attributes = reduce( blockType.attributes, ( result, source, key ) => {
+ const value = blockAttributes[ key ];
if ( undefined !== value ) {
result[ key ] = value;
} else if ( source.default ) {
@@ -39,6 +39,9 @@ export function createBlock( name, attributes = {} ) {
return result;
}, {} );
+ if ( blockType.supportAnchor && blockAttributes.anchor ) {
+ attributes.anchor = blockAttributes.anchor;
+ }
// Blocks are stored with a unique ID, the assigned type name,
// and the block attributes.
diff --git a/blocks/api/parser.js b/blocks/api/parser.js
index c908414a775974..9c3ff3b513c183 100644
--- a/blocks/api/parser.js
+++ b/blocks/api/parser.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { parse as hpqParse } from 'hpq';
+import { parse as hpqParse, attr } from 'hpq';
import { mapValues, reduce, pickBy } from 'lodash';
/**
@@ -101,7 +101,7 @@ export function getBlockAttributes( blockType, rawContent, attributes ) {
blockType.attributes
);
- return reduce( blockType.attributes, ( result, source, key ) => {
+ const blockAttributes = reduce( blockType.attributes, ( result, source, key ) => {
let value;
if ( sourcedAttributes.hasOwnProperty( key ) ) {
value = sourcedAttributes[ key ];
@@ -146,6 +146,13 @@ export function getBlockAttributes( blockType, rawContent, attributes ) {
result[ key ] = coercedValue;
return result;
}, {} );
+
+ // If the block supports anchor, parse the id
+ if ( blockType.supportAnchor ) {
+ blockAttributes.anchor = hpqParse( rawContent, attr( '*', 'id' ) );
+ }
+
+ return blockAttributes;
}
/**
diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js
index c5aed9e25d3220..feb6ceae9c6d4b 100644
--- a/blocks/api/serializer.js
+++ b/blocks/api/serializer.js
@@ -50,20 +50,28 @@ export function getSaveContent( blockType, attributes ) {
}
// Adding a generic classname
- const addClassnameToElement = ( element ) => {
- if ( ! element || ! isObject( element ) || ! className ) {
+ const addAdvancedAttributes = ( element ) => {
+ if ( ! element || ! isObject( element ) ) {
return element;
}
- const updatedClassName = classnames(
- className,
- element.props.className,
- attributes.className
- );
+ const extraProps = {};
+ if ( !! className ) {
+ const updatedClassName = classnames(
+ className,
+ element.props.className,
+ attributes.className
+ );
+ extraProps.className = updatedClassName;
+ }
+
+ if ( blockType.supportAnchor && attributes.anchor ) {
+ extraProps.id = attributes.anchor;
+ }
- return cloneElement( element, { className: updatedClassName } );
+ return cloneElement( element, extraProps );
};
- const contentWithClassname = Children.map( rawContent, addClassnameToElement );
+ const contentWithClassname = Children.map( rawContent, addAdvancedAttributes );
// Otherwise, infer as element
return renderToString( contentWithClassname );
diff --git a/blocks/api/test/factory.js b/blocks/api/test/factory.js
index 8d1ea343252a88..a91450e5988830 100644
--- a/blocks/api/test/factory.js
+++ b/blocks/api/test/factory.js
@@ -54,6 +54,29 @@ describe( 'block factory', () => {
expect( block.isValid ).toBe( true );
expect( typeof block.uid ).toBe( 'string' );
} );
+
+ it( 'should keep the anchor if the block supports it', () => {
+ registerBlockType( 'core/test-block', {
+ attributes: {
+ align: {
+ type: 'string',
+ },
+ },
+ save: noop,
+ category: 'common',
+ supportAnchor: true,
+ } );
+ const block = createBlock( 'core/test-block', {
+ align: 'left',
+ anchor: 'chicken',
+ } );
+
+ expect( block.attributes ).toEqual( {
+ anchor: 'chicken',
+ align: 'left',
+ } );
+ expect( block.isValid ).toBe( true );
+ } );
} );
describe( 'switchToBlockType()', () => {
diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js
index 5445f57233a8cb..49fb5a82b18ba2 100644
--- a/blocks/api/test/parser.js
+++ b/blocks/api/test/parser.js
@@ -155,6 +155,26 @@ describe( 'block parser', () => {
topic: 'none',
} );
} );
+
+ it( 'should parse the anchor if the block supports it', () => {
+ const blockType = {
+ attributes: {
+ content: {
+ type: 'string',
+ source: text( 'div' ),
+ },
+ },
+ supportAnchor: true,
+ };
+
+ const rawContent = '
Ribs
';
+ const attrs = {};
+
+ expect( getBlockAttributes( blockType, rawContent, attrs ) ).toEqual( {
+ content: 'Ribs',
+ anchor: 'chicken',
+ } );
+ } );
} );
describe( 'createBlockWithFallback', () => {
diff --git a/blocks/api/test/serializer.js b/blocks/api/test/serializer.js
index 23c3d4b54faacc..9ddba1e639bb8e 100644
--- a/blocks/api/test/serializer.js
+++ b/blocks/api/test/serializer.js
@@ -112,6 +112,20 @@ describe( 'block serializer', () => {
expect( saved ).toBe( 'Bananas
' );
} );
+
+ it( 'should add an id if the block supports anchors', () => {
+ const saved = getSaveContent(
+ {
+ save: ( { attributes } ) => createElement( 'div', null, attributes.fruit ),
+ supportAnchor: true,
+ name: 'myplugin/fruit',
+ className: false,
+ },
+ { fruit: 'Bananas', anchor: 'my-fruit' }
+ );
+
+ expect( saved ).toBe( 'Bananas
' );
+ } );
} );
describe( 'component save', () => {
diff --git a/blocks/inspector-controls/base-control/index.js b/blocks/inspector-controls/base-control/index.js
index 6d671320fb915b..2e21c38f494546 100644
--- a/blocks/inspector-controls/base-control/index.js
+++ b/blocks/inspector-controls/base-control/index.js
@@ -8,11 +8,12 @@ import classnames from 'classnames';
*/
import './style.scss';
-function BaseControl( { id, label, className, children } ) {
+function BaseControl( { id, label, help, className, children } ) {
return (
{ label &&
}
{ children }
+ { !! help &&
{ help }
}
);
}
diff --git a/blocks/inspector-controls/base-control/style.scss b/blocks/inspector-controls/base-control/style.scss
index 80d89bc4036516..cf7f4c77014fc2 100644
--- a/blocks/inspector-controls/base-control/style.scss
+++ b/blocks/inspector-controls/base-control/style.scss
@@ -6,3 +6,7 @@
display: block;
margin-bottom: 5px;
}
+
+.blocks-base-control__help {
+ font-style: italic;
+}
diff --git a/blocks/inspector-controls/checkbox-control/index.js b/blocks/inspector-controls/checkbox-control/index.js
index 55ca47b2cd4b28..b05b44f709ee3d 100644
--- a/blocks/inspector-controls/checkbox-control/index.js
+++ b/blocks/inspector-controls/checkbox-control/index.js
@@ -9,12 +9,12 @@ import { withInstanceId } from '@wordpress/components';
import BaseControl from './../base-control';
import './style.scss';
-function CheckboxControl( { label, heading, checked, instanceId, onChange, ...props } ) {
+function CheckboxControl( { label, heading, checked, help, instanceId, onChange, ...props } ) {
const id = 'inspector-checkbox-control-' + instanceId;
const onChangeValue = ( event ) => onChange( event.target.value );
return (
-
+