Skip to content

Commit

Permalink
Test: Move the merge block handler to the actions file and unit test it
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed May 16, 2017
1 parent 022d0df commit 092fe89
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 54 deletions.
55 changes: 55 additions & 0 deletions editor/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@
import { get } from 'lodash';
import { parse, stringify } from 'querystring';

export function focusBlock( uid, config ) {
return {
type: 'UPDATE_FOCUS',
uid,
config,
};
}

export function replaceBlocks( uids, blocks ) {
return {
type: 'REPLACE_BLOCKS',
uids,
blocks,
};
}

export function savePost( dispatch, postId, edits ) {
const toSend = postId ? { id: postId, ...edits } : edits;
const isNew = ! postId;
Expand Down Expand Up @@ -41,3 +57,42 @@ export function savePost( dispatch, postId, edits ) {
} );
} );
}

export function mergeBlocks( dispatch, blockA, blockB ) {
const blockASettings = wp.blocks.getBlockSettings( blockA.blockType );

// Only focus the previous block if it's not mergeable
if ( ! blockASettings.merge ) {
dispatch( focusBlock( blockA.uid ) );
return;
}

// We can only merge blocks with similar types
// thus, we transform the block to merge first
const blocksWithTheSameType = blockA.blockType === blockB.blockType
? [ blockB ]
: wp.blocks.switchToBlockType( blockB, blockA.blockType );

// If the block types can not match, do nothing
if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) {
return;
}

// Calling the merge to update the attributes and remove the block to be merged
const updatedAttributes = blockASettings.merge( blockA.attributes, blocksWithTheSameType[ 0 ].attributes );

dispatch( focusBlock( blockA.uid, { offset: -1 } ) );
dispatch( replaceBlocks(
[ blockA.uid, blockB.uid ],
[
{
...blockA,
attributes: {
...blockA.attributes,
...updatedAttributes,
},
},
...blocksWithTheSameType.slice( 1 ),
]
) );
}
10 changes: 5 additions & 5 deletions editor/block-switcher/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Dashicon from 'components/dashicon';
* Internal dependencies
*/
import './style.scss';
import { replaceBlocks } from '../actions';
import { getBlock } from '../selectors';

class BlockSwitcher extends wp.element.Component {
Expand Down Expand Up @@ -111,11 +112,10 @@ export default connect(
} ),
( dispatch, ownProps ) => ( {
onTransform( block, blockType ) {
dispatch( {
type: 'REPLACE_BLOCKS',
uids: [ ownProps.uid ],
blocks: wp.blocks.switchToBlockType( block, blockType ),
} );
dispatch( replaceBlocks(
[ ownProps.uid ],
wp.blocks.switchToBlockType( block, blockType )
) );
},
} )
)( clickOutside( BlockSwitcher ) );
56 changes: 7 additions & 49 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Toolbar from 'components/toolbar';
*/
import BlockMover from '../../block-mover';
import BlockSwitcher from '../../block-switcher';
import { focusBlock, mergeBlocks } from '../../actions';
import {
getPreviousBlock,
getBlock,
Expand Down Expand Up @@ -101,49 +102,14 @@ class VisualEditorBlock extends wp.element.Component {
}

mergeWithPrevious() {
const { block, previousBlock, onFocus, replaceBlocks } = this.props;
const { block, previousBlock, onMerge } = this.props;

// Do nothing when it's the first block
if ( ! previousBlock ) {
return;
}

const previousBlockSettings = wp.blocks.getBlockSettings( previousBlock.blockType );

// Do nothing if the previous block is not mergeable
if ( ! previousBlockSettings.merge ) {
onFocus( previousBlock.uid );
return;
}

// We can only merge blocks with similar types
// thus, we transform the block to merge first
const blocksWithTheSameType = previousBlock.blockType === block.blockType
? [ block ]
: wp.blocks.switchToBlockType( block, previousBlock.blockType );

// If the block types can not match, do nothing
if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) {
return;
}

// Calling the merge to update the attributes and remove the block to be merged
const updatedAttributes = previousBlockSettings.merge( previousBlock.attributes, blocksWithTheSameType[ 0 ].attributes );

onFocus( previousBlock.uid, { offset: -1 } );
replaceBlocks(
[ previousBlock.uid, block.uid ],
[
{
...previousBlock,
attributes: {
...previousBlock.attributes,
...updatedAttributes,
},
},
...blocksWithTheSameType.slice( 1 ),
]
);
onMerge( previousBlock, block );
}

componentDidUpdate( prevProps ) {
Expand Down Expand Up @@ -316,12 +282,8 @@ export default connect(
} );
},

onFocus( uid, config ) {
dispatch( {
type: 'UPDATE_FOCUS',
uid,
config,
} );
onFocus( ...args ) {
dispatch( focusBlock( ...args ) );
},

onRemove( uid ) {
Expand All @@ -331,12 +293,8 @@ export default connect(
} );
},

replaceBlocks( uids, blocks ) {
dispatch( {
type: 'REPLACE_BLOCKS',
uids,
blocks,
} );
onMerge( ...args ) {
mergeBlocks( dispatch, ...args );
},
} )
)( VisualEditorBlock );
168 changes: 168 additions & 0 deletions editor/test/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* External dependencies
*/
import { expect } from 'chai';
import sinon from 'sinon';

/**
* WordPress dependencies
*/
import { getBlocks, unregisterBlock, registerBlock, createBlock } from 'blocks';

/**
* Internal dependencies
*/
import { focusBlock, replaceBlocks, mergeBlocks } from '../actions';

describe( 'actions', () => {
describe( 'focusBlock', () => {
it( 'should return the UPDATE_FOCUS action', () => {
const focusConfig = {
editable: 'cite',
};

expect( focusBlock( 'chicken', focusConfig ) ).to.eql( {
type: 'UPDATE_FOCUS',
uid: 'chicken',
config: focusConfig,
} );
} );
} );

describe( 'replaceBlocks', () => {
it( 'should return the REPLACE_BLOCKS action', () => {
const blocks = [ {
uid: 'ribs',
} ];

expect( replaceBlocks( [ 'chicken' ], blocks ) ).to.eql( {
type: 'REPLACE_BLOCKS',
uids: [ 'chicken' ],
blocks,
} );
} );
} );

describe( 'mergeBlocks', () => {
afterEach( () => {
getBlocks().forEach( ( block ) => {
unregisterBlock( block.slug );
} );
} );

it( 'should only focus the blockA if the blockA has no merge function', () => {
registerBlock( 'core/test-block', {} );
const blockA = {
uid: 'chicken',
blockType: 'core/test-block',
};
const blockB = {
uid: 'ribs',
blockType: 'core/test-block',
};
const dispatch = sinon.spy();
mergeBlocks( dispatch, blockA, blockB );

expect( dispatch ).to.have.been.calledOnce();
expect( dispatch ).to.have.been.calledWith( focusBlock( 'chicken' ) );
} );

it( 'should merge the blocks if blocks of the same type', () => {
registerBlock( 'core/test-block', {
merge( attributes, attributesToMerge ) {
return {
content: attributes.content + ' ' + attributesToMerge.content,
};
},
} );
const blockA = {
uid: 'chicken',
blockType: 'core/test-block',
attributes: { content: 'chicken' },
};
const blockB = {
uid: 'ribs',
blockType: 'core/test-block',
attributes: { content: 'ribs' },
};
const dispatch = sinon.spy();
mergeBlocks( dispatch, blockA, blockB );

expect( dispatch ).to.have.been.calledTwice();
expect( dispatch ).to.have.been.calledWith( focusBlock( 'chicken', { offset: -1 } ) );
expect( dispatch ).to.have.been.calledWith( replaceBlocks( [ 'chicken', 'ribs' ], [ {
uid: 'chicken',
blockType: 'core/test-block',
attributes: { content: 'chicken ribs' },
} ] ) );
} );

it( 'should not merge the blocks have different types without transformation', () => {
registerBlock( 'core/test-block', {
merge( attributes, attributesToMerge ) {
return {
content: attributes.content + ' ' + attributesToMerge.content,
};
},
} );
registerBlock( 'core/test-block-2', {} );
const blockA = {
uid: 'chicken',
blockType: 'core/test-block',
attributes: { content: 'chicken' },
};
const blockB = {
uid: 'ribs',
blockType: 'core/test-block2',
attributes: { content: 'ribs' },
};
const dispatch = sinon.spy();
mergeBlocks( dispatch, blockA, blockB );

expect( dispatch ).to.have.not.been.called();
} );

it( 'should transform and merge the blocks', () => {
registerBlock( 'core/test-block', {
merge( attributes, attributesToMerge ) {
return {
content: attributes.content + ' ' + attributesToMerge.content,
};
},
} );
registerBlock( 'core/test-block-2', {
transforms: {
to: [ {
type: 'blocks',
blocks: [ 'core/test-block' ],
transform: ( { content2 } ) => {
return createBlock( 'core/test-block', {
content: content2,
} );
},
} ],
},
} );
const blockA = {
uid: 'chicken',
blockType: 'core/test-block',
attributes: { content: 'chicken' },
};
const blockB = {
uid: 'ribs',
blockType: 'core/test-block-2',
attributes: { content2: 'ribs' },
};
const dispatch = sinon.spy();
mergeBlocks( dispatch, blockA, blockB );

expect( dispatch ).to.have.been.calledTwice();
expect( dispatch ).to.have.been.calledWith( focusBlock( 'chicken', { offset: -1 } ) );
expect( dispatch ).to.have.been.calledWith( replaceBlocks( [ 'chicken', 'ribs' ], [ {
uid: 'chicken',
blockType: 'core/test-block',
attributes: { content: 'chicken ribs' },
} ] ) );
} );
} );
} );

0 comments on commit 092fe89

Please sign in to comment.