diff --git a/blocks/api/parser.js b/blocks/api/parser.js index fa1a1ad201e49e..09ec75a80050e1 100644 --- a/blocks/api/parser.js +++ b/blocks/api/parser.js @@ -1,8 +1,8 @@ /** * External dependencies */ -import * as query from 'hpq'; -import { escape, unescape } from 'lodash'; +import { parse as hpqParse } from 'hpq'; +import { escape, unescape, pickBy } from 'lodash'; /** * Internal dependencies @@ -19,10 +19,19 @@ import { createBlock } from './factory'; * @return {Object} Block attributes */ export function parseBlockAttributes( rawContent, blockSettings ) { - if ( 'function' === typeof blockSettings.attributes ) { - return blockSettings.attributes( rawContent ); - } else if ( blockSettings.attributes ) { - return query.parse( rawContent, blockSettings.attributes ); + const { attributes } = blockSettings; + if ( 'function' === typeof attributes ) { + return attributes( rawContent ); + } else if ( attributes ) { + // Matchers are implemented as functions that receive a DOM node from + // which to select data. Use of the DOM is incidental and we shouldn't + // guarantee a contract that this be provided, else block implementers + // may feel compelled to use the node. Instead, matchers are intended + // as a generic interface to query data from any tree shape. Here we + // pick only matchers which include an internal flag. + const knownMatchers = pickBy( attributes, '_wpBlocksKnownMatcher' ); + + return hpqParse( rawContent, knownMatchers ); } return {}; diff --git a/blocks/api/query.js b/blocks/api/query.js index 559a9b67069b59..f9e7782660de96 100644 --- a/blocks/api/query.js +++ b/blocks/api/query.js @@ -2,10 +2,35 @@ * External dependencies */ import { nodeListToReact } from 'dom-react'; +import { flow } from 'lodash'; +import { + attr as originalAttr, + prop as originalProp, + html as originalHtml, + text as originalText, + query as originalQuery +} from 'hpq'; -export * from 'hpq'; +/** + * Given a matcher function creator, returns a new function which applies an + * internal flag to the created matcher. + * + * @param {Function} fn Original matcher function creator + * @return {Function} Modified matcher function creator + */ +function withKnownMatcherFlag( fn ) { + return flow( fn, ( matcher ) => { + matcher._wpBlocksKnownMatcher = true; + return matcher; + } ); +} -export function children( selector ) { +export const attr = withKnownMatcherFlag( originalAttr ); +export const prop = withKnownMatcherFlag( originalProp ); +export const html = withKnownMatcherFlag( originalHtml ); +export const text = withKnownMatcherFlag( originalText ); +export const query = withKnownMatcherFlag( originalQuery ); +export const children = withKnownMatcherFlag( ( selector ) => { return ( node ) => { let match = node; @@ -19,4 +44,4 @@ export function children( selector ) { return []; }; -} +} ); diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js index 99671d43cbf150..51c15bd6f92eca 100644 --- a/blocks/api/test/parser.js +++ b/blocks/api/test/parser.js @@ -2,11 +2,11 @@ * External dependencies */ import { expect } from 'chai'; -import { text } from 'hpq'; /** * Internal dependencies */ +import { text } from '../query'; import { getBlockAttributes, parseBlockAttributes, @@ -47,7 +47,8 @@ describe( 'block parser', () => { it( 'should use the query object implementation', () => { const blockSettings = { attributes: { - emphasis: text( 'strong' ) + emphasis: text( 'strong' ), + ignoredDomMatcher: ( node ) => node.innerHTML } }; diff --git a/blocks/api/test/query.js b/blocks/api/test/query.js index 4f1fa45472af62..e372cf1df66305 100644 --- a/blocks/api/test/query.js +++ b/blocks/api/test/query.js @@ -2,16 +2,23 @@ * External dependencies */ import { expect } from 'chai'; +import { parse } from 'hpq'; /** * Internal dependencies */ -import { parse, children } from '../query'; +import * as query from '../query'; describe( 'query', () => { + it( 'should generate matchers which apply internal flag', () => { + for ( const matcherFn in query ) { + expect( query[ matcherFn ]()._wpBlocksKnownMatcher ).to.be.true(); + } + } ); + describe( 'children()', () => { it( 'should return a matcher function', () => { - const matcher = children(); + const matcher = query.children(); expect( matcher ).to.be.a( 'function' ); } ); @@ -20,7 +27,7 @@ describe( 'query', () => { // Assumption here is that we can cleanly convert back and forth // between a string and WPElement representation const html = '

A delicious sundae dessert

'; - const match = parse( html, children() ); + const match = parse( html, query.children() ); expect( wp.element.renderToString( match ) ).to.equal( html ); } ); diff --git a/blocks/library/button/index.js b/blocks/library/button/index.js index fffb1a4eec4174..81917d246c88cd 100644 --- a/blocks/library/button/index.js +++ b/blocks/library/button/index.js @@ -32,8 +32,7 @@ registerBlock( 'core/button', { attributes: { url: attr( 'a', 'href' ), title: attr( 'a', 'title' ), - text: children( 'a' ), - align: ( node ) => ( node.className.match( /\balign(\S+)/ ) || [] )[ 1 ] + text: children( 'a' ) }, controls: [ diff --git a/blocks/library/image/index.js b/blocks/library/image/index.js index e63127968c5929..e8bdb9e4a276f8 100644 --- a/blocks/library/image/index.js +++ b/blocks/library/image/index.js @@ -34,8 +34,7 @@ registerBlock( 'core/image', { attributes: { url: attr( 'img', 'src' ), alt: attr( 'img', 'alt' ), - caption: children( 'figcaption' ), - align: ( node ) => ( node.className.match( /\balign(\S+)/ ) || [] )[ 1 ] + caption: children( 'figcaption' ) }, controls: [ diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index fc3d9c4f411c71..6c1cf716bd41ff 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -5,7 +5,7 @@ import './style.scss'; import { registerBlock, query as hpq } from 'api'; import Editable from 'components/editable'; -const { children, query, attr } = hpq; +const { children, query } = hpq; registerBlock( 'core/quote', { title: wp.i18n.__( 'Quote' ), @@ -14,26 +14,13 @@ registerBlock( 'core/quote', { attributes: { value: query( 'blockquote > p', children() ), - citation: children( 'footer' ), - style: ( node ) => { - const value = attr( 'blockquote', 'class' )( node ); - if ( ! value ) { - return; - } - - const match = value.match( /\bblocks-quote-style-(\d+)\b/ ); - if ( ! match ) { - return; - } - - return Number( match[ 1 ] ); - } + citation: children( 'footer' ) }, controls: [ 1, 2 ].map( ( variation ) => ( { icon: 'format-quote', title: wp.i18n.sprintf( wp.i18n.__( 'Quote style %d' ), variation ), - isActive: ( { style = 1 } ) => style === variation, + isActive: ( { style = 1 } ) => Number( style ) === variation, onClick( attributes, setAttributes ) { setAttributes( { style: variation } ); }, diff --git a/post-content.js b/post-content.js index 0c4980bbce2287..9ecdf7a7b6b407 100644 --- a/post-content.js +++ b/post-content.js @@ -29,7 +29,7 @@ window._wpGutenbergPost = { '

Handling images and media with the utmost care is a primary focus of the new editor. Hopefully you\'ll find aspects like adding captions or going full-width with your pictures much easier and robust than before.

', '', - '', + '', '

Give it a try. Press the "really wide" button on the image toolbar.

', '', @@ -57,7 +57,7 @@ window._wpGutenbergPost = { '

If you want to learn more about how to build additional blocks, or if you are interested in helping with the project, head over to the GitHub repository.

', '', - '', + '', '
Help build Gutenberg
', '', @@ -73,7 +73,7 @@ window._wpGutenbergPost = { '

A huge benefit of blocks is that you can edit them in place and manipulate you content directly. Instead of having fields for editing things like the source of a quote, or the text of a button, you can directly change the content. Try editing the following quote:

', '', - '', + '', '

The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.

', '', @@ -85,7 +85,7 @@ window._wpGutenbergPost = { '

Blocks can be anything you need. For instance, you may want to insert a subdued quote as part of the composition of your text, or you may prefer to display a giant stylized one. All of these options are available in the inserter.

', '', - '', + '', '

There is no greater agony than bearing an untold story inside you.

', '',