Skip to content

Commit

Permalink
Merge pull request #360 from WordPress/add/selected-hover-state
Browse files Browse the repository at this point in the history
UI: Adding the selected and hover state for the visual editor
  • Loading branch information
aduth authored Apr 5, 2017
2 parents c03e7d9 + db21197 commit a33cd56
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 42 deletions.
67 changes: 61 additions & 6 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* External dependencies
*/
import { connect } from 'react-redux';
import classnames from 'classnames';

function VisualEditorBlock( { block, onChange } ) {
function VisualEditorBlock( props ) {
const { block } = props;
const settings = wp.blocks.getBlockSettings( block.blockType );

let BlockEdit;
Expand All @@ -16,24 +18,49 @@ function VisualEditorBlock( { block, onChange } ) {
}

function onAttributesChange( attributes ) {
onChange( {
props.onChange( {
attributes: {
...block.attributes,
...attributes
}
} );
}

const { isSelected, isHovered } = props;
const className = classnames( 'editor-visual-editor__block', {
'is-selected': isSelected,
'is-hovered': isHovered
} );

const { onSelect, onDeselect, onMouseEnter, onMouseLeave } = props;

// Disable reason: Each block can receive focus but must be able to contain
// block children. Tab keyboard navigation enabled by tabIndex assignment.

/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<BlockEdit
attributes={ block.attributes }
onChange={ onAttributesChange } />
<div
tabIndex="0"
onFocus={ onSelect }
onBlur={ onDeselect }
onKeyDown={ onDeselect }
onMouseEnter={ onMouseEnter }
onMouseLeave={ onMouseLeave }
className={ className }
>
<BlockEdit
attributes={ block.attributes }
onChange={ onAttributesChange } />
</div>
);
/* eslint-enable jsx-a11y/no-static-element-interactions */
}

export default connect(
( state, ownProps ) => ( {
block: state.blocks.byUid[ ownProps.uid ]
block: state.blocks.byUid[ ownProps.uid ],
isSelected: !! state.blocks.selected[ ownProps.uid ],
isHovered: !! state.blocks.hovered[ ownProps.uid ]
} ),
( dispatch, ownProps ) => ( {
onChange( updates ) {
Expand All @@ -42,6 +69,34 @@ export default connect(
uid: ownProps.uid,
updates
} );
},
onSelect() {
dispatch( {
type: 'TOGGLE_BLOCK_SELECTED',
selected: true,
uid: ownProps.uid
} );
},
onDeselect() {
dispatch( {
type: 'TOGGLE_BLOCK_SELECTED',
selected: false,
uid: ownProps.uid
} );
},
onMouseEnter() {
dispatch( {
type: 'TOGGLE_BLOCK_HOVERED',
hovered: true,
uid: ownProps.uid
} );
},
onMouseLeave() {
dispatch( {
type: 'TOGGLE_BLOCK_HOVERED',
hovered: false,
uid: ownProps.uid
} );
}
} )
)( VisualEditorBlock );
19 changes: 19 additions & 0 deletions editor/modes/visual-editor/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,23 @@
font-size: $editor-font-size;
line-height: $editor-line-height;
}

p {
margin-top: 0;
margin-bottom: 0;
}
}

.editor-visual-editor__block {
padding: 15px;
border: 2px solid transparent;
transition: 0.2s border-color;

&.is-hovered {
border-left: 2px solid $light-gray-500;
}

&.is-selected {
border: 2px solid $light-gray-500;
}
}
96 changes: 68 additions & 28 deletions editor/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,84 @@ export function html( state = null, action ) {
}

/**
* Reducer returning editor blocks state, an object with keys byUid and order,
* where blocks are parsed from current HTML markup.
* Reducer returning editor blocks state, an combined reducer of keys byUid,
* order, selected, hovered, where blocks are parsed from current HTML markup.
*
* @param {Object} state Current state
* @param {Object} action Dispatched action
* @return {Object} Updated state
*/
export function blocks( state, action ) {
if ( undefined === state ) {
state = {
byUid: {},
order: []
};
}
export const blocks = ( () => {
const reducer = combineReducers( {
byUid( state = {}, action ) {
switch ( action.type ) {
case 'SET_HTML':
return keyBy( action.blockNodes, 'uid' );

switch ( action.type ) {
case 'SET_HTML':
const blockNodes = wp.blocks.parse( action.html );
return {
byUid: keyBy( blockNodes, 'uid' ),
order: blockNodes.map( ( { uid } ) => uid )
};
case 'UPDATE_BLOCK':
return {
...state,
[ action.uid ]: {
...state[ action.uid ],
...action.updates
}
};
}

return state;
},
order( state = [], action ) {
switch ( action.type ) {
case 'SET_HTML':
return action.blockNodes.map( ( { uid } ) => uid );
}

return state;
},
selected( state = {}, action ) {
switch ( action.type ) {
case 'TOGGLE_BLOCK_SELECTED':
return {
...state,
[ action.uid ]: action.selected
};
}

case 'UPDATE_BLOCK':
return {
...state,
byUid: {
...state.byUid,
[ action.uid ]: {
...state.byUid[ action.uid ],
...action.updates
return state;
},
hovered( state = {}, action ) {
switch ( action.type ) {
case 'TOGGLE_BLOCK_HOVERED':
return {
...state,
[ action.uid ]: action.hovered
};

case 'TOGGLE_BLOCK_SELECTED':
if ( state[ action.uid ] ) {
return {
...state,
[ action.uid ]: false
};
}
}
break;
}

return state;
}
} );

return ( state, action ) => {
if ( 'SET_HTML' === action.type ) {
action = {
...action,
blockNodes: wp.blocks.parse( action.html )
};
}
}

return state;
}
return reducer( state, action );
};
} )();

/**
* Reducer returning current editor mode, either "visual" or "text".
Expand Down
64 changes: 57 additions & 7 deletions editor/test/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,23 @@ describe( 'state', () => {
wp.blocks.unregisterBlock( 'core/test-block' );
} );

it( 'should return empty byUid, order by default', () => {
it( 'should return empty byUid, order, selected, hovered by default', () => {
const state = blocks( undefined, {} );

expect( state ).to.eql( {
byUid: {},
order: []
order: [],
selected: {},
hovered: {}
} );
} );

it( 'should key set html blocks', () => {
const original = deepFreeze( {
byUid: {},
order: []
order: [],
selected: {},
hovered: {}
} );
const state = blocks( original, {
type: 'SET_HTML',
Expand All @@ -64,8 +68,6 @@ describe( 'state', () => {

expect( Object.keys( state.byUid ) ).to.have.lengthOf( 1 );
expect( values( state.byUid )[ 0 ].blockType ).to.equal( 'core/test-block' );
expect( state.order ).to.have.lengthOf( 1 );
expect( state.order[ 0 ] ).to.be.a( 'string' );
} );

it( 'should return with block updates', () => {
Expand All @@ -77,7 +79,9 @@ describe( 'state', () => {
attributes: {}
}
},
order: [ 'kumquat' ]
order: [ 'kumquat' ],
selected: {},
hovered: {}
} );
const state = blocks( original, {
type: 'UPDATE_BLOCK',
Expand All @@ -90,7 +94,53 @@ describe( 'state', () => {
} );

expect( state.byUid.kumquat.attributes.updated ).to.be.true();
expect( state.order[ 0 ] ).to.equal( 'kumquat' );
} );

it( 'should return with block uid as hovered', () => {
const original = deepFreeze( {
byUid: {
kumquat: {
uid: 'kumquat',
blockType: 'core/test-block',
attributes: {}
}
},
order: [ 'kumquat' ],
selected: {},
hovered: {}
} );
const state = blocks( original, {
type: 'TOGGLE_BLOCK_HOVERED',
uid: 'kumquat',
hovered: true
} );

expect( state.hovered.kumquat ).to.be.true();
} );

it( 'should return with block uid as selected, clearing hover', () => {
const original = deepFreeze( {
byUid: {
kumquat: {
uid: 'kumquat',
blockType: 'core/test-block',
attributes: {}
}
},
order: [ 'kumquat' ],
selected: {},
hovered: {
kumquat: true
}
} );
const state = blocks( original, {
type: 'TOGGLE_BLOCK_SELECTED',
uid: 'kumquat',
selected: true
} );

expect( state.hovered.kumquat ).to.be.false();
expect( state.selected.kumquat ).to.be.true();
} );
} );

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"babel-core": "^6.24.0",
"babel-eslint": "^7.2.0",
"babel-loader": "^6.4.1",
"babel-plugin-lodash": "^3.2.11",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-react-jsx": "^6.23.0",
"babel-plugin-transform-runtime": "^6.23.0",
Expand Down Expand Up @@ -55,7 +56,7 @@
"webpack-node-externals": "^1.5.4"
},
"dependencies": {
"babel-plugin-lodash": "^3.2.11",
"classnames": "^2.2.5",
"hpq": "^1.1.1",
"jed": "^1.1.1",
"lodash": "^4.17.4",
Expand Down

0 comments on commit a33cd56

Please sign in to comment.