Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge the focus and the selected contcepts
  • Loading branch information
youknowriad committed Apr 20, 2017
commit 6db8d8e6d5208c1c33d64873a8cf9f0ef874002a
6 changes: 1 addition & 5 deletions blocks/library/image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@ registerBlock( 'core/image', {
edit( { attributes, setAttributes, focus, updateFocus } ) {
const { url, alt, caption } = attributes;

// Disable reason: Clicking the image should set the focus to its caption

/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/onclick-has-role, jsx-a11y/no-static-element-interactions */
return (
<figure>
<img src={ url } alt={ alt } onClick={ updateFocus } />
<img src={ url } alt={ alt } />
{ caption || !! focus ? (
<Editable
tagName="figcaption"
Expand All @@ -39,7 +36,6 @@ registerBlock( 'core/image', {
) : null }
</figure>
);
/* eslint-enable jsx-a11y/click-events-have-key-events, jsx-a11y/onclick-has-role, jsx-a11y/no-static-element-interactions */
},

save( { attributes } ) {
Expand Down
30 changes: 18 additions & 12 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ class VisualEditorBlock extends wp.element.Component {
return null;
}

const { isHovered, isSelected, focus } = this.props;
const { isHovered, isSelected, isTyping, focus } = this.props;
const className = classnames( 'editor-visual-editor__block', {
'is-selected': isSelected,
'is-selected': isSelected && ! isTyping,
'is-hovered': isHovered
} );

const { onSelect, onDeselect, onMouseEnter, onMouseLeave, onInsertAfter, onFocus } = this.props;
const { onSelect, onStartTyping, onMouseMove, onMouseLeave, onFocus, onInsertAfter } = this.props;

// Disable reason: Each block can receive focus but must be able to contain
// block children. Tab keyboard navigation enabled by tabIndex assignment.
Expand All @@ -93,15 +93,15 @@ class VisualEditorBlock extends wp.element.Component {
tabIndex="0"
onFocus={ onSelect }
onBlur={ this.maybeDeselect }
onKeyDown={ onDeselect }
onMouseEnter={ onMouseEnter }
onKeyDown={ onStartTyping }
onMouseMove={ onMouseMove }
onMouseLeave={ onMouseLeave }
className={ className }
>
{ ( isSelected || isHovered ) && <BlockMover uid={ block.uid } /> }
{ ( ( isSelected && ! isTyping ) || isHovered ) && <BlockMover uid={ block.uid } /> }
<div className="editor-visual-editor__block-controls">
{ isSelected && <BlockSwitcher uid={ block.uid } /> }
{ isSelected && settings.controls ? (
{ isSelected && ! isTyping && <BlockSwitcher uid={ block.uid } /> }
{ isSelected && ! isTyping && settings.controls ? (
<Toolbar
controls={ settings.controls.map( ( control ) => ( {
...control,
Expand All @@ -111,7 +111,6 @@ class VisualEditorBlock extends wp.element.Component {
) : null }
</div>
<BlockEdit
isSelected={ isSelected }
focus={ focus }
attributes={ block.attributes }
setAttributes={ this.setAttributes }
Expand All @@ -128,9 +127,10 @@ export default connect(
( state, ownProps ) => ( {
order: state.blocks.order.indexOf( ownProps.uid ),
block: state.blocks.byUid[ ownProps.uid ],
isSelected: state.selectedBlock === ownProps.uid,
isSelected: state.selectedBlock.uid === ownProps.uid,
isHovered: state.hoveredBlock === ownProps.uid,
focus: state.focus.uid === ownProps.uid ? state.focus.config : null
focus: state.selectedBlock.uid === ownProps.uid ? state.selectedBlock.focus : null,
isTyping: state.selectedBlock.uid === ownProps.uid ? state.selectedBlock.typing : false,
} ),
( dispatch, ownProps ) => ( {
onChange( updates ) {
Expand All @@ -154,7 +154,13 @@ export default connect(
uid: ownProps.uid
} );
},
onMouseEnter() {
onStartTyping() {
dispatch( {
type: 'START_TYPING',
uid: ownProps.uid
} );
},
onMouseMove() {
dispatch( {
type: 'TOGGLE_BLOCK_HOVERED',
hovered: true,
Expand Down
62 changes: 38 additions & 24 deletions editor/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,49 @@ export const blocks = combineUndoableReducers( {
* @param {Object} action Dispatched action
* @return {Object} Updated state
*/
export function selectedBlock( state = null, action ) {
export function selectedBlock( state = {}, action ) {
switch ( action.type ) {
case 'TOGGLE_BLOCK_SELECTED':
return action.selected ? action.uid : null;
if ( ! action.selected ) {
return state.uid === action.uid ? {} : state;
}
return action.uid === state.uid
? state
: { uid: action.uid, typing: false, focus: {} };

case 'MOVE_BLOCK_UP':
case 'MOVE_BLOCK_DOWN':
return action.uid;
return action.uid === state.uid
? state
: { uid: action.uid, typing: false, focus: {} };

case 'INSERT_BLOCK':
return action.block.uid;
return {
uid: action.block.uid,
typing: false,
focus: {}
};

case 'UPDATE_FOCUS':
return {
uid: action.uid,
typing: state.uid === action.uid ? state.typing : false,
focus: action.config
};

case 'START_TYPING':
if ( action.uid !== state.uid ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does typing consideration need to be per-block? Seems like a global effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like implemented for now "START_TYPING" is per-block. It's triggered when typing inside a specific block, we can drop the uid. I feel it should stay as is unless we catch the start typing in a higher level maybe?

return {
uid: action.uid,
typing: true,
focus: {}
};
}

return {
...state,
typing: true
};
}

return state;
Expand All @@ -133,25 +165,8 @@ export function hoveredBlock( state = null, action ) {
return null;
}
break;
}

return state;
}

/**
* Reducer returning the focused block state.
*
* @param {Object} state Current state
* @param {Object} action Dispatched action
* @return {Object} Updated state
*/
export function focus( state = {}, action ) {
switch ( action.type ) {
case 'UPDATE_FOCUS':
return {
uid: action.uid,
config: action.config
};
case 'START_TYPING':
return null;
}

return state;
Expand Down Expand Up @@ -190,7 +205,6 @@ export function isSidebarOpened( state = false, action ) {
export function createReduxStore() {
const reducer = combineReducers( {
blocks,
focus,
selectedBlock,
hoveredBlock,
mode,
Expand Down
120 changes: 89 additions & 31 deletions editor/test/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
*/
import { expect } from 'chai';
import { values } from 'lodash';
import deepFreeze from 'deep-freeze';

/**
* Internal dependencies
*/
import {
blocks,
focus,
hoveredBlock,
selectedBlock,
mode,
Expand Down Expand Up @@ -227,7 +227,40 @@ describe( 'state', () => {
selected: true
} );

expect( state ).to.equal( 'kumquat' );
expect( state ).to.eql( { uid: 'kumquat', typing: false, focus: {} } );
} );

it( 'should not update the state if already selected', () => {
const original = deepFreeze( { uid: 'kumquat', typing: true, focus: {} } );
const state = selectedBlock( original, {
type: 'TOGGLE_BLOCK_SELECTED',
uid: 'kumquat',
selected: true
} );

expect( state ).to.equal( original );
} );

it( 'should unselect the block if currently selected', () => {
const original = deepFreeze( { uid: 'kumquat', typing: true, focus: {} } );
const state = selectedBlock( original, {
type: 'TOGGLE_BLOCK_SELECTED',
uid: 'kumquat',
selected: false
} );

expect( state ).to.eql( {} );
} );

it( 'should not unselect the block if another block is selected', () => {
const original = deepFreeze( { uid: 'loquat', typing: true, focus: {} } );
const state = selectedBlock( original, {
type: 'TOGGLE_BLOCK_SELECTED',
uid: 'kumquat',
selected: false
} );

expect( state ).to.equal( original );
} );

it( 'should return with inserted block', () => {
Expand All @@ -239,7 +272,7 @@ describe( 'state', () => {
}
} );

expect( state ).to.equal( 'ribs' );
expect( state ).to.eql( { uid: 'ribs', typing: false, focus: {} } );
} );

it( 'should return with block moved up', () => {
Expand All @@ -248,7 +281,7 @@ describe( 'state', () => {
uid: 'ribs'
} );

expect( state ).to.equal( 'ribs' );
expect( state ).to.eql( { uid: 'ribs', typing: false, focus: {} } );
} );

it( 'should return with block moved down', () => {
Expand All @@ -257,7 +290,57 @@ describe( 'state', () => {
uid: 'chicken'
} );

expect( state ).to.equal( 'chicken' );
expect( state ).to.eql( { uid: 'chicken', typing: false, focus: {} } );
} );

it( 'should not update the state if the block moved is already selected', () => {
const original = deepFreeze( { uid: 'ribs', typing: true, focus: {} } );
const state = selectedBlock( original, {
type: 'MOVE_BLOCK_UP',
uid: 'ribs'
} );

expect( state ).to.equal( original );
} );

it( 'should update the focus and selects the block', () => {
const state = selectedBlock( undefined, {
type: 'UPDATE_FOCUS',
uid: 'chicken',
config: { editable: 'citation' }
} );

expect( state ).to.eql( { uid: 'chicken', typing: false, focus: { editable: 'citation' } } );
} );

it( 'should update the focus and merge the existing state', () => {
const original = deepFreeze( { uid: 'ribs', typing: true, focus: {} } );
const state = selectedBlock( original, {
type: 'UPDATE_FOCUS',
uid: 'ribs',
config: { editable: 'citation' }
} );

expect( state ).to.eql( { uid: 'ribs', typing: true, focus: { editable: 'citation' } } );
} );

it( 'should set the typing flag and selects the block', () => {
const state = selectedBlock( undefined, {
type: 'START_TYPING',
uid: 'chicken'
} );

expect( state ).to.eql( { uid: 'chicken', typing: true, focus: {} } );
} );

it( 'should set the typing flag and merge the existing state', () => {
const original = deepFreeze( { uid: 'ribs', typing: false, focus: { editable: 'citation' } } );
const state = selectedBlock( original, {
type: 'START_TYPING',
uid: 'ribs'
} );

expect( state ).to.eql( { uid: 'ribs', typing: true, focus: { editable: 'citation' } } );
} );

it( 'should insert after the specified block uid', () => {
Expand Down Expand Up @@ -321,30 +404,6 @@ describe( 'state', () => {
} );
} );

describe( 'focus()', () => {
it( 'should return an empty object by default', () => {
const state = focus( undefined, {} );
expect( state ).to.eql( {} );
} );

it( 'should update the focused block', () => {
const state = focus( null, {
type: 'UPDATE_FOCUS',
uid: 'chicken',
config: {
editable: 'ribs'
}
} );

expect( state ).to.eql( {
uid: 'chicken',
config: {
editable: 'ribs'
}
} );
} );
} );

describe( 'createReduxStore()', () => {
it( 'should return a redux store', () => {
const store = createReduxStore();
Expand All @@ -362,8 +421,7 @@ describe( 'state', () => {
'selectedBlock',
'hoveredBlock',
'mode',
'isSidebarOpened',
'focus'
'isSidebarOpened'
] );
} );
} );
Expand Down
2 changes: 1 addition & 1 deletion languages/gutenberg.pot
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ msgstr ""
msgid "Image"
msgstr ""

#: blocks/library/image/index.js:34
#: blocks/library/image/index.js:31
msgid "Write caption…"
msgstr ""

Expand Down