Skip to content

Commit

Permalink
Input Interaction: set caret correctly on merge forward (Delete) (#15599
Browse files Browse the repository at this point in the history
)

* Input Interaction: set caret correctly on merge forward (Delete)

* Reset selected block attrs
  • Loading branch information
ellatrix authored May 14, 2019
1 parent bbb37b1 commit 5963776
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 35 deletions.
79 changes: 44 additions & 35 deletions packages/block-editor/src/store/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export default {
MERGE_BLOCKS( action, store ) {
const { dispatch } = store;
const state = store.getState();
const [ firstBlockClientId, secondBlockClientId ] = action.blocks;
const blockA = getBlock( state, firstBlockClientId );
const [ clientIdA, clientIdB ] = action.blocks;
const blockA = getBlock( state, clientIdA );
const blockAType = getBlockType( blockA.name );

// Only focus the previous block if it's not mergeable
Expand All @@ -80,27 +80,33 @@ export default {
return;
}

// We can only merge blocks with similar types
// thus, we transform the block to merge first
const blockB = getBlock( state, secondBlockClientId );
const blockB = getBlock( state, clientIdB );
const blockBType = getBlockType( blockB.name );
const { clientId, attributeKey, offset } = getSelectionStart( state );
const hasSelection = clientId === clientIdA || clientId === clientIdB;
const selectedBlock = clientId === clientIdA ? blockA : blockB;
const html = selectedBlock.attributes[ attributeKey ];

// A robust way to retain selection position through various transforms
// is to insert a special character at the position and then recover it.
const START_OF_SELECTED_AREA = '\u0086';
const { attributeKey, offset } = getSelectionStart( state );
const html = blockB.attributes[ attributeKey ];
const multilineTagB = blockBType.attributes[ attributeKey ].multiline;
const value = insert( create( {
html,
multilineTag: multilineTagB,
} ), START_OF_SELECTED_AREA, offset, offset );

blockB.attributes[ attributeKey ] = toHTMLString( {
value,
multilineTag: multilineTagB,
} );

if ( hasSelection ) {
const selectedBlockType = clientId === clientIdA ? blockAType : blockBType;
const multilineTag = selectedBlockType.attributes[ attributeKey ].multiline;
const value = insert( create( {
html,
multilineTag,
} ), START_OF_SELECTED_AREA, offset, offset );

selectedBlock.attributes[ attributeKey ] = toHTMLString( {
value,
multilineTag,
} );
}

// We can only merge blocks with similar types
// thus, we transform the block to merge first
const blocksWithTheSameType = blockA.name === blockB.name ?
[ blockB ] :
switchToBlockType( blockB, blockA.name );
Expand All @@ -116,24 +122,27 @@ export default {
blocksWithTheSameType[ 0 ].attributes
);

const newAttributeKey = findKey( updatedAttributes, ( v ) =>
typeof v === 'string' && v.indexOf( START_OF_SELECTED_AREA ) !== -1
);
const convertedHtml = updatedAttributes[ newAttributeKey ];
const multilineTagA = blockAType.attributes[ newAttributeKey ].multiline;
const convertedValue = create( { html: convertedHtml, multilineTag: multilineTagA } );
const newOffset = convertedValue.text.indexOf( START_OF_SELECTED_AREA );
const newValue = remove( convertedValue, newOffset, newOffset + 1 );
const newHtml = toHTMLString( { value: newValue, multilineTag: multilineTagA } );

updatedAttributes[ newAttributeKey ] = newHtml;

dispatch( selectionChange(
blockA.clientId,
newAttributeKey,
newOffset,
newOffset
) );
if ( hasSelection ) {
const newAttributeKey = findKey( updatedAttributes, ( v ) =>
typeof v === 'string' && v.indexOf( START_OF_SELECTED_AREA ) !== -1
);
const convertedHtml = updatedAttributes[ newAttributeKey ];
const multilineTag = blockAType.attributes[ newAttributeKey ].multiline;
const convertedValue = create( { html: convertedHtml, multilineTag } );
const newOffset = convertedValue.text.indexOf( START_OF_SELECTED_AREA );
const newValue = remove( convertedValue, newOffset, newOffset + 1 );
const newHtml = toHTMLString( { value: newValue, multilineTag } );

updatedAttributes[ newAttributeKey ] = newHtml;
selectedBlock.attributes[ attributeKey ] = html;

dispatch( selectionChange(
blockA.clientId,
newAttributeKey,
newOffset,
newOffset
) );
}

dispatch( replaceBlocks(
[ blockA.clientId, blockB.clientId ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ exports[`adding blocks should insert line break mid text 1`] = `
<!-- /wp:paragraph -->"
`;
exports[`adding blocks should merge forwards 1`] = `
"<!-- wp:paragraph -->
<p>123</p>
<!-- /wp:paragraph -->"
`;
exports[`adding blocks should navigate around inline boundaries 1`] = `
"<!-- wp:paragraph -->
<p>FirstAfter</p>
Expand Down
12 changes: 12 additions & 0 deletions packages/e2e-tests/specs/writing-flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,16 @@ describe( 'adding blocks', () => {

expect( await getEditedPostContent() ).toMatchSnapshot();
} );

it( 'should merge forwards', async () => {
await page.keyboard.press( 'Enter' );
await page.keyboard.type( '1' );
await page.keyboard.press( 'Enter' );
await page.keyboard.type( '3' );
await page.keyboard.press( 'ArrowUp' );
await page.keyboard.press( 'Delete' );
await page.keyboard.type( '2' );

expect( await getEditedPostContent() ).toMatchSnapshot();
} );
} );

0 comments on commit 5963776

Please sign in to comment.