diff --git a/blocks/api/index.js b/blocks/api/index.js index be8351e5ca643a..5d010d41932d7e 100644 --- a/blocks/api/index.js +++ b/blocks/api/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import * as query from 'hpq'; +import * as query from './query'; export { query }; export { createBlock, switchToBlockType } from './factory'; diff --git a/blocks/api/query.js b/blocks/api/query.js new file mode 100644 index 00000000000000..cc058dbd447389 --- /dev/null +++ b/blocks/api/query.js @@ -0,0 +1,13 @@ +/** + * External dependencies + */ +import { flow } from 'lodash'; +import { html } from 'hpq'; +import { Parser } from 'html-to-react'; + +export * from 'hpq'; + +const parser = new Parser(); +export function children( selector ) { + return flow( html( selector ), parser.parse ); +} diff --git a/blocks/api/test/query.js b/blocks/api/test/query.js new file mode 100644 index 00000000000000..4f1fa45472af62 --- /dev/null +++ b/blocks/api/test/query.js @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { expect } from 'chai'; + +/** + * Internal dependencies + */ +import { parse, children } from '../query'; + +describe( 'query', () => { + describe( 'children()', () => { + it( 'should return a matcher function', () => { + const matcher = children(); + + expect( matcher ).to.be.a( 'function' ); + } ); + + it( 'should return HTML equivalent WPElement of matched element', () => { + // Assumption here is that we can cleanly convert back and forth + // between a string and WPElement representation + const html = '
'; + const match = parse( html, children() ); + + expect( wp.element.renderToString( match ) ).to.equal( html ); + } ); + } ); +} ); diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js index ee315966e6e301..e3e37a39e03912 100644 --- a/blocks/components/editable/index.js +++ b/blocks/components/editable/index.js @@ -3,12 +3,15 @@ */ import classnames from 'classnames'; import { last } from 'lodash'; +import { Parser as HtmlToReactParser } from 'html-to-react'; /** * Internal dependencies */ import './style.scss'; +const htmlToReactParser = new HtmlToReactParser(); + export default class Editable extends wp.element.Component { constructor() { super( ...arguments ); @@ -50,8 +53,7 @@ export default class Editable extends wp.element.Component { } onInit() { - const { value = '' } = this.props; - this.editor.setContent( value ); + this.setContent( this.props.value ); this.focus(); } @@ -68,9 +70,9 @@ export default class Editable extends wp.element.Component { if ( ! this.editor.isDirty() ) { return; } - const value = this.editor.getContent(); + this.editor.save(); - this.props.onChange( value ); + this.props.onChange( this.getContent() ); } onNewBlock() { @@ -100,15 +102,21 @@ export default class Editable extends wp.element.Component { return; } const before = getHtml( beforeNodes.slice( 0, beforeNodes.length - 1 ) ); - const after = getHtml( childNodes.slice( splitIndex ) ); // Splitting into two blocks - this.editor.setContent( this.props.value || '' ); + this.setContent( this.props.value ); const hasAfter = !! childNodes.slice( splitIndex ) .reduce( ( memo, node ) => memo + node.textContent, '' ); + const after = hasAfter ? getHtml( childNodes.slice( splitIndex ) ) : ''; + // The setTimeout fixes the focus jump to the original block - setTimeout( () => this.props.onSplit( before, hasAfter ? after : '' ) ); + setTimeout( () => { + this.props.onSplit( + htmlToReactParser.parse( before ), + htmlToReactParser.parse( after ) + ); + } ); } bindNode( ref ) { @@ -117,13 +125,28 @@ export default class Editable extends wp.element.Component { updateContent() { const bookmark = this.editor.selection.getBookmark( 2, true ); - this.editor.setContent( this.props.value ); + this.setContent( this.props.value ); this.editor.selection.moveToBookmark( bookmark ); // Saving the editor on updates avoid unecessary onChanges calls // These calls can make the focus jump this.editor.save(); } + setContent( content ) { + if ( ! content ) { + content = ''; + } + + content = wp.element.renderToString( content ); + this.editor.setContent( content ); + } + + getContent() { + const content = this.editor.getContent( { format: 'raw' } ); + + return htmlToReactParser.parse( content ); + } + focus() { if ( this.props.focus ) { this.editor.focus(); diff --git a/blocks/library/embed/index.js b/blocks/library/embed/index.js index 1401b7ba542507..39473174323ecd 100644 --- a/blocks/library/embed/index.js +++ b/blocks/library/embed/index.js @@ -4,7 +4,7 @@ import { registerBlock, query } from 'api'; import Editable from 'components/editable'; -const { attr, html } = query; +const { attr, children } = query; registerBlock( 'core/embed', { title: wp.i18n.__( 'Embed' ), @@ -16,7 +16,7 @@ registerBlock( 'core/embed', { attributes: { url: attr( 'iframe', 'src' ), title: attr( 'iframe', 'title' ), - caption: html( 'figcaption' ) + caption: children( 'figcaption' ) }, edit( { attributes, isSelected, setAttributes } ) { @@ -47,7 +47,7 @@ registerBlock( 'core/embed', { return (A delicious sundae dessert
${ paragraph }
` ).join( '' ) : ''; -const fromParagraphsToValue = ( paragraphs ) => parse( paragraphs, query( 'p', html() ) ); +const { children, query, attr } = hpq; registerBlock( 'core/quote', { title: wp.i18n.__( 'Quote' ), @@ -16,13 +13,13 @@ registerBlock( 'core/quote', { category: 'common', attributes: { - value: query( 'blockquote > p', html() ), - citation: html( 'footer' ), + value: query( 'blockquote > p', children() ), + citation: children( 'footer' ), style: node => { const value = attr( 'blockquote', 'class' )( node ); const match = value.match( /\bblocks-quote-style-(\d+)\b/ ); return match ? +match[ 1 ] : null; - }, + } }, controls: [ 1, 2 ].map( ( variation ) => ( { @@ -42,29 +39,29 @@ registerBlock( 'core/quote', { return (); }, @@ -76,15 +73,9 @@ registerBlock( 'core/quote', { return (setAttributes( { - value: fromParagraphsToValue( paragraphs ) + ( nextValue ) => setAttributes( { + value: nextValue } ) } focus={ focus && focus.editable === 'value' ? focus : null } onFocus={ () => setFocus( { editable: 'value' } ) } /> - { ( citation || !! focus ) && + { ( citation || !! focus ) && ( - } + ) }
{ value && value.map( ( paragraph, i ) => ( - +); } diff --git a/blocks/library/text/index.js b/blocks/library/text/index.js index 007e7f4a099fe8..75c60a6c9d79a1 100644 --- a/blocks/library/text/index.js +++ b/blocks/library/text/index.js @@ -1,13 +1,10 @@ /** * Internal dependencies */ -import { registerBlock, query as hpq } from 'api'; +import { registerBlock, query } from 'api'; import Editable from 'components/editable'; -const { html, parse, query } = hpq; - -const fromValueToParagraphs = ( value ) => value ? value.map( ( paragraph ) => `{ paragraph }
) ) } - +
${ paragraph }
` ).join( '' ) : ''; -const fromParagraphsToValue = ( paragraphs ) => parse( paragraphs, query( 'p', html() ) ); +const { children } = query; registerBlock( 'core/text', { title: wp.i18n.__( 'Text' ), @@ -17,7 +14,7 @@ registerBlock( 'core/text', { category: 'common', attributes: { - content: query( 'p', html() ), + content: children(), }, controls: [ @@ -48,21 +45,23 @@ registerBlock( 'core/text', { ], edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus } ) { - const { content, align } = attributes; + const { content = , align } = attributes; return (I imagine prior to the launch of the iPod, or the iPhone, there were teams saying the same thing: the copy + paste guys are so close to being ready and we know Walt Mossberg is going to ding us for this so let\'s just not ship to the manufacturers in China for just a few more weeks… The Apple teams were probably embarrassed. But if you\'re not embarrassed when you ship your first version you waited too long.
I imagine prior to the launch of the iPod, or the iPhone, there were teams saying the same thing: the copy + paste guys are so close to being ready and we know Walt Mossberg is going to ding us for this so let\'s just not ship to the manufacturers in China for just a few more weeks… The Apple teams were probably embarrassed. But if you\'re not embarrassed when you ship your first version you waited too long.
', '', '', @@ -17,7 +17,8 @@ window._wpGutenbergPost = { '', '', - 'A beautiful thing about Apple is how quickly they obsolete their own products. I imagine this also makes the discipline of getting things out there easier. Like I mentioned before, the longer it’s been since the last release the more pressure there is, but if you know that if your bit of code doesn’t make this version but there’s the +0.1 coming out in 6 weeks, then it’s not that bad. It’s like flights from San Francisco to LA, if you miss one you know there’s another one an hour later so it’s not a big deal. Amazon has done a fantastic job of this with the Kindle as well, with a new model every year.
A beautiful thing about Apple is how quickly they obsolete their own products. I imagine this also makes the discipline of getting things out there easier. Like I mentioned before, the longer it’s been since the last release the more pressure there is, but if you know that if your bit of code doesn’t make this version but there’s the +0.1 coming out in 6 weeks, then it’s not that bad.
', + 'It’s like flights from San Francisco to LA, if you miss one you know there’s another one an hour later so it’s not a big deal. Amazon has done a fantastic job of this with the Kindle as well, with a new model every year.
', '', '', @@ -29,7 +30,7 @@ window._wpGutenbergPost = { '', '', - 'By shipping early and often you have the unique competitive advantage of hearing from real people what they think of your work, which in best case helps you anticipate market direction, and in worst case gives you a few people rooting for you that you can email when your team pivots to a new idea. Nothing can recreate the crucible of real usage.
By shipping early and often you have the unique competitive advantage of hearing from real people what they think of your work, which in best case helps you anticipate market direction, and in worst case gives you a few people rooting for you that you can email when your team pivots to a new idea. Nothing can recreate the crucible of real usage.
', '', '',