From 1aed21a84a0a9761e8dbd23dcae5fc4874cc7594 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 20 Apr 2017 14:29:38 +0200 Subject: [PATCH 1/8] Move heading block `transforms` to before `edit` and `save` https://github.com/WordPress/gutenberg/pull/429#discussion_r111995575 --- blocks/library/heading/index.js | 60 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/blocks/library/heading/index.js b/blocks/library/heading/index.js index acb6c5d0de4f2e..50a5c69ef9dc49 100644 --- a/blocks/library/heading/index.js +++ b/blocks/library/heading/index.js @@ -31,29 +31,6 @@ registerBlock( 'core/heading', { } ) ) ], - edit( { attributes, setAttributes } ) { - const { content, tag, align } = attributes; - - return ( - setAttributes( { content: value } ) } - style={ align ? { textAlign: align } : null } - /> - ); - }, - - save( { attributes } ) { - const { align, tag: Tag, content } = attributes; - - return ( - - ); - }, - transforms: { from: [ { @@ -68,10 +45,10 @@ registerBlock( 'core/heading', { return { tag: 'H2', content, - align + align, }; - } - } + }, + }, ], to: [ { @@ -80,10 +57,33 @@ registerBlock( 'core/heading', { transform: ( { content, align } ) => { return { content: [ content ], - align + align, }; - } - } + }, + }, ] - } + }, + + edit( { attributes, setAttributes } ) { + const { content, tag, align } = attributes; + + return ( + setAttributes( { content: value } ) } + style={ align ? { textAlign: align } : null } + /> + ); + }, + + save( { attributes } ) { + const { align, tag: Tag, content } = attributes; + + return ( + + ); + }, } ); From 98a87eabbe760127c97e72f6bd9ca3032f4e0d1b Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 20 Apr 2017 14:52:39 +0200 Subject: [PATCH 2/8] Disable switching a multi-paragraph text block to a heading block --- blocks/api/factory.js | 12 ++++-- blocks/library/heading/index.js | 7 +++- editor/components/block-switcher/index.js | 49 ++++++++++++++++------- editor/components/icon-button/index.js | 12 +++++- 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/blocks/api/factory.js b/blocks/api/factory.js index c5514147f121c1..8e370abbbf5342 100644 --- a/blocks/api/factory.js +++ b/blocks/api/factory.js @@ -29,7 +29,7 @@ export function createBlock( blockType, attributes = {} ) { * * @param {Object} block Block object * @param {string} blockType BlockType - * @return {Object?} Block object + * @return {?Object|Error} Block object, null, or error with failure message */ export function switchToBlockType( block, blockType ) { // Find the right transformation by giving priority to the "to" transformation @@ -45,9 +45,15 @@ export function switchToBlockType( block, blockType ) { return null; } + const attributes = transformation.transform( block.attributes ); + if ( attributes instanceof Error ) { + // Blocks can perform validation and cancel transformations if needed. + return attributes; + } + return Object.assign( { uid: block.uid, - attributes: transformation.transform( block.attributes ), - blockType + attributes, + blockType, } ); } diff --git a/blocks/library/heading/index.js b/blocks/library/heading/index.js index 50a5c69ef9dc49..c83e1d1c62ea9d 100644 --- a/blocks/library/heading/index.js +++ b/blocks/library/heading/index.js @@ -38,8 +38,11 @@ registerBlock( 'core/heading', { blocks: [ 'core/text' ], transform: ( { content, align } ) => { if ( Array.isArray( content ) ) { - // TODO this appears to always be true? - // TODO reject the switch if more than one paragraph + if ( content.length > 1 ) { + return new Error( + 'Block has more than one paragraph.' + ); + } content = content[ 0 ]; } return { diff --git a/editor/components/block-switcher/index.js b/editor/components/block-switcher/index.js index e93df85dfd5896..25f517c3e03f2a 100644 --- a/editor/components/block-switcher/index.js +++ b/editor/components/block-switcher/index.js @@ -2,7 +2,7 @@ * External dependencies */ import { connect } from 'react-redux'; -import { uniq, get, reduce } from 'lodash'; +import { uniq, get, reduce, noop } from 'lodash'; /** * Internal dependencies @@ -35,10 +35,12 @@ class BlockSwitcher extends wp.element.Component { } render() { - const blockSettings = wp.blocks.getBlockSettings( this.props.block.blockType ); + const { block } = this.props; + + const blockSettings = wp.blocks.getBlockSettings( block.blockType ); const blocksToBeTransformedFrom = reduce( wp.blocks.getBlocks(), ( memo, block ) => { const transformFrom = get( block, 'transforms.from', [] ); - const transformation = transformFrom.find( t => t.blocks.indexOf( this.props.block.blockType ) !== -1 ); + const transformation = transformFrom.find( t => t.blocks.indexOf( block.blockType ) !== -1 ); return transformation ? memo.concat( [ block.slug ] ) : memo; }, [] ); const blocksToBeTransformedTo = get( blockSettings, 'transforms.to', [] ) @@ -65,21 +67,36 @@ class BlockSwitcher extends wp.element.Component { { this.state.open &&
- { allowedBlocks.map( ( { slug, title, icon } ) => ( - - { title } - - ) ) } + { allowedBlocks.map( + newBlock => this.renderAllowedBlock( block, newBlock ) + ) }
}
); } + + renderAllowedBlock( currentBlock, newBlockSettings ) { + const newBlockAttributes = wp.blocks.switchToBlockType( + currentBlock, + newBlockSettings.slug + ); + const disabled = ( newBlockAttributes instanceof Error ); + const disabledMessage = disabled ? newBlockAttributes.message : null; + const { slug, title, icon } = newBlockSettings; + return ( + + { title } + + ); + } } export default connect( @@ -88,10 +105,14 @@ export default connect( } ), ( dispatch, ownProps ) => ( { onTransform( block, blockType ) { + block = wp.blocks.switchToBlockType( block, blockType ); + if ( block instanceof Error ) { + return; + } dispatch( { type: 'SWITCH_BLOCK_TYPE', uid: ownProps.uid, - block: wp.blocks.switchToBlockType( block, blockType ) + block, } ); } } ) diff --git a/editor/components/icon-button/index.js b/editor/components/icon-button/index.js index d99307a7eff5e8..902a5be8bfb2ea 100644 --- a/editor/components/icon-button/index.js +++ b/editor/components/icon-button/index.js @@ -10,11 +10,19 @@ import './style.scss'; import Button from '../button'; import Dashicon from '../dashicon'; -function IconButton( { icon, children, label, className, ...additionalProps } ) { +function IconButton( { + icon, children, label, className, tooltip, + ...additionalProps +} ) { const classes = classnames( 'editor-icon-button', className ); return ( - From 0b44dcdf253d58a307dec5ef03f4cb21486b9454 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 20 Apr 2017 14:53:16 +0200 Subject: [PATCH 3/8] Add transformations between quote and text blocks --- blocks/library/quote/index.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index a88a47bdf5cbb3..4f714aa5239b03 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -20,6 +20,36 @@ registerBlock( 'core/quote', { citation: html( 'footer' ) }, + transforms: { + from: [ + { + type: 'block', + blocks: [ 'core/text' ], + transform: ( { content, align } ) => { + return { + value: content, + }; + }, + }, + ], + to: [ + { + type: 'block', + blocks: [ 'core/text' ], + transform: ( { value, citation } ) => { + if ( citation ) { + return new Error( + 'Quote citation would be lost on transform.' + ); + } + return { + content: value, + }; + }, + }, + ], + }, + edit( { attributes, setAttributes } ) { const { value, citation } = attributes; From decdfcbde89a849d4302cca371f545ba195fe3f5 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 20 Apr 2017 15:00:11 +0200 Subject: [PATCH 4/8] Fix lint errors --- blocks/library/quote/index.js | 2 +- editor/components/block-switcher/index.js | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index 4f714aa5239b03..5ba78ef507378b 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -25,7 +25,7 @@ registerBlock( 'core/quote', { { type: 'block', blocks: [ 'core/text' ], - transform: ( { content, align } ) => { + transform: ( { content } ) => { return { value: content, }; diff --git a/editor/components/block-switcher/index.js b/editor/components/block-switcher/index.js index 25f517c3e03f2a..ba2a27fd3eea72 100644 --- a/editor/components/block-switcher/index.js +++ b/editor/components/block-switcher/index.js @@ -38,17 +38,23 @@ class BlockSwitcher extends wp.element.Component { const { block } = this.props; const blockSettings = wp.blocks.getBlockSettings( block.blockType ); - const blocksToBeTransformedFrom = reduce( wp.blocks.getBlocks(), ( memo, block ) => { - const transformFrom = get( block, 'transforms.from', [] ); - const transformation = transformFrom.find( t => t.blocks.indexOf( block.blockType ) !== -1 ); - return transformation ? memo.concat( [ block.slug ] ) : memo; - }, [] ); + const blocksToBeTransformedFrom = reduce( + wp.blocks.getBlocks(), + ( memo, candidateBlock ) => { + const transformFrom = get( candidateBlock, 'transforms.from', [] ); + const transformation = transformFrom.find( + t => t.blocks.indexOf( candidateBlock.blockType ) !== -1 + ); + return transformation ? memo.concat( [ candidateBlock.slug ] ) : memo; + }, + [] + ); const blocksToBeTransformedTo = get( blockSettings, 'transforms.to', [] ) .reduce( ( memo, transformation ) => memo.concat( transformation.blocks ), [] ); const allowedBlocks = uniq( blocksToBeTransformedFrom.concat( blocksToBeTransformedTo ) ) .reduce( ( memo, blockType ) => { - const block = wp.blocks.getBlockSettings( blockType ); - return !! block ? memo.concat( block ) : memo; + const settings = wp.blocks.getBlockSettings( blockType ); + return !! settings ? memo.concat( settings ) : memo; }, [] ); if ( ! allowedBlocks.length ) { From c30cd9260283c5abbbf0adef090a45e8039e3017 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 20 Apr 2017 15:00:16 +0200 Subject: [PATCH 5/8] Add a test for the new functionality --- blocks/api/test/factory.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/blocks/api/test/factory.js b/blocks/api/test/factory.js index cb1a0265738d59..e41c9fb2050e3e 100644 --- a/blocks/api/test/factory.js +++ b/blocks/api/test/factory.js @@ -32,7 +32,7 @@ describe( 'block factory', () => { } ); describe( 'switchBlockType()', () => { - it( 'should switch the blockType of a block using the "transform form"', () => { + it( 'should switch the blockType of a block using the "transform from"', () => { registerBlock( 'core/updated-text-block', { transforms: { from: [ { @@ -116,5 +116,30 @@ describe( 'block factory', () => { expect( updateBlock ).to.be.null(); } ); + + it( 'should allow blocks to abort a transformation', () => { + registerBlock( 'core/updated-text-block', {} ); + registerBlock( 'core/text-block', { + transforms: { + to: [ { + blocks: [ 'core/updated-text-block' ], + transform: () => new Error( 'no soup for you' ), + } ] + } + } ); + + const block = { + uid: 1, + blockType: 'core/text-block', + attributes: { + value: 'ribs' + } + }; + + const updateBlock = switchToBlockType( block, 'core/updated-text-block' ); + + expect( updateBlock ).to.be.an.instanceof( Error ); + expect( updateBlock.message ).to.eql( 'no soup for you' ); + } ); } ); } ); From d79463b953aa267c05fdf080f347cb17f6a1a770 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 20 Apr 2017 15:10:04 +0200 Subject: [PATCH 6/8] Rename quote block 'value' to 'content' (this matches the text block) --- blocks/library/quote/index.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index 5ba78ef507378b..f9eaad577f5c6f 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -16,7 +16,7 @@ registerBlock( 'core/quote', { category: 'common', attributes: { - value: query( 'blockquote > p', html() ), + content: query( 'blockquote > p', html() ), citation: html( 'footer' ) }, @@ -27,7 +27,7 @@ registerBlock( 'core/quote', { blocks: [ 'core/text' ], transform: ( { content } ) => { return { - value: content, + content, }; }, }, @@ -36,14 +36,14 @@ registerBlock( 'core/quote', { { type: 'block', blocks: [ 'core/text' ], - transform: ( { value, citation } ) => { + transform: ( { content, citation } ) => { if ( citation ) { return new Error( 'Quote citation would be lost on transform.' ); } return { - content: value, + content, }; }, }, @@ -51,15 +51,15 @@ registerBlock( 'core/quote', { }, edit( { attributes, setAttributes } ) { - const { value, citation } = attributes; + const { content, citation } = attributes; return (
setAttributes( { - value: fromParagraphsToValue( paragraphs ) + content: fromParagraphsToValue( paragraphs ) } ) } />