From cc659d6d89bd4d39496c1e88e2b58d081aa2f571 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 21 Jun 2018 15:46:48 +0200 Subject: [PATCH] Use nested blocks for quotes 7f666e35b5c8cd5463fb502843ef32a3da470345, 3478730d137f0c536417bc4ab2e0cd8c6e4eaca4, db4c94d5fa6395fd8d73f0ddc8177c5858496df3, 01df9e72e531be0c0ba65e3f49995d978439dc61, 56315f1067d1cd8b47f7fcfa7c28bef23eaaa997 --- core-blocks/audio/edit.js | 2 +- core-blocks/cover-image/index.js | 2 +- core-blocks/embed/index.js | 2 +- core-blocks/gallery/gallery-image.js | 2 +- core-blocks/image/edit.js | 2 +- core-blocks/list/index.js | 33 +-- core-blocks/pullquote/index.js | 90 ++++--- .../test/__snapshots__/index.js.snap | 150 ++++++++++-- core-blocks/quote/index.js | 219 +++++++----------- .../quote/test/__snapshots__/index.js.snap | 150 ++++++++++-- .../test/fixtures/core__pullquote.html | 5 +- .../test/fixtures/core__pullquote.json | 32 +-- .../test/fixtures/core__pullquote.parsed.json | 11 +- .../fixtures/core__pullquote.serialized.html | 4 +- .../core__pullquote__multi-paragraph.html | 4 + .../core__pullquote__multi-paragraph.json | 73 +++--- ...re__pullquote__multi-paragraph.parsed.json | 17 +- ...pullquote__multi-paragraph.serialized.html | 7 +- .../test/fixtures/core__quote__style-1.html | 7 +- .../test/fixtures/core__quote__style-1.json | 32 +-- .../fixtures/core__quote__style-1.parsed.json | 11 +- .../core__quote__style-1.serialized.html | 4 +- .../test/fixtures/core__quote__style-2.html | 7 +- .../test/fixtures/core__quote__style-2.json | 32 +-- .../fixtures/core__quote__style-2.parsed.json | 11 +- .../core__quote__style-2.serialized.html | 4 +- core-blocks/video/edit.js | 2 +- editor/components/block-list/block.js | 43 ++-- editor/components/block-mover/index.js | 6 + editor/components/rich-text/README.md | 6 +- editor/components/rich-text/index.js | 52 +++-- editor/store/selectors.js | 19 ++ editor/store/test/selectors.js | 39 ++++ post-content.js | 18 +- .../__snapshots__/adding-blocks.test.js.snap | 10 +- .../splitting-merging.test.js.snap | 14 ++ test/e2e/specs/adding-blocks.test.js | 20 +- test/e2e/specs/multi-block-selection.test.js | 6 +- test/e2e/specs/splitting-merging.test.js | 35 ++- test/integration/fixtures/markdown-out.html | 5 + 40 files changed, 774 insertions(+), 414 deletions(-) diff --git a/core-blocks/audio/edit.js b/core-blocks/audio/edit.js index 8b2d2d8db18982..3dd4d4f76938ca 100644 --- a/core-blocks/audio/edit.js +++ b/core-blocks/audio/edit.js @@ -94,7 +94,7 @@ class AudioEdit extends Component { placeholder={ __( 'Write caption…' ) } value={ caption } onChange={ ( value ) => setAttributes( { caption: value } ) } - inlineToolbar + inlineToolbar="center" /> ) } diff --git a/core-blocks/cover-image/index.js b/core-blocks/cover-image/index.js index 70d09efbd15148..01cf08fdf85569 100644 --- a/core-blocks/cover-image/index.js +++ b/core-blocks/cover-image/index.js @@ -221,7 +221,7 @@ export const settings = { placeholder={ __( 'Write title…' ) } value={ title } onChange={ ( value ) => setAttributes( { title: value } ) } - inlineToolbar + inlineToolbar="center" /> ) : null } diff --git a/core-blocks/embed/index.js b/core-blocks/embed/index.js index 1332db7164099c..b5f6fd05f79c37 100644 --- a/core-blocks/embed/index.js +++ b/core-blocks/embed/index.js @@ -271,7 +271,7 @@ function getEmbedBlockSettings( { title, description, icon, category = 'embed', placeholder={ __( 'Write caption…' ) } value={ caption } onChange={ ( value ) => setAttributes( { caption: value } ) } - inlineToolbar + inlineToolbar="center" /> ) : null } diff --git a/core-blocks/gallery/gallery-image.js b/core-blocks/gallery/gallery-image.js index 6301bbb2fec8e1..5982ef07adaf28 100644 --- a/core-blocks/gallery/gallery-image.js +++ b/core-blocks/gallery/gallery-image.js @@ -136,7 +136,7 @@ class GalleryImage extends Component { isSelected={ this.state.captionSelected } onChange={ ( newCaption ) => setAttributes( { caption: newCaption } ) } unstableOnFocus={ this.onSelectCaption } - inlineToolbar + inlineToolbar="center" /> ) : null } diff --git a/core-blocks/image/edit.js b/core-blocks/image/edit.js index 49a718da13565e..609f625594093c 100644 --- a/core-blocks/image/edit.js +++ b/core-blocks/image/edit.js @@ -393,7 +393,7 @@ class ImageEdit extends Component { unstableOnFocus={ this.onFocusCaption } onChange={ ( value ) => setAttributes( { caption: value } ) } isSelected={ this.state.captionFocused } - inlineToolbar + inlineToolbar="center" /> ) : null } diff --git a/core-blocks/list/index.js b/core-blocks/list/index.js index 8c3924f87a15e8..0e9701e41ef620 100644 --- a/core-blocks/list/index.js +++ b/core-blocks/list/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find, compact, get, initial, last, isEmpty } from 'lodash'; +import { find, compact, get, isEmpty } from 'lodash'; /** * WordPress dependencies @@ -80,21 +80,6 @@ export const settings = { } ); }, }, - { - type: 'block', - blocks: [ 'core/quote' ], - transform: ( { value, citation } ) => { - const items = value.map( ( p ) => get( p, [ 'children', 'props', 'children' ] ) ); - if ( ! isEmpty( citation ) ) { - items.push( citation ); - } - const hasItems = ! items.every( isEmpty ); - return createBlock( 'core/list', { - nodeName: 'UL', - values: hasItems ? items.map( ( content, index ) =>
  • { content }
  • ) : [], - } ); - }, - }, { type: 'raw', selector: 'ol,ul', @@ -134,18 +119,6 @@ export const settings = { content: [ content ], } ) ), }, - { - type: 'block', - blocks: [ 'core/quote' ], - transform: ( { values } ) => { - return createBlock( 'core/quote', { - value: compact( ( values.length === 1 ? values : initial( values ) ) - .map( ( value ) => get( value, [ 'props', 'children' ], null ) ) ) - .map( ( children ) => ( { children:

    { children }

    } ) ), - citation: ( values.length === 1 ? undefined : [ get( last( values ), [ 'props', 'children' ] ) ] ), - } ); - }, - }, ], }, @@ -253,7 +226,6 @@ export const settings = { const { attributes, insertBlocksAfter, - setAttributes, mergeBlocks, onReplace, className, @@ -301,7 +273,7 @@ export const settings = { onMerge={ mergeBlocks } onSplit={ insertBlocksAfter ? - ( before, after, ...blocks ) => { + ( unused, after, ...blocks ) => { if ( ! blocks.length ) { blocks.push( createBlock( 'core/paragraph' ) ); } @@ -313,7 +285,6 @@ export const settings = { } ) ); } - setAttributes( { values: before } ); insertBlocksAfter( blocks ); } : undefined diff --git a/core-blocks/pullquote/index.js b/core-blocks/pullquote/index.js index d325aa720f0a7c..87a1fe300fd2bd 100644 --- a/core-blocks/pullquote/index.js +++ b/core-blocks/pullquote/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { map } from 'lodash'; +import { castArray } from 'lodash'; /** * WordPress dependencies @@ -9,9 +9,11 @@ import { map } from 'lodash'; import { __ } from '@wordpress/i18n'; import { Fragment } from '@wordpress/element'; import { + createBlock, BlockControls, BlockAlignmentToolbar, RichText, + InnerBlocks, } from '@wordpress/editor'; /** @@ -20,21 +22,7 @@ import { import './editor.scss'; import './style.scss'; -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( { attributes, setAttributes, isSelected, className } ) { - const { value, citation, align } = attributes; + const { citation, align } = attributes; const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } ); return ( @@ -80,18 +68,7 @@ export const settings = { />
    - setAttributes( { - value: fromRichTextValue( nextValue ), - } ) - } - /* translators: the text of the quotation */ - placeholder={ __( 'Write quote…' ) } - wrapperClassName="core-blocks-pullquote__content" - /> + { ( citation || isSelected ) && ( ) }
    @@ -111,11 +89,11 @@ export const settings = { }, save( { attributes } ) { - const { value, citation, align } = attributes; + const { citation, align } = attributes; return (
    - + { citation && citation.length > 0 && }
    ); @@ -124,6 +102,54 @@ 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 ) => +

    { paragraph.children && paragraph.children.props.children }

    + ) } + { citation && citation.length > 0 && } +
    + ); + }, + }, { + attributes: { + ...blockAttributes, + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + }, citation: { type: 'array', source: 'children', @@ -136,7 +162,9 @@ export const settings = { return (
    - + { value && value.map( ( paragraph, i ) => +

    { paragraph.children && paragraph.children.props.children }

    + ) } { citation && citation.length > 0 && }
    ); diff --git a/core-blocks/pullquote/test/__snapshots__/index.js.snap b/core-blocks/pullquote/test/__snapshots__/index.js.snap index c1e9bd890f51e5..cc71b474c6f238 100644 --- a/core-blocks/pullquote/test/__snapshots__/index.js.snap +++ b/core-blocks/pullquote/test/__snapshots__/index.js.snap @@ -5,29 +5,149 @@ exports[`core/pullquote block edit matches snapshot 1`] = ` class="wp-block-pullquote" >
    -
    +
    +
    + +
    +
    -

    - Write quote… -

    +
    + + +

    + +

    diff --git a/core-blocks/quote/index.js b/core-blocks/quote/index.js index 08bbaa286979b8..05d911f8156792 100644 --- a/core-blocks/quote/index.js +++ b/core-blocks/quote/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { castArray, get, isString, isEmpty } from 'lodash'; +import { castArray } from 'lodash'; import classnames from 'classnames'; /** @@ -10,11 +10,12 @@ import classnames from 'classnames'; import { __, sprintf } from '@wordpress/i18n'; import { Toolbar } from '@wordpress/components'; import { Fragment } from '@wordpress/element'; -import { createBlock, getPhrasingContentSchema } from '@wordpress/blocks'; +import { createBlock, rawHandler } from '@wordpress/blocks'; import { BlockControls, AlignmentToolbar, RichText, + InnerBlocks, } from '@wordpress/editor'; /** @@ -24,23 +25,7 @@ import './style.scss'; import './editor.scss'; import './theme.scss'; -const toRichTextValue = ( value ) => value.map( ( ( subValue ) => subValue.children ) ); -const fromRichTextValue = ( value ) => value.map( ( subValue ) => ( { - children: subValue, -} ) ); - const blockAttributes = { - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - default: [], - }, citation: { type: 'array', source: 'children', @@ -67,119 +52,40 @@ export const settings = { transforms: { from: [ - { + ...[ 'core/paragraph', 'core/heading' ].map( ( fromName ) => ( { type: 'block', - isMultiBlock: true, - blocks: [ 'core/paragraph' ], - transform: ( attributes ) => { - const items = attributes.map( ( { content } ) => content ); - const hasItems = ! items.every( isEmpty ); - return createBlock( 'core/quote', { - value: hasItems ? - items.map( ( content, index ) => ( { children:

    { 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', selector: 'blockquote', schema: { blockquote: { - children: { - p: { - children: getPhrasingContentSchema(), - }, - }, + children: '*', }, }, - }, - ], - 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', + } ) ); }, }, ], }, - edit( { attributes, setAttributes, isSelected, mergeBlocks, onReplace, className } ) { - const { align, value, citation, style } = attributes; + edit( { attributes, setAttributes, isSelected, className, hasSelectedBlock } ) { + const { align, citation, style } = attributes; const containerClassname = classnames( className, style === 2 ? 'is-large' : '' ); return ( @@ -204,25 +110,9 @@ 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…' ) } - /> - { ( ( citation && citation.length > 0 ) || isSelected ) && ( + + + { ( ( citation && citation.length > 0 ) || isSelected || hasSelectedBlock ) && ( ) } @@ -241,14 +132,14 @@ export const settings = { }, save( { attributes } ) { - const { align, value, citation, style } = attributes; + const { align, citation, style } = attributes; return (
    - + { citation && citation.length > 0 && }
    ); @@ -258,6 +149,60 @@ 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 ) => ( +

    { paragraph.children && paragraph.children.props.children }

    + ) ) } + { citation && citation.length > 0 && } +
    + ); + }, + }, + { + attributes: { + ...blockAttributes, + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + default: [], + }, citation: { type: 'array', source: 'children', @@ -273,7 +218,9 @@ export const settings = { className={ `blocks-quote-style-${ style }` } style={ { textAlign: align ? align : null } } > - + { value.map( ( paragraph, i ) => ( +

    { paragraph.children && paragraph.children.props.children }

    + ) ) } { citation && citation.length > 0 && } ); diff --git a/core-blocks/quote/test/__snapshots__/index.js.snap b/core-blocks/quote/test/__snapshots__/index.js.snap index 813b6d8417cb66..53bbb31913502a 100644 --- a/core-blocks/quote/test/__snapshots__/index.js.snap +++ b/core-blocks/quote/test/__snapshots__/index.js.snap @@ -5,29 +5,149 @@ exports[`core/quote block edit matches snapshot 1`] = ` class="wp-block-quote" >
    -
    +
    +
    + +
    +
    -

    - Write quote… -

    +
    + + +

    + +

    diff --git a/core-blocks/test/fixtures/core__pullquote.html b/core-blocks/test/fixtures/core__pullquote.html index 06d1ea9ab61144..87941125770945 100644 --- a/core-blocks/test/fixtures/core__pullquote.html +++ b/core-blocks/test/fixtures/core__pullquote.html @@ -1,5 +1,8 @@
    -

    Testing pullquote block...

    ...with a caption + +

    Testing pullquote block...

    + + ...with a caption
    diff --git a/core-blocks/test/fixtures/core__pullquote.json b/core-blocks/test/fixtures/core__pullquote.json index e044ae447f65f8..0b477b21e70159 100644 --- a/core-blocks/test/fixtures/core__pullquote.json +++ b/core-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": "
    \n

    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...

    " + } + ], + "originalContent": "
    \n\t\n\t...with a caption\n
    " } ] diff --git a/core-blocks/test/fixtures/core__pullquote.parsed.json b/core-blocks/test/fixtures/core__pullquote.parsed.json index 4a96da0d5574ba..248585392f75d6 100644 --- a/core-blocks/test/fixtures/core__pullquote.parsed.json +++ b/core-blocks/test/fixtures/core__pullquote.parsed.json @@ -2,8 +2,15 @@ { "blockName": "core/pullquote", "attrs": null, - "innerBlocks": [], - "innerHTML": "\n
    \n

    Testing pullquote block...

    ...with a caption\n
    \n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n\t

    Testing pullquote block...

    \n\t" + } + ], + "innerHTML": "\n
    \n\t\n\t...with a caption\n
    \n" }, { "attrs": {}, diff --git a/core-blocks/test/fixtures/core__pullquote.serialized.html b/core-blocks/test/fixtures/core__pullquote.serialized.html index 02aa277f756539..a8617d21d83bde 100644 --- a/core-blocks/test/fixtures/core__pullquote.serialized.html +++ b/core-blocks/test/fixtures/core__pullquote.serialized.html @@ -1,4 +1,6 @@
    -

    Testing pullquote block...

    ...with a caption
    + +

    Testing pullquote block...

    + ...with a caption diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html index 113d829d01329c..4c6800b8f1e50c 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html +++ b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html @@ -1,7 +1,11 @@
    +

    Paragraph one

    + +

    Paragraph two

    + by whomever
    diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json index c2a6c0d770de73..ac24bc35d1500a 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json +++ b/core-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": "
    \n

    Paragraph one

    \n

    Paragraph two

    \n 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

    " + }, + { + "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/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json index 4821344abd80aa..85048516b27971 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json +++ b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json @@ -2,8 +2,21 @@ { "blockName": "core/pullquote", "attrs": null, - "innerBlocks": [], - "innerHTML": "\n
    \n

    Paragraph one

    \n

    Paragraph two

    \n by whomever\n
    \n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n

    Paragraph one

    \n " + }, + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n

    Paragraph two

    \n " + } + ], + "innerHTML": "\n
    \n\t\n \n by whomever\n
    \n" }, { "attrs": {}, diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html index 58d40223987735..c57ac475a512b8 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html +++ b/core-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/core-blocks/test/fixtures/core__quote__style-1.html b/core-blocks/test/fixtures/core__quote__style-1.html index 50f330921b5a34..2517aea08d4196 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.html +++ b/core-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
    +
    + +

    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/core-blocks/test/fixtures/core__quote__style-1.json b/core-blocks/test/fixtures/core__quote__style-1.json index 1a1f57668d6bba..64f98a8efab360 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.json +++ b/core-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.

    " + } + ], + "originalContent": "
    \n\t\n\tMatt Mullenweg, 2017\n
    " } ] diff --git a/core-blocks/test/fixtures/core__quote__style-1.parsed.json b/core-blocks/test/fixtures/core__quote__style-1.parsed.json index f09e5b0b9d384d..6feb5ac802d7e9 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.parsed.json +++ b/core-blocks/test/fixtures/core__quote__style-1.parsed.json @@ -4,8 +4,15 @@ "attrs": { "style": "1" }, - "innerBlocks": [], - "innerHTML": "\n

    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
    \n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n\t

    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/core-blocks/test/fixtures/core__quote__style-1.serialized.html b/core-blocks/test/fixtures/core__quote__style-1.serialized.html index a56c5859bd335d..68fb9ec55b95b3 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.serialized.html +++ b/core-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/core-blocks/test/fixtures/core__quote__style-2.html b/core-blocks/test/fixtures/core__quote__style-2.html index 544a6062c1d802..e44adca0be651a 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.html +++ b/core-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
    +
    + +

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

    + + Maya Angelou +
    diff --git a/core-blocks/test/fixtures/core__quote__style-2.json b/core-blocks/test/fixtures/core__quote__style-2.json index 2462f8aa0a8fc4..08dd3bb011ad14 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.json +++ b/core-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.

    " + } + ], + "originalContent": "
    \n\t\n\tMaya Angelou\n
    " } ] diff --git a/core-blocks/test/fixtures/core__quote__style-2.parsed.json b/core-blocks/test/fixtures/core__quote__style-2.parsed.json index 5af57f9cc0706d..c0e0d840ace732 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.parsed.json +++ b/core-blocks/test/fixtures/core__quote__style-2.parsed.json @@ -4,8 +4,15 @@ "attrs": { "style": "2" }, - "innerBlocks": [], - "innerHTML": "\n

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

    Maya Angelou
    \n" + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n\t

    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/core-blocks/test/fixtures/core__quote__style-2.serialized.html b/core-blocks/test/fixtures/core__quote__style-2.serialized.html index e715726fb9cc68..046b58ec47a9cd 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.serialized.html +++ b/core-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/core-blocks/video/edit.js b/core-blocks/video/edit.js index 729c3b0813f277..ef9db99cac51ef 100644 --- a/core-blocks/video/edit.js +++ b/core-blocks/video/edit.js @@ -94,7 +94,7 @@ class VideoEdit extends Component { placeholder={ __( 'Write caption…' ) } value={ caption } onChange={ ( value ) => setAttributes( { caption: value } ) } - inlineToolbar + inlineToolbar="center" /> ) } diff --git a/editor/components/block-list/block.js b/editor/components/block-list/block.js index fb03d7b7d0c4a6..d3b64d7933ae12 100644 --- a/editor/components/block-list/block.js +++ b/editor/components/block-list/block.js @@ -23,6 +23,7 @@ import { getSaveElement, isSharedBlock, isUnmodifiedDefaultBlock, + getDefaultBlockName, } from '@wordpress/blocks'; import { withFilters } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; @@ -63,7 +64,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 ); @@ -248,10 +248,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 @@ -329,9 +325,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; @@ -531,12 +527,13 @@ 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 } isSelectionEnabled={ this.props.isSelectionEnabled } toggleSelection={ this.props.toggleSelection } + hasSelectedBlock={ this.props.hasSelectedBlock } /> ) } { isValid && mode === 'html' && ( @@ -596,6 +593,8 @@ const applyWithSelect = withSelect( ( select, { uid, rootUID } ) => { getSelectedBlocksInitialCaretPosition, getEditorSettings, hasSelectedInnerBlock, + getBlockRootUID, + hasBlockSelectedBlock, } = select( 'core/editor' ); const isSelected = isBlockSelected( uid ); const isParentOfSelectedBlock = hasSelectedInnerBlock( uid ); @@ -625,6 +624,9 @@ const applyWithSelect = withSelect( ( select, { uid, rootUID } ) => { block, isSelected, hasFixedToolbar, + rootUIDOfRoot: getBlockRootUID( rootUID ), + orderOfRoot: getBlockIndex( rootUID, getBlockRootUID( rootUID ) ), + hasSelectedBlock: hasBlockSelectedBlock( uid ), }; } ); @@ -638,6 +640,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps ) => { replaceBlocks, editPost, toggleSelection, + moveBlockToPosition, } = dispatch( 'core/editor' ); return { @@ -647,10 +650,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/editor/components/block-mover/index.js b/editor/components/block-mover/index.js index 1cb87dce0be59c..3cc67edb932dd5 100644 --- a/editor/components/block-mover/index.js +++ b/editor/components/block-mover/index.js @@ -46,10 +46,16 @@ export class BlockMover extends Component { const { onMoveUp, onMoveDown, isFirst, isLast, uids, blockType, firstIndex, isLocked, instanceId, isHidden } = this.props; const { isFocused } = this.state; const blocksCount = castArray( uids ).length; + if ( isLocked ) { return null; } + // Don't render if there's only one block in the list. + if ( isFirst && isLast ) { + return null; + } + // We emulate a disabled state because forcefully applying the `disabled` // attribute on the button while it has focus causes the screen to change // to an unfocused state (body as active element) without firing blur on, diff --git a/editor/components/rich-text/README.md b/editor/components/rich-text/README.md index 306bb28cd7b85c..a226a8f8dac479 100644 --- a/editor/components/rich-text/README.md +++ b/editor/components/rich-text/README.md @@ -33,7 +33,7 @@ Render a rich [`contenteditable` input](https://developer.mozilla.org/en-US/docs ### `onSplit( before: Array|String, after: Array|String, ...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` @@ -63,6 +63,10 @@ Render a rich [`contenteditable` input](https://developer.mozilla.org/en-US/docs *Optional.* A list of autocompleters to use instead of the default. +### `inlineToolbar: String` + +*Optional.* Render the formatting toolbar inline, next to the rich text field. Needs to be a string that can be used with the `justify-content` CSS property. + ## RichText.Content When using RichText in the edit function of blocks, the usage of `RichText.Content` is recommended in the save function of your blocks to save the correct HTML. diff --git a/editor/components/rich-text/index.js b/editor/components/rich-text/index.js index 9f7a6d1afd7d03..25c435ca3af2b6 100644 --- a/editor/components/rich-text/index.js +++ b/editor/components/rich-text/index.js @@ -43,6 +43,11 @@ import patterns from './patterns'; import { withBlockEditContext } from '../block-edit/context'; import { domToFormat, valueToString } from './format'; +/** + * Browser dependencies + */ +const { log, warn, error } = window.console; + const { BACKSPACE, DELETE, ENTER, rawShortcut } = keycodes; /** @@ -103,7 +108,7 @@ export function getFormatProperties( formatName, parents ) { const DEFAULT_FORMATS = [ 'bold', 'italic', 'strikethrough', 'link', 'code' ]; export class RichText extends Component { - constructor() { + constructor( { value } ) { super( ...arguments ); this.onInit = this.onInit.bind( this ); @@ -128,6 +133,7 @@ export class RichText extends Component { }; this.containerRef = createRef(); + this.savedContent = value; } /** @@ -270,7 +276,7 @@ export class RichText extends Component { const shouldReplace = this.props.onReplace && this.isEmpty(); // Allows us to ask for this information when we get a report. - window.console.log( 'Received item:\n\n', file ); + log( 'Received item:\n\n', file ); if ( shouldReplace ) { // Necessary to allow the paste bin to be removed without errors. @@ -304,8 +310,8 @@ export class RichText extends Component { event.preventDefault(); // 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 ); + log( 'Received HTML:\n\n', HTML ); + log( 'Received plain text:\n\n', this.pastedPlainText ); // There is a selection, check if a link is pasted. if ( ! this.editor.selection.isCollapsed() ) { @@ -320,7 +326,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 ); + log( 'Created link:\n\n', pastedText ); return; } @@ -510,6 +516,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(); } } @@ -650,10 +660,8 @@ export class RichText extends Component { return memo; }, [] ); - // Splitting into two blocks - this.setContent( this.props.value ); - const { format } = this.props; + this.restoreContentAndSplit( domToFormat( before, format, this.editor ), domToFormat( after, format, this.editor ) @@ -732,21 +740,21 @@ 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 ) + ) { + 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.' ); + } } if ( 'development' === process.env.NODE_ENV ) { if ( ! isEqual( this.props.formatters, prevProps.formatters ) ) { - // eslint-disable-next-line no-console - console.error( 'Formatters passed via `formatters` prop will only be registered once. Formatters can be enabled/disabled via the `formattingControls` prop.' ); + error( 'Formatters passed via `formatters` prop will only be registered once. Formatters can be enabled/disabled via the `formattingControls` prop.' ); } } } @@ -823,14 +831,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 ); } @@ -883,7 +892,10 @@ export class RichText extends Component { ) } { isSelected && inlineToolbar && ( -
    +
    { formatToolbar }
    ) } diff --git a/editor/store/selectors.js b/editor/store/selectors.js index ea5d7d8180b4cd..da44933371741e 100644 --- a/editor/store/selectors.js +++ b/editor/store/selectors.js @@ -690,6 +690,25 @@ export function getSelectedBlockUID( state ) { return start === end && start ? start : null; } +/** + * Returns true if there is a selected block inside a given block, or false + * otherwise. + * + * @param {Object} state Editor state. + * @param {string} uid Block in which to find a selected block. + * + * @return {boolean} Whether a the block contains a selected block. + */ +export function hasBlockSelectedBlock( state, uid ) { + const { start, end } = state.blockSelection; + + if ( ! start || start !== end ) { + return false; + } + + return getBlockRootUID( state, start ) === uid; +} + /** * Returns the currently selected block, or null if there is no selected block. * diff --git a/editor/store/test/selectors.js b/editor/store/test/selectors.js index 42e2b917e39d3c..f59a20a8fd6611 100644 --- a/editor/store/test/selectors.js +++ b/editor/store/test/selectors.js @@ -97,6 +97,7 @@ const { INSERTER_UTILITY_HIGH, INSERTER_UTILITY_MEDIUM, INSERTER_UTILITY_LOW, + hasBlockSelectedBlock, } = selectors; describe( 'selectors', () => { @@ -1826,6 +1827,44 @@ describe( 'selectors', () => { } ); } ); + describe( 'hasBlockSelectedBlock', () => { + it( 'should return true if the block has selected blocks', () => { + const state = { + editor: { + present: { + blockOrder: { + 1: [ '2' ], + }, + }, + }, + blockSelection: { + start: '2', + end: '2', + }, + }; + + expect( hasBlockSelectedBlock( state, '1' ) ).toBe( true ); + } ); + + it( 'should return false if the block does not have selected blocks', () => { + const state = { + editor: { + present: { + blockOrder: { + 1: [ '2' ], + }, + }, + }, + blockSelection: { + start: '1', + end: '1', + }, + }; + + expect( hasBlockSelectedBlock( state, '1' ) ).toBe( false ); + } ); + } ); + describe( 'getGlobalBlockCount', () => { it( 'should return the global number of top-level blocks in the post', () => { const state = { diff --git a/post-content.js b/post-content.js index a6ca6923e9ca5f..0b8b4450a63396 100644 --- a/post-content.js +++ b/post-content.js @@ -70,7 +70,14 @@ window._wpGutenbergDefaultPost = { '', '', - '

    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', + '
    ', '', '', @@ -133,7 +140,14 @@ window._wpGutenbergDefaultPost = { '', '', - '

    Code is Poetry

    The WordPress community
    ', + '
    ', + + '', + '

    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 adcd4803278805..458941551d0f74 100644 --- a/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap +++ b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap @@ -5,16 +5,18 @@ exports[`adding blocks Should insert content using the placeholder and the regul

    Paragraph block

    - -

    Second paragraph

    - -
    +

    Quote block

    +
    + +

    Second paragraph

    + +
    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..e5a7168ef92c16 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`] = ` +" +
    + +

    test

    + +
    + + + +

    +" +`; diff --git a/test/e2e/specs/adding-blocks.test.js b/test/e2e/specs/adding-blocks.test.js index 1aa2e1aaa8b6b5..9232c1bfccc195 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, insertBlock } from '../support/utils'; +import { newPost, newDesktopBrowserPage, insertBlock, getHTMLFromCodeEditor } from '../support/utils'; describe( 'adding blocks', () => { beforeAll( async () => { @@ -48,6 +48,10 @@ describe( 'adding blocks', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'Quote block' ); + // Leave nested area. + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); + // Using the regular inserter await insertBlock( 'code' ); await page.keyboard.type( 'Code block' ); @@ -56,20 +60,12 @@ describe( 'adding blocks', () => { await page.click( '.editor-post-title__input' ); // Using the between inserter - const insertionPoint = await page.$( '[data-type="core/quote"] .editor-block-list__insertion-point-button' ); + const insertionPoint = await page.$( '[data-type="core/code"] .editor-block-list__insertion-point-button' ); const rect = await insertionPoint.boundingBox(); await page.mouse.move( rect.x + ( rect.width / 2 ), rect.y + ( rect.height / 2 ) ); - await page.click( '[data-type="core/quote"] .editor-block-list__insertion-point-button' ); + await page.click( '[data-type="core/code"] .editor-block-list__insertion-point-button' ); 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 fae260ca0efbc4..1346a6d22ad1de 100644 --- a/test/e2e/specs/multi-block-selection.test.js +++ b/test/e2e/specs/multi-block-selection.test.js @@ -13,15 +13,15 @@ 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 await page.click( '.editor-default-block-appender' ); await page.keyboard.type( 'First Paragraph' ); await insertBlock( 'Image' ); - await insertBlock( 'Quote' ); - await page.keyboard.type( 'Quote Block' ); + await insertBlock( 'List' ); + await page.keyboard.type( 'List Block' ); const blocks = [ firstBlockSelector, secondBlockSelector, thirdBlockSelector ]; const expectMultiSelected = async ( selectors, areMultiSelected ) => { diff --git a/test/e2e/specs/splitting-merging.test.js b/test/e2e/specs/splitting-merging.test.js index 4790aa8f2cc07f..12dc95439b6c51 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, insertBlock } from '../support/utils'; +import { newPost, newDesktopBrowserPage, insertBlock, getHTMLFromCodeEditor } from '../support/utils'; describe( 'splitting and merging blocks', () => { - beforeAll( async () => { + beforeEach( async () => { await newDesktopBrowserPage(); await newPost(); } ); @@ -21,32 +21,23 @@ 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 () => { + //Use regular inserter to add paragraph block and text + await insertBlock( 'quote' ); + 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/integration/fixtures/markdown-out.html b/test/integration/fixtures/markdown-out.html index 5b674a8bfaf864..02205061e1e0ca 100644 --- a/test/integration/fixtures/markdown-out.html +++ b/test/integration/fixtures/markdown-out.html @@ -65,8 +65,13 @@

    Quote

    +

    First

    + + +

    Second

    +