From e09640e8b37eb941e1b395b6d4e2aa426e7a3aee Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 18 May 2017 10:05:23 -0400 Subject: [PATCH 1/3] Clear selected block by clicking canvas or escape key --- editor/modes/visual-editor/block.js | 21 +++++++-------- editor/modes/visual-editor/index.js | 41 +++++++++++++++++++++++++---- editor/state.js | 3 +++ editor/test/state.js | 9 +++++++ 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index e3230769e08e7..56dc40aacdf57 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -37,11 +37,11 @@ class VisualEditorBlock extends wp.element.Component { super( ...arguments ); this.bindBlockNode = this.bindBlockNode.bind( this ); this.setAttributes = this.setAttributes.bind( this ); - this.maybeDeselect = this.maybeDeselect.bind( this ); this.maybeHover = this.maybeHover.bind( this ); this.maybeStartTyping = this.maybeStartTyping.bind( this ); this.removeOnBackspace = this.removeOnBackspace.bind( this ); this.mergeBlocks = this.mergeBlocks.bind( this ); + this.selectAndStopPropagation = this.selectAndStopPropagation.bind( this ); this.previousOffset = null; } @@ -76,14 +76,6 @@ class VisualEditorBlock extends wp.element.Component { } } - maybeDeselect( event ) { - // Annoyingly React does not support focusOut and we're forced to check - // related target to ensure it's not a child when blur fires. - if ( ! event.currentTarget.contains( event.relatedTarget ) ) { - this.props.onDeselect(); - } - } - maybeStartTyping() { // We do not want to dispatch start typing if... // - State value already reflects that we're typing (dispatch noise) @@ -124,6 +116,14 @@ class VisualEditorBlock extends wp.element.Component { } } + selectAndStopPropagation( event ) { + this.props.onSelect(); + + // Visual editor infers click as intent to clear the selected block, so + // prevent bubbling when occurring on block where selection is intended + event.stopPropagation(); + } + componentDidUpdate( prevProps ) { if ( this.previousOffset ) { window.scrollTo( @@ -178,9 +178,8 @@ class VisualEditorBlock extends wp.element.Component { return (
+
{ blocks.map( ( uid ) => ( @@ -22,8 +47,14 @@ function VisualEditor( { blocks } ) {
); + /* eslint-enable jsx-a11y/no-static-element-interactions */ } -export default connect( ( state ) => ( { - blocks: getBlockUids( state ), -} ) )( VisualEditor ); +export default connect( + ( state ) => ( { + blocks: getBlockUids( state ), + } ), + ( dispatch ) => ( { + clearSelectedBlock: () => dispatch( { type: 'CLEAR_SELECTED_BLOCK' } ), + } ) +)( VisualEditor ); diff --git a/editor/state.js b/editor/state.js index 9e1a73e612d72..0c9bd344f0f75 100644 --- a/editor/state.js +++ b/editor/state.js @@ -197,6 +197,9 @@ export function selectedBlock( state = {}, action ) { focus: action.uid === state.uid ? state.focus : {}, }; + case 'CLEAR_SELECTED_BLOCK': + return {}; + case 'MOVE_BLOCK_UP': case 'MOVE_BLOCK_DOWN': return action.uid === state.uid diff --git a/editor/test/state.js b/editor/test/state.js index 1d5b9daac64c1..4808202f2c34b 100644 --- a/editor/test/state.js +++ b/editor/test/state.js @@ -408,6 +408,15 @@ describe( 'state', () => { expect( state ).to.eql( { uid: 'kumquat', typing: false, focus: {} } ); } ); + it( 'returns an empty object when clearing selected block', () => { + const original = deepFreeze( { uid: 'kumquat', typing: false, focus: {} } ); + const state = selectedBlock( original, { + type: 'CLEAR_SELECTED_BLOCK', + } ); + + expect( state ).to.eql( {} ); + } ); + it( 'should not update the state if already selected and not typing', () => { const original = deepFreeze( { uid: 'kumquat', typing: false, focus: {} } ); const state = selectedBlock( original, { From 535b475e1252561e36fdaad5f65f0b59dd9bd96f Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 18 May 2017 10:05:35 -0400 Subject: [PATCH 2/3] Clear selected block by click adjacent block controls --- editor/modes/visual-editor/style.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/modes/visual-editor/style.scss b/editor/modes/visual-editor/style.scss index 5e4793d7ec90b..354c865004fed 100644 --- a/editor/modes/visual-editor/style.scss +++ b/editor/modes/visual-editor/style.scss @@ -79,6 +79,8 @@ margin-top: -$block-controls-height - $item-spacing; margin-bottom: $item-spacing + 20px; // 20px is the offset from the bottom of the selected block where it stops sticking height: $block-controls-height; + width: 0; + white-space: nowrap; top: $header-height + $admin-bar-height-big + $item-spacing; From f5204297f44b85960f39fd4058da29ced17257b7 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 18 May 2017 14:15:55 -0400 Subject: [PATCH 3/3] Handle deselect by escape at block component --- editor/modes/visual-editor/block.js | 13 ++++++++++--- editor/modes/visual-editor/index.js | 20 ++++---------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 56dc40aacdf57..f612e32760921 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -39,7 +39,7 @@ class VisualEditorBlock extends wp.element.Component { this.setAttributes = this.setAttributes.bind( this ); this.maybeHover = this.maybeHover.bind( this ); this.maybeStartTyping = this.maybeStartTyping.bind( this ); - this.removeOnBackspace = this.removeOnBackspace.bind( this ); + this.removeOrDeselect = this.removeOrDeselect.bind( this ); this.mergeBlocks = this.mergeBlocks.bind( this ); this.selectAndStopPropagation = this.selectAndStopPropagation.bind( this ); this.previousOffset = null; @@ -88,14 +88,21 @@ class VisualEditorBlock extends wp.element.Component { } } - removeOnBackspace( event ) { + removeOrDeselect( event ) { const { keyCode, target } = event; + + // Remove block on backspace if ( 8 /* Backspace */ === keyCode && target === this.node ) { this.props.onRemove( this.props.uid ); if ( this.props.previousBlock ) { this.props.onFocus( this.props.previousBlock.uid, { offset: -1 } ); } } + + // Deselect on escape + if ( 27 /* Escape */ === event.keyCode ) { + this.props.onDeselect(); + } } mergeBlocks( forward = false ) { @@ -180,7 +187,7 @@ class VisualEditorBlock extends wp.element.Component { ref={ this.bindBlockNode } onClick={ this.selectAndStopPropagation } onFocus={ onSelect } - onKeyDown={ this.removeOnBackspace } + onKeyDown={ this.removeOrDeselect } onMouseEnter={ onHover } onMouseMove={ this.maybeHover } onMouseLeave={ onMouseLeave } diff --git a/editor/modes/visual-editor/index.js b/editor/modes/visual-editor/index.js index 822a2eb6c4c44..46a013ed73766 100644 --- a/editor/modes/visual-editor/index.js +++ b/editor/modes/visual-editor/index.js @@ -2,7 +2,6 @@ * External dependencies */ import { connect } from 'react-redux'; -import { cond } from 'lodash'; /** * WordPress dependencies @@ -18,26 +17,15 @@ import VisualEditorBlock from './block'; import PostTitle from '../../post-title'; import { getBlockUids } from '../../selectors'; -/** - * Returns true if the key pressed is the escape key, or false otherwise. - * - * @param {KeyboardEvent} event Key event to test - * @return {Boolean} Whether event is escape key press - */ -function isEscapeKey( event ) { - return 27 /* Escape */ === event.keyCode; -} - function VisualEditor( { blocks, clearSelectedBlock } ) { - // Disable reason: Focus transfers between blocks are handled by focusable - // block elements. Capture unhandled event bubbling as unselect intent. + // Disable reason: Focus transfer between blocks and key events are handled + // by focused block element. Consider unhandled click bubbling as unselect. - /* eslint-disable jsx-a11y/no-static-element-interactions */ + /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ return (
@@ -47,7 +35,7 @@ function VisualEditor( { blocks, clearSelectedBlock } ) {
); - /* eslint-enable jsx-a11y/no-static-element-interactions */ + /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ } export default connect(