diff --git a/blocks/inner-blocks/index.js b/blocks/inner-blocks/index.js
index 37ae97e13c71a7..4d25e0bd4b28fe 100644
--- a/blocks/inner-blocks/index.js
+++ b/blocks/inner-blocks/index.js
@@ -4,13 +4,13 @@
import { withContext } from '@wordpress/components';
function InnerBlocks( { BlockList, layouts } ) {
- return
{ children }
} ) ), - citation: ( values.length === 1 ? undefined : [ get( last( values ), 'props.children' ) ] ), - } ); - }, - }, ], }, @@ -230,7 +203,6 @@ export const settings = { const { attributes, insertBlocksAfter, - setAttributes, mergeBlocks, onReplace, className, @@ -278,7 +250,7 @@ export const settings = { onMerge={ mergeBlocks } onSplit={ insertBlocksAfter ? - ( before, after, ...blocks ) => { + ( unused, after, ...blocks ) => { if ( ! blocks.length ) { blocks.push( createBlock( 'core/paragraph' ) ); } @@ -290,7 +262,6 @@ export const settings = { } ) ); } - setAttributes( { values: before } ); insertBlocksAfter( blocks ); } : undefined diff --git a/blocks/library/paragraph/index.js b/blocks/library/paragraph/index.js index f776299fda0bde..71ae29fed9733e 100644 --- a/blocks/library/paragraph/index.js +++ b/blocks/library/paragraph/index.js @@ -246,8 +246,7 @@ class ParagraphBlock extends Component { } ); } } onSplit={ insertBlocksAfter ? - ( before, after, ...blocks ) => { - setAttributes( { content: before } ); + ( unused, after, ...blocks ) => { insertBlocksAfter( [ ...blocks, createBlock( 'core/paragraph', { content: after } ), diff --git a/blocks/library/pullquote/index.js b/blocks/library/pullquote/index.js index d0169d59b8ca6f..c53c6724ef6715 100644 --- a/blocks/library/pullquote/index.js +++ b/blocks/library/pullquote/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { map } from 'lodash'; +import { castArray } from 'lodash'; /** * WordPress dependencies @@ -14,25 +14,13 @@ import { withState } from '@wordpress/components'; */ import './editor.scss'; import './style.scss'; +import { createBlock } from '../../api'; import RichText from '../../rich-text'; import BlockControls from '../../block-controls'; import BlockAlignmentToolbar from '../../block-alignment-toolbar'; +import InnerBlocks from '../../inner-blocks'; -const toRichTextValue = value => map( value, ( subValue => subValue.children ) ); -const fromRichTextValue = value => map( value, ( subValue ) => ( { - children: subValue, -} ) ); const blockAttributes = { - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - }, citation: { type: 'array', source: 'children', @@ -68,7 +56,7 @@ export const settings = { edit: withState( { editable: 'content', } )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => { - const { value, citation, align } = attributes; + const { citation, align } = attributes; const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } ); const onSetActiveEditable = ( newEditable ) => () => setState( { editable: newEditable } ); @@ -82,20 +70,7 @@ export const settings = { ),-diff --git a/blocks/test/fixtures/core__pullquote__multi-paragraph.html b/blocks/test/fixtures/core__pullquote__multi-paragraph.html index 113d829d01329c..4c6800b8f1e50c 100644 --- a/blocks/test/fixtures/core__pullquote__multi-paragraph.html +++ b/blocks/test/fixtures/core__pullquote__multi-paragraph.html @@ -1,7 +1,11 @@setAttributes( { - value: fromRichTextValue( nextValue ), - } ) - } - /* translators: the text of the quotation */ - placeholder={ __( 'Write quote…' ) } - wrapperClassName="blocks-pullquote__content" - isSelected={ isSelected && editable === 'content' } - onFocus={ onSetActiveEditable( 'content' ) } - /> + { ( citation || isSelected ) && ( - { value && value.map( ( paragraph, i ) => - { paragraph.children && paragraph.children.props.children }
- ) } +{ citation && citation.length > 0 && ( { citation } ) } @@ -133,6 +106,56 @@ export const settings = { deprecated: [ { attributes: { ...blockAttributes, + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + }, + }, + + migrate( { value = [], ...attributes } ) { + return [ + attributes, + value.map( ( { children: paragraph } ) => + createBlock( 'core/paragraph', { + content: castArray( paragraph.props.children ), + } ) + ), + ]; + }, + + save( { attributes } ) { + const { value, citation, align } = attributes; + + return ( + + { value && value.map( ( paragraph, i ) => ++ ); + }, + }, { + attributes: { + ...blockAttributes, + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + }, citation: { type: 'array', source: 'children', diff --git a/blocks/library/pullquote/test/__snapshots__/index.js.snap b/blocks/library/pullquote/test/__snapshots__/index.js.snap index f8bdb27de8f502..cb551f1f951010 100644 --- a/blocks/library/pullquote/test/__snapshots__/index.js.snap +++ b/blocks/library/pullquote/test/__snapshots__/index.js.snap @@ -3,35 +3,5 @@ exports[`core/pullquote block edit matches snapshot 1`] = `{ paragraph.children && paragraph.children.props.children }
+ ) } + { citation && citation.length > 0 && ( + { citation } + ) } +-+/> `; diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index 98371b07561b81..ef0c5ba7eda8d3 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -1,8 +1,8 @@ /** * External dependencies */ -import { castArray, get, isString } from 'lodash'; import classnames from 'classnames'; +import { castArray } from 'lodash'; /** * WordPress dependencies @@ -15,28 +15,13 @@ import { Toolbar, withState } from '@wordpress/components'; */ import './style.scss'; import './editor.scss'; -import { createBlock } from '../../api'; +import { createBlock, rawHandler } from '../../api'; import AlignmentToolbar from '../../alignment-toolbar'; import BlockControls from '../../block-controls'; import RichText from '../../rich-text'; - -const toRichTextValue = value => value.map( ( subValue => subValue.children ) ); -const fromRichTextValue = value => value.map( ( subValue ) => ( { - children: subValue, -} ) ); +import InnerBlocks from '../../inner-blocks'; const blockAttributes = { - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - default: [], - }, citation: { type: 'array', source: 'children', @@ -63,100 +48,28 @@ export const settings = { transforms: { from: [ - { + ...[ 'core/paragraph', 'core/heading' ].map( ( fromName ) => ( { type: 'block', - blocks: [ 'core/paragraph' ], - transform: ( { content } ) => { - return createBlock( 'core/quote', { - value: [ - { children:------- ----- Write quote… -
-{ content }
}, - ], - } ); - }, - }, - { - type: 'block', - blocks: [ 'core/heading' ], - transform: ( { content } ) => { - return createBlock( 'core/quote', { - value: [ - { children:{ content }
}, - ], - } ); - }, - }, + blocks: [ fromName ], + transform: ( attributes ) => createBlock( name, {}, [ + createBlock( fromName, attributes ), + ] ), + } ) ), { type: 'pattern', regExp: /^>\s/, - transform: ( { content } ) => { - return createBlock( 'core/quote', { - value: [ - { children:{ content }
}, - ], - } ); - }, + transform: ( attributes ) => createBlock( name, {}, [ + createBlock( 'core/paragraph', attributes ), + ] ), }, { type: 'raw', isMatch: ( node ) => node.nodeName === 'BLOCKQUOTE', - }, - ], - to: [ - { - type: 'block', - blocks: [ 'core/paragraph' ], - transform: ( { value, citation } ) => { - // transforming an empty quote - if ( ( ! value || ! value.length ) && ! citation ) { - return createBlock( 'core/paragraph' ); - } - // transforming a quote with content - return ( value || [] ).map( item => createBlock( 'core/paragraph', { - content: [ get( item, 'children.props.children', '' ) ], - } ) ).concat( citation ? createBlock( 'core/paragraph', { - content: citation, - } ) : [] ); - }, - }, - { - type: 'block', - blocks: [ 'core/heading' ], - transform: ( { value, citation, ...attrs } ) => { - // if no text content exist just transform the quote into an heading block - // using citation as the content, it may be empty creating an empty heading block. - if ( ( ! value || ! value.length ) ) { - return createBlock( 'core/heading', { - content: citation, - } ); - } - - const firstValue = get( value, [ 0, 'children' ] ); - const headingContent = castArray( isString( firstValue ) ? - firstValue : - get( firstValue, [ 'props', 'children' ], '' ) - ); - - // if the quote content just contains a paragraph and no citation exist - // convert the quote content into and heading block. - if ( ! citation && value.length === 1 ) { - return createBlock( 'core/heading', { - content: headingContent, - } ); - } - - // In the normal case convert the first paragraph of quote into an heading - // and create a new quote block equal tl what we had excluding the first paragraph - const heading = createBlock( 'core/heading', { - content: headingContent, - } ); - - const quote = createBlock( 'core/quote', { - ...attrs, - citation, - value: value.slice( 1 ), - } ); - - return [ heading, quote ]; + transform( node ) { + return createBlock( name, {}, rawHandler( { + HTML: node.innerHTML, + mode: 'BLOCKS', + } ) ); }, }, ], @@ -164,8 +77,8 @@ export const settings = { edit: withState( { editable: 'content', - } )( ( { attributes, setAttributes, isSelected, mergeBlocks, onReplace, className, editable, setState } ) => { - const { align, value, citation, style } = attributes; + } )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => { + const { align, citation, style } = attributes; const containerClassname = classnames( className, style === 2 ? 'is-large' : '' ); const onSetActiveEditable = ( newEditable ) => () => { setState( { editable: newEditable } ); @@ -195,26 +108,7 @@ export const settings = { className={ containerClassname } style={ { textAlign: align } } > -setAttributes( { - value: fromRichTextValue( nextValue ), - } ) - } - onMerge={ mergeBlocks } - onRemove={ ( forward ) => { - const hasEmptyCitation = ! citation || citation.length === 0; - if ( ! forward && hasEmptyCitation ) { - onReplace( [] ); - } - } } - /* translators: the text of the quotation */ - placeholder={ __( 'Write quote…' ) } - isSelected={ isSelected && editable === 'content' } - onFocus={ onSetActiveEditable( 'content' ) } - /> + { ( ( citation && citation.length > 0 ) || isSelected ) && ( - { value.map( ( paragraph, i ) => ( - { paragraph.children && paragraph.children.props.children }
- ) ) } +{ citation && citation.length > 0 && ( { citation } ) } @@ -256,6 +148,62 @@ export const settings = { { attributes: { ...blockAttributes, + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + default: [], + }, + }, + + migrate( { value = [], ...attributes } ) { + return [ + attributes, + value.map( ( { children: paragraph } ) => + createBlock( 'core/paragraph', { + content: castArray( paragraph.props.children ), + } ) + ), + ]; + }, + + save( { attributes } ) { + const { align, value, citation, style } = attributes; + + return ( + + { value.map( ( paragraph, i ) => ( ++ ); + }, + }, + { + attributes: { + ...blockAttributes, + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + default: [], + }, citation: { type: 'array', source: 'children', diff --git a/blocks/library/quote/test/__snapshots__/index.js.snap b/blocks/library/quote/test/__snapshots__/index.js.snap index 010e122e054ab7..dd11350eb8d886 100644 --- a/blocks/library/quote/test/__snapshots__/index.js.snap +++ b/blocks/library/quote/test/__snapshots__/index.js.snap @@ -3,35 +3,5 @@ exports[`core/quote block edit matches snapshot 1`] = `{ paragraph.children && paragraph.children.props.children }
+ ) ) } + { citation && citation.length > 0 && ( + { citation } + ) } +-+/> `; diff --git a/blocks/rich-text/README.md b/blocks/rich-text/README.md index 74db6896146a39..7464dd1c6d3739 100644 --- a/blocks/rich-text/README.md +++ b/blocks/rich-text/README.md @@ -33,7 +33,7 @@ a traditional `input` field, usually when the user exits the field. ### `onSplit( before: Array, after: Array, ...blocks: Object ): Function` -*Optional.* Called when the content can be split with `before` and `after`. There might be blocks present, which should be inserted in between. +*Optional.* Called when the content can be split with `after` as the split off value. There might be blocks present, which should be inserted before the `after` value. Note: the `before` value should no longer be used. ### `onReplace( blocks: Array ): Function` diff --git a/blocks/rich-text/index.js b/blocks/rich-text/index.js index e4867d969a82f7..a7b1d0b456e8c7 100644 --- a/blocks/rich-text/index.js +++ b/blocks/rich-text/index.js @@ -39,6 +39,11 @@ import patterns from './patterns'; import { EVENTS } from './constants'; import { withBlockEditContext } from '../block-edit/context'; +/** + * Browser dependencies + */ +const { console } = window; + const { BACKSPACE, DELETE, ENTER } = keycodes; export function createTinyMCEElement( type, props, ...children ) { @@ -150,6 +155,7 @@ export class RichText extends Component { }; this.isEmpty = ! value || ! value.length; + this.savedContent = value; } /** @@ -301,7 +307,7 @@ export class RichText extends Component { } ); // Allows us to ask for this information when we get a report. - window.console.log( 'Received item:\n\n', blob ); + console.log( 'Received item:\n\n', blob ); if ( isEmpty && this.props.onReplace ) { // Necessary to allow the paste bin to be removed without errors. @@ -332,8 +338,8 @@ export class RichText extends Component { onPastePreProcess( event ) { const HTML = this.isPlainTextPaste ? this.pastedPlainText : event.content; // Allows us to ask for this information when we get a report. - window.console.log( 'Received HTML:\n\n', HTML ); - window.console.log( 'Received plain text:\n\n', this.pastedPlainText ); + console.log( 'Received HTML:\n\n', HTML ); + console.log( 'Received plain text:\n\n', this.pastedPlainText ); // There is a selection, check if a link is pasted. if ( ! this.editor.selection.isCollapsed() ) { @@ -348,7 +354,7 @@ export class RichText extends Component { } ); // Allows us to ask for this information when we get a report. - window.console.log( 'Created link:\n\n', pastedText ); + console.log( 'Created link:\n\n', pastedText ); event.preventDefault(); @@ -514,6 +520,10 @@ export class RichText extends Component { if ( event.shiftKey || ! this.props.onSplit ) { this.editor.execCommand( 'InsertLineBreak', false, event ); } else { + // Splitting the content might destroy the editor, so it's + // important that we stop other handlers (e.g. ones + // registered by TinyMCE) from also handling this event. + event.stopImmediatePropagation(); this.splitContent(); } } @@ -645,9 +655,6 @@ export class RichText extends Component { return memo; }, [] ); - // Splitting into two blocks - this.setContent( this.props.value ); - this.restoreContentAndSplit( nodeListToReact( before, createTinyMCEElement ), nodeListToReact( after, createTinyMCEElement ) @@ -716,15 +723,16 @@ export class RichText extends Component { !! this.editor && this.props.tagName === prevProps.tagName && this.props.value !== prevProps.value && - this.props.value !== this.savedContent && - - // Comparing using isEqual is necessary especially to avoid unnecessary updateContent calls - // This fixes issues in multi richText blocks like quotes when moving the focus between - // the different editables. - ! isEqual( this.props.value, prevProps.value ) && - ! isEqual( this.props.value, this.savedContent ) + this.props.value !== this.savedContent ) { this.updateContent(); + + if ( + 'development' === process.env.NODE_ENV && + isEqual( this.props.value, prevProps.value ) + ) { + console.warn( 'The current and previous value props are not strictly equal but the contents are the same. Please ensure the value prop reference does not change.' ); + } } } @@ -780,14 +788,15 @@ export class RichText extends Component { /** * Calling onSplit means we need to abort the change done by TinyMCE. - * we need to call updateContent to restore the initial content before calling onSplit. + * we need to call setContent to restore the initial content before calling onSplit. * * @param {Array} before content before the split position * @param {Array} after content after the split position * @param {?Array} blocks blocks to insert at the split position */ restoreContentAndSplit( before, after, blocks = [] ) { - this.updateContent(); + this.setContent( before ); + this.onChange(); this.props.onSplit( before, after, ...blocks ); } diff --git a/blocks/test/fixtures/core__pullquote.html b/blocks/test/fixtures/core__pullquote.html index 06d1ea9ab61144..87941125770945 100644 --- a/blocks/test/fixtures/core__pullquote.html +++ b/blocks/test/fixtures/core__pullquote.html @@ -1,5 +1,8 @@------- ----- Write quote… -
--diff --git a/blocks/test/fixtures/core__pullquote.json b/blocks/test/fixtures/core__pullquote.json index e044ae447f65f8..0b477b21e70159 100644 --- a/blocks/test/fixtures/core__pullquote.json +++ b/blocks/test/fixtures/core__pullquote.json @@ -4,26 +4,26 @@ "name": "core/pullquote", "isValid": true, "attributes": { - "value": [ - { - "children": { - "type": "p", - "key": null, - "ref": null, - "props": { - "children": "Testing pullquote block..." - }, - "_owner": null, - "_store": {} - } - } - ], "citation": [ "...with a caption" ], "align": "none" }, - "innerBlocks": [], - "originalContent": "Testing pullquote block...
...with a caption + +Testing pullquote block...
+ + ...with a caption\n" + "innerBlocks": [ + { + "name": "core/paragraph", + "uid": "_uid_0", + "isValid": true, + "attributes": { + "content": [ + "Testing pullquote block..." + ], + "dropCap": false + }, + "innerBlocks": [], + "originalContent": "Testing pullquote block...
...with a caption\nTesting pullquote block...
" + } + ], + "originalContent": "\n\t\n\t...with a caption\n" } ] diff --git a/blocks/test/fixtures/core__pullquote.parsed.json b/blocks/test/fixtures/core__pullquote.parsed.json index 4a96da0d5574ba..248585392f75d6 100644 --- a/blocks/test/fixtures/core__pullquote.parsed.json +++ b/blocks/test/fixtures/core__pullquote.parsed.json @@ -2,8 +2,15 @@ { "blockName": "core/pullquote", "attrs": null, - "innerBlocks": [], - "innerHTML": "\n\n\n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n\tTesting pullquote block...
...with a caption\nTesting pullquote block...
\n\t" + } + ], + "innerHTML": "\n\n\t\n\t...with a caption\n\n" }, { "attrs": {}, diff --git a/blocks/test/fixtures/core__pullquote.serialized.html b/blocks/test/fixtures/core__pullquote.serialized.html index 6aa8fe7d115ec2..41392a1b203561 100644 --- a/blocks/test/fixtures/core__pullquote.serialized.html +++ b/blocks/test/fixtures/core__pullquote.serialized.html @@ -1,4 +1,6 @@-+ +Testing pullquote block...
...with a captionTesting pullquote block...
+ ...with a caption
+diff --git a/blocks/test/fixtures/core__pullquote__multi-paragraph.json b/blocks/test/fixtures/core__pullquote__multi-paragraph.json index c2a6c0d770de73..ac24bc35d1500a 100644 --- a/blocks/test/fixtures/core__pullquote__multi-paragraph.json +++ b/blocks/test/fixtures/core__pullquote__multi-paragraph.json @@ -4,50 +4,43 @@ "name": "core/pullquote", "isValid": true, "attributes": { - "value": [ - { - "children": { - "type": "p", - "key": null, - "ref": null, - "props": { - "children": [ - "Paragraph ", - { - "type": "strong", - "key": "_domReact71", - "ref": null, - "props": { - "children": "one" - }, - "_owner": null, - "_store": {} - } - ] - }, - "_owner": null, - "_store": {} - } - }, - { - "children": { - "type": "p", - "key": null, - "ref": null, - "props": { - "children": "Paragraph two" - }, - "_owner": null, - "_store": {} - } - } - ], "citation": [ "by whomever" ], "align": "none" }, - "innerBlocks": [], - "originalContent": "Paragraph one
+ +Paragraph two
+ by whomever
\n" + "innerBlocks": [ + { + "uid": "_uid_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "content": [ + "Paragraph ", + { + "type": "strong", + "children": "one" + } + ], + "dropCap": false + }, + "innerBlocks": [], + "originalContent": "Paragraph one
\nParagraph two
\n by whomever\n
Paragraph one
" + }, + { + "uid": "_uid_1", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "content": [ + "Paragraph two" + ], + "dropCap": false + }, + "innerBlocks": [], + "originalContent": "Paragraph two
" + } + ], + "originalContent": "\n\t\n \n by whomever\n" } ] diff --git a/blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json b/blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json index 4821344abd80aa..85048516b27971 100644 --- a/blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json +++ b/blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json @@ -2,8 +2,21 @@ { "blockName": "core/pullquote", "attrs": null, - "innerBlocks": [], - "innerHTML": "\n
\n\n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\nParagraph one
\nParagraph two
\n by whomever\n
Paragraph one
\n " + }, + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\nParagraph two
\n " + } + ], + "innerHTML": "\n\n\t\n \n by whomever\n\n" }, { "attrs": {}, diff --git a/blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html b/blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html index e250273ded2b75..0c148cbe82efdd 100644 --- a/blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html +++ b/blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html @@ -1,5 +1,10 @@
++ + + +Paragraph one
-Paragraph two
by whomever
Paragraph two
+ by whomever diff --git a/blocks/test/fixtures/core__quote__style-1.html b/blocks/test/fixtures/core__quote__style-1.html index 50f330921b5a34..2517aea08d4196 100644 --- a/blocks/test/fixtures/core__quote__style-1.html +++ b/blocks/test/fixtures/core__quote__style-1.html @@ -1,3 +1,8 @@ -+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.
Matt Mullenweg, 2017
+ +diff --git a/blocks/test/fixtures/core__quote__style-1.json b/blocks/test/fixtures/core__quote__style-1.json index 1a1f57668d6bba..64f98a8efab360 100644 --- a/blocks/test/fixtures/core__quote__style-1.json +++ b/blocks/test/fixtures/core__quote__style-1.json @@ -4,26 +4,26 @@ "name": "core/quote", "isValid": true, "attributes": { - "value": [ - { - "children": { - "type": "p", - "key": null, - "ref": null, - "props": { - "children": "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." - }, - "_owner": null, - "_store": {} - } - } - ], "citation": [ "Matt Mullenweg, 2017" ], "style": 1 }, - "innerBlocks": [], - "originalContent": "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.
+ + Matt Mullenweg, 2017 +
" + "innerBlocks": [ + { + "uid": "_uid_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "content": [ + "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." + ], + "dropCap": false + }, + "innerBlocks": [], + "originalContent": "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.
Matt Mullenweg, 2017
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.
" + } + ], + "originalContent": "\n\t\n\tMatt Mullenweg, 2017\n" } ] diff --git a/blocks/test/fixtures/core__quote__style-1.parsed.json b/blocks/test/fixtures/core__quote__style-1.parsed.json index f09e5b0b9d384d..6feb5ac802d7e9 100644 --- a/blocks/test/fixtures/core__quote__style-1.parsed.json +++ b/blocks/test/fixtures/core__quote__style-1.parsed.json @@ -4,8 +4,15 @@ "attrs": { "style": "1" }, - "innerBlocks": [], - "innerHTML": "\n
\n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n\tThe 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.
Matt Mullenweg, 2017
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.
\n\t" + } + ], + "innerHTML": "\n\n\t\n\tMatt Mullenweg, 2017\n\n" }, { "attrs": {}, diff --git a/blocks/test/fixtures/core__quote__style-1.serialized.html b/blocks/test/fixtures/core__quote__style-1.serialized.html index ad7516a48ae532..e8531ec98aa533 100644 --- a/blocks/test/fixtures/core__quote__style-1.serialized.html +++ b/blocks/test/fixtures/core__quote__style-1.serialized.html @@ -1,4 +1,6 @@
-+ +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.
Matt Mullenweg, 2017
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.
+ Matt Mullenweg, 2017 diff --git a/blocks/test/fixtures/core__quote__style-2.html b/blocks/test/fixtures/core__quote__style-2.html index 544a6062c1d802..e44adca0be651a 100644 --- a/blocks/test/fixtures/core__quote__style-2.html +++ b/blocks/test/fixtures/core__quote__style-2.html @@ -1,3 +1,8 @@ -+There is no greater agony than bearing an untold story inside you.
Maya Angelou
+ +diff --git a/blocks/test/fixtures/core__quote__style-2.json b/blocks/test/fixtures/core__quote__style-2.json index 2462f8aa0a8fc4..08dd3bb011ad14 100644 --- a/blocks/test/fixtures/core__quote__style-2.json +++ b/blocks/test/fixtures/core__quote__style-2.json @@ -4,26 +4,26 @@ "name": "core/quote", "isValid": true, "attributes": { - "value": [ - { - "children": { - "type": "p", - "key": null, - "ref": null, - "props": { - "children": "There is no greater agony than bearing an untold story inside you." - }, - "_owner": null, - "_store": {} - } - } - ], "citation": [ "Maya Angelou" ], "style": 2 }, - "innerBlocks": [], - "originalContent": "There is no greater agony than bearing an untold story inside you.
+ + Maya Angelou +
" + "innerBlocks": [ + { + "uid": "_uid_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "content": [ + "There is no greater agony than bearing an untold story inside you." + ], + "dropCap": false + }, + "innerBlocks": [], + "originalContent": "There is no greater agony than bearing an untold story inside you.
Maya Angelou
There is no greater agony than bearing an untold story inside you.
" + } + ], + "originalContent": "\n\t\n\tMaya Angelou\n" } ] diff --git a/blocks/test/fixtures/core__quote__style-2.parsed.json b/blocks/test/fixtures/core__quote__style-2.parsed.json index 5af57f9cc0706d..c0e0d840ace732 100644 --- a/blocks/test/fixtures/core__quote__style-2.parsed.json +++ b/blocks/test/fixtures/core__quote__style-2.parsed.json @@ -4,8 +4,15 @@ "attrs": { "style": "2" }, - "innerBlocks": [], - "innerHTML": "\n
\n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n\tThere is no greater agony than bearing an untold story inside you.
Maya Angelou
There is no greater agony than bearing an untold story inside you.
\n\t" + } + ], + "innerHTML": "\n\n\t\n\tMaya Angelou\n\n" }, { "attrs": {}, diff --git a/blocks/test/fixtures/core__quote__style-2.serialized.html b/blocks/test/fixtures/core__quote__style-2.serialized.html index f1256d9f8952ea..a60d828af5c388 100644 --- a/blocks/test/fixtures/core__quote__style-2.serialized.html +++ b/blocks/test/fixtures/core__quote__style-2.serialized.html @@ -1,4 +1,6 @@
-+ +There is no greater agony than bearing an untold story inside you.
Maya Angelou
There is no greater agony than bearing an untold story inside you.
+ Maya Angelou diff --git a/editor/components/block-list/block.js b/editor/components/block-list/block.js index 7d5da4e893d4b3..a029f922825da9 100644 --- a/editor/components/block-list/block.js +++ b/editor/components/block-list/block.js @@ -25,6 +25,7 @@ import { isSharedBlock, isUnmodifiedDefaultBlock, withEditorSettings, + getDefaultBlockName, } from '@wordpress/blocks'; import { withFilters } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; @@ -65,7 +66,6 @@ export class BlockListBlock extends Component { this.maybeHover = this.maybeHover.bind( this ); this.hideHoverEffects = this.hideHoverEffects.bind( this ); this.mergeBlocks = this.mergeBlocks.bind( this ); - this.insertBlocksAfter = this.insertBlocksAfter.bind( this ); this.onFocus = this.onFocus.bind( this ); this.preventDrag = this.preventDrag.bind( this ); this.onPointerDown = this.onPointerDown.bind( this ); @@ -270,10 +270,6 @@ export class BlockListBlock extends Component { } } - insertBlocksAfter( blocks ) { - this.props.onInsertBlocks( blocks, this.props.order + 1 ); - } - /** * Marks the block as selected when focused and not already selected. This * specifically handles the case where block does not set focus on its own @@ -351,9 +347,9 @@ export class BlockListBlock extends Component { case ENTER: // Insert default block after current block if enter and event // not already handled by descendant. - this.props.onInsertBlocks( [ - createBlock( 'core/paragraph' ), - ], this.props.order + 1 ); + this.props.onInsertBlocksAfter( [ + createBlock( getDefaultBlockName() ), + ] ); event.preventDefault(); break; @@ -555,7 +551,7 @@ export class BlockListBlock extends Component { isSelected={ isSelected } attributes={ block.attributes } setAttributes={ this.setAttributes } - insertBlocksAfter={ isLocked ? undefined : this.insertBlocksAfter } + insertBlocksAfter={ isLocked ? undefined : this.props.onInsertBlocksAfter } onReplace={ isLocked ? undefined : onReplace } mergeBlocks={ isLocked ? undefined : this.mergeBlocks } id={ uid } @@ -620,6 +616,7 @@ const applyWithSelect = withSelect( ( select, { uid, rootUID } ) => { isSelectionEnabled, getSelectedBlocksInitialCaretPosition, getBlockSelectionEnd, + getBlockRootUID, } = select( 'core/editor' ); const isSelected = isBlockSelected( uid ); return { @@ -639,6 +636,8 @@ const applyWithSelect = withSelect( ( select, { uid, rootUID } ) => { isSelectionEnabled: isSelectionEnabled(), initialPosition: getSelectedBlocksInitialCaretPosition(), isSelected, + rootUIDOfRoot: getBlockRootUID( rootUID ), + orderOfRoot: getBlockIndex( rootUID, getBlockRootUID( rootUID ) ), }; } ); @@ -652,6 +651,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps ) => { replaceBlocks, editPost, toggleSelection, + moveBlockToPosition, } = dispatch( 'core/editor' ); return { @@ -661,10 +661,24 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps ) => { onSelect( uid = ownProps.uid, initialPosition ) { selectBlock( uid, initialPosition ); }, - onInsertBlocks( blocks, index ) { - const { rootUID, layout } = ownProps; - blocks = blocks.map( ( block ) => cloneBlock( block, { layout } ) ); - insertBlocks( blocks, index, rootUID ); + onInsertBlocksAfter( blocks ) { + const { block, order, isLast, rootUID, orderOfRoot, rootUIDOfRoot, layout } = ownProps; + + blocks = blocks.map( ( oldBlock ) => cloneBlock( oldBlock, { layout } ) ); + + // If the current block is the last nested empty paragraph block, + // and we're about to insert another empty paragraph block, then + // move the empty paragraph block behind the wrapping block. + // This is a way for the user to escape out of wrapping blocks. + if ( + rootUID && isLast && blocks.length === 1 && + isUnmodifiedDefaultBlock( first( blocks ) ) && + isUnmodifiedDefaultBlock( block ) + ) { + moveBlockToPosition( block.uid, rootUID, rootUIDOfRoot, layout, orderOfRoot + 1 ); + } else { + insertBlocks( blocks, order + 1, rootUID ); + } }, onRemove( uid ) { removeBlock( uid ); diff --git a/post-content.js b/post-content.js index d556c8c74df1f9..fe042e409ed4de 100644 --- a/post-content.js +++ b/post-content.js @@ -72,7 +72,12 @@ window._wpGutenbergPost.content = { '', '', - '', + '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.
Matt Mullenweg, 2017
', + '', + '', '', '', @@ -135,7 +140,12 @@ window._wpGutenbergPost.content = { '', '', - '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.
', + '', + 'Matt Mullenweg, 2017', + '
', + 'Code is Poetry
The WordPress community
', + '', + '', '', '', diff --git a/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap index 571013e6decb06..1ac9e168763c67 100644 --- a/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap +++ b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap @@ -11,11 +11,13 @@ exports[`adding blocks Should insert content using the placeholder and the regulCode is Poetry
', + '', + 'The WordPress community', + '
+- + - -Quote block
-
Code block
-"
+
+ Code block
+
+
+"
`;
diff --git a/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap b/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap
index 98d163a480fbd4..6068a1b134729e 100644
--- a/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap
+++ b/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap
@@ -15,3 +15,17 @@ exports[`splitting and merging blocks Should split and merge paragraph blocks us
FirstSecond
" `; + +exports[`splitting and merging blocks should split out of quote block using enter 1`] = ` +" ++ ++ + + + +" +`; diff --git a/test/e2e/specs/adding-blocks.test.js b/test/e2e/specs/adding-blocks.test.js index 973063c4613f40..71cfd11ee22044 100644 --- a/test/e2e/specs/adding-blocks.test.js +++ b/test/e2e/specs/adding-blocks.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage } from '../support/utils'; +import { newPost, newDesktopBrowserPage, getHTMLFromCodeEditor } from '../support/utils'; describe( 'adding blocks', () => { beforeAll( async () => { @@ -85,14 +85,6 @@ describe( 'adding blocks', () => { await clickAtRightish( inserter ); await page.keyboard.type( 'Second paragraph' ); - // Switch to Text Mode to check HTML Output - await page.click( '.edit-post-more-menu [aria-label="More"]' ); - const codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; - await codeEditorButton.click( 'button' ); - - // Assertions - const textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); - - expect( textEditorContent ).toMatchSnapshot(); + expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/specs/multi-block-selection.test.js b/test/e2e/specs/multi-block-selection.test.js index 09142e94846b08..85eba730bed696 100644 --- a/test/e2e/specs/multi-block-selection.test.js +++ b/test/e2e/specs/multi-block-selection.test.js @@ -13,7 +13,7 @@ describe( 'Multi-block selection', () => { it( 'Should select/unselect multiple blocks', async () => { const firstBlockSelector = '[data-type="core/paragraph"]'; const secondBlockSelector = '[data-type="core/image"]'; - const thirdBlockSelector = '[data-type="core/quote"]'; + const thirdBlockSelector = '[data-type="core/list"]'; const multiSelectedCssClass = 'is-multi-selected'; // Creating test blocks @@ -24,10 +24,10 @@ describe( 'Multi-block selection', () => { await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Enter' ); await page.click( '.edit-post-header [aria-label="Add block"]' ); - await page.keyboard.type( 'Quote' ); + await page.keyboard.type( 'List' ); await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'Quote Block' ); + await page.keyboard.type( 'List Block' ); const blocks = [ firstBlockSelector, secondBlockSelector, thirdBlockSelector ]; const expectMultiSelected = ( selectors, areMultiSelected ) => { diff --git a/test/e2e/specs/splitting-merging.test.js b/test/e2e/specs/splitting-merging.test.js index 4e78b0366eba28..c8dea70cf0c002 100644 --- a/test/e2e/specs/splitting-merging.test.js +++ b/test/e2e/specs/splitting-merging.test.js @@ -2,10 +2,10 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage } from '../support/utils'; +import { newPost, newDesktopBrowserPage, getHTMLFromCodeEditor } from '../support/utils'; describe( 'splitting and merging blocks', () => { - beforeAll( async () => { + beforeEach( async () => { await newDesktopBrowserPage(); await newPost(); } ); @@ -24,32 +24,22 @@ describe( 'splitting and merging blocks', () => { } await page.keyboard.press( 'Enter' ); - //Switch to Code Editor to check HTML output - await page.click( '.edit-post-more-menu [aria-label="More"]' ); - let codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; - await codeEditorButton.click( 'button' ); - - //Assert that there are now two paragraph blocks with correct content - let textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); - expect( textEditorContent ).toMatchSnapshot(); - - //Switch to Visual Editor to continue testing - await page.click( '.edit-post-more-menu [aria-label="More"]' ); - const visualEditorButton = ( await page.$x( '//button[contains(text(), \'Visual Editor\')]' ) )[ 0 ]; - await visualEditorButton.click( 'button' ); + expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); //Press Backspace to merge paragraph blocks await page.click( '.is-selected' ); await page.keyboard.press( 'Home' ); await page.keyboard.press( 'Backspace' ); - //Switch to Code Editor to check HTML output - await page.click( '.edit-post-more-menu [aria-label="More"]' ); - codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; - await codeEditorButton.click( 'button' ); + expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); + } ); + + it( 'should split out of quote block using enter', async () => { + await page.click( '.editor-default-block-appender' ); + await page.keyboard.type( '> test' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); - //Assert that there is now one paragraph with correct content - textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); - expect( textEditorContent ).toMatchSnapshot(); + expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/specs/templates.test.js b/test/e2e/specs/templates.test.js index 15d86445d7f243..925e589e0a41e4 100644 --- a/test/e2e/specs/templates.test.js +++ b/test/e2e/specs/templates.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage } from '../support/utils'; +import { newPost, newDesktopBrowserPage, getHTMLFromCodeEditor } from '../support/utils'; import { activatePlugin, deactivatePlugin } from '../support/plugins'; describe( 'Using a CPT with a predefined template', () => { @@ -18,13 +18,6 @@ describe( 'Using a CPT with a predefined template', () => { } ); it( 'Should add a custom post types with a predefined template', async () => { - //Switch to Code Editor to check HTML output - await page.click( '.edit-post-more-menu [aria-label="More"]' ); - const codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; - await codeEditorButton.click( 'button' ); - - // Assert that the post already contains the template defined blocks - const textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); - expect( textEditorContent ).toMatchSnapshot(); + expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/support/utils.js b/test/e2e/support/utils.js index ae4050789f64eb..7dce3539143609 100644 --- a/test/e2e/support/utils.js +++ b/test/e2e/support/utils.js @@ -58,3 +58,16 @@ export async function newDesktopBrowserPage() { global.page = await browser.newPage(); await page.setViewport( { width: 1000, height: 700 } ); } + +export async function switchToEditor( mode ) { + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + const [ button ] = await page.$x( `//button[contains(text(), \'${ mode } Editor\')]` ); + await button.click( 'button' ); +} + +export async function getHTMLFromCodeEditor() { + await switchToEditor( 'Code' ); + const textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); + await switchToEditor( 'Visual' ); + return textEditorContent; +}test
+ +