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
Next Next commit
Inserter: Enable inserting a block using the inserter
 - Adding the INSERT_BLOCK action
 - Simplifying the hovered and selected reducers
  • Loading branch information
youknowriad committed Apr 13, 2017
commit a6d2631e39dc196b64c661b2b8dca7c04b2a1b29
9 changes: 8 additions & 1 deletion editor/components/inserter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Inserter extends wp.element.Component {
constructor() {
super( ...arguments );
this.toggle = this.toggle.bind( this );
this.close = this.close.bind( this );
this.state = {
opened: false
};
Expand All @@ -19,6 +20,12 @@ class Inserter extends wp.element.Component {
} );
}

close() {
this.setState( {
opened: false
} );
}

render() {
const { opened } = this.state;
const { position } = this.props;
Expand All @@ -30,7 +37,7 @@ class Inserter extends wp.element.Component {
label={ wp.i18n.__( 'Insert block' ) }
onClick={ this.toggle }
className="editor-inserter__toggle" />
{ opened && <InserterMenu position={ position } /> }
{ opened && <InserterMenu position={ position } onSelect={ this.close } /> }
</div>
);
}
Expand Down
124 changes: 89 additions & 35 deletions editor/components/inserter/menu.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,100 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';
import uuid from 'uuid/v4';

/**
* Internal dependencies
*/
import './style.scss';
import Dashicon from 'components/dashicon';

function InserterMenu( { position = 'top' } ) {
const blocks = wp.blocks.getBlocks();
const blocksByCategory = blocks.reduce( ( groups, block ) => {
if ( ! groups[ block.category ] ) {
groups[ block.category ] = [];
}
groups[ block.category ].push( block );
return groups;
}, {} );
const categories = wp.blocks.getCategories();
class InserterMenu extends wp.element.Component {
constructor() {
super( ...arguments );
this.state = {
filterValue: ''
};
this.filter = this.filter.bind( this );
}

filter( event ) {
this.setState( {
filterValue: event.target.value
} );
}

return (
<div className={ `editor-inserter__menu is-${ position }` }>
<div className="editor-inserter__arrow" />
<div className="editor-inserter__content">
{ categories
.map( ( category ) => !! blocksByCategory[ category.slug ] && (
<div key={ category.slug }>
<div className="editor-inserter__separator">{ category.title }</div>
<div className="editor-inserter__category-blocks">
{ blocksByCategory[ category.slug ].map( ( { slug, title, icon } ) => (
<div key={ slug } className="editor-inserter__block">
<Dashicon icon={ icon } />
{ title }
</div>
) ) }
selectBlock( slug ) {
return () => {
this.props.onInsertBlock( slug );
this.props.onSelect();
Copy link
Member

Choose a reason for hiding this comment

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

Should we clear the current filter value?

};
}

render() {
const { position = 'top' } = this.props;
const blocks = wp.blocks.getBlocks();
const isShownBlock = block => block.title.toLowerCase().indexOf( this.state.filterValue.toLowerCase() ) !== -1;
Copy link
Member

Choose a reason for hiding this comment

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

Total aside: How do you feel about a keywords property for a block that allows it to be discovered by more than just its title? Do we also want to search slug?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree this is too limited, we need to think more about how we search for blocks. Keywords seem like a good idea, we could consider adding a description to the blocks.

Copy link
Member

Choose a reason for hiding this comment

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

Another note is we should probably extract some of these utilities as standalone functions and unit test them. Fine for another pull request.

const blocksByCategory = blocks.reduce( ( groups, block ) => {
if ( ! isShownBlock( block ) ) {
return groups;
}
if ( ! groups[ block.category ] ) {
groups[ block.category ] = [];
}
groups[ block.category ].push( block );
return groups;
}, {} );
const categories = wp.blocks.getCategories();

return (
<div className={ `editor-inserter__menu is-${ position }` }>
<div className="editor-inserter__arrow" />
<div className="editor-inserter__content">
{ categories
.map( ( category ) => !! blocksByCategory[ category.slug ] && (
<div key={ category.slug }>
<div className="editor-inserter__separator">{ category.title }</div>
<div className="editor-inserter__category-blocks">
{ blocksByCategory[ category.slug ].map( ( { slug, title, icon } ) => (
<button
key={ slug }
className="editor-inserter__block"
onClick={ this.selectBlock( slug ) }
>
<Dashicon icon={ icon } />
{ title }
</button>
) ) }
</div>
</div>
</div>
) )
}
) )
}
</div>
<input
type="search"
placeholder={ wp.i18n.__( 'Search…' ) }
className="editor-inserter__search"
onChange={ this.filter }
/>
</div>
<input
type="search"
placeholder={ wp.i18n.__( 'Search…' ) }
className="editor-inserter__search" />
</div>
);
);
}
}

export default InserterMenu;
export default connect(
undefined,
( dispatch ) => ( {
onInsertBlock( slug ) {
dispatch( {
type: 'INSERT_BLOCK',
block: {
uid: uuid(),
blockType: slug,
attributes: {}
}
} );
}
} )
)( InserterMenu );
1 change: 1 addition & 0 deletions editor/components/inserter/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ input[type=search].editor-inserter__search {
align-items: center;
cursor: pointer;
border: 1px solid transparent;
background: none;

&:hover {
border: 1px solid $dark-gray-500;
Expand Down
4 changes: 2 additions & 2 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ function VisualEditorBlock( props ) {
export default connect(
( state, ownProps ) => ( {
block: state.blocks.byUid[ ownProps.uid ],
isSelected: !! state.blocks.selected[ ownProps.uid ],
isHovered: !! state.blocks.hovered[ ownProps.uid ]
isSelected: state.blocks.selected === ownProps.uid,
isHovered: state.blocks.hovered === ownProps.uid
} ),
( dispatch, ownProps ) => ( {
onChange( updates ) {
Expand Down
51 changes: 36 additions & 15 deletions editor/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export const blocks = combineReducers( {
...action.updates
}
};

case 'INSERT_BLOCK':
return {
...state,
[ action.block.uid ]: action.block
};
}

return state;
Expand Down Expand Up @@ -62,35 +68,50 @@ export const blocks = combineReducers( {
action.uid,
...state.slice( index + 2 )
];

case 'INSERT_BLOCK':
Copy link
Member

Choose a reason for hiding this comment

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

Maybe not here, but we'll need to consider how to insert a block at a specific index, supporting behavior like in this mockup:

Insert at index

https://wpcoredesign.mystagingwebsite.com/gutenberg/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this action can be extended. For example, in #409 I'm adding an option "after" property to this action to insert after a specific block.

return [
...state,
action.block.uid
];
}

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

if ( ! action.selected && action.uid === state ) {
return null;
}

return state;
case 'INSERT_BLOCK':
return action.block.uid;
}

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

if ( ! action.hovered && action.uid === state ) {
return null;
}

return state;

case 'TOGGLE_BLOCK_SELECTED':
if ( state[ action.uid ] ) {
return {
...state,
[ action.uid ]: false
};
if ( state === action.uid ) {
return null;
}
break;
}
Expand Down
56 changes: 41 additions & 15 deletions editor/test/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ describe( 'state', () => {
expect( state ).to.eql( {
byUid: {},
order: [],
selected: {},
hovered: {}
selected: null,
hovered: null
} );
} );

it( 'should key by replaced blocks uid', () => {
const original = deepFreeze( {
byUid: {},
order: [],
selected: {},
hovered: {}
selected: null,
hovered: null
} );
const state = blocks( original, {
type: 'REPLACE_BLOCKS',
Expand All @@ -62,8 +62,8 @@ describe( 'state', () => {
}
},
order: [ 'kumquat' ],
selected: {},
hovered: {}
selected: null,
hovered: null
} );
const state = blocks( original, {
type: 'UPDATE_BLOCK',
Expand All @@ -88,16 +88,16 @@ describe( 'state', () => {
}
},
order: [ 'kumquat' ],
selected: {},
hovered: {}
selected: null,
hovered: null
} );
const state = blocks( original, {
type: 'TOGGLE_BLOCK_HOVERED',
uid: 'kumquat',
hovered: true
} );

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

it( 'should return with block uid as selected, clearing hover', () => {
Expand All @@ -110,19 +110,45 @@ describe( 'state', () => {
}
},
order: [ 'kumquat' ],
selected: {},
hovered: {
kumquat: true
}
selected: null,
hovered: 'kumquat'
} );
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();
expect( state.hovered ).to.be.null();
expect( state.selected ).to.equal( 'kumquat' );
} );

it( 'should insert and select block', () => {
const original = deepFreeze( {
byUid: {
kumquat: {
uid: 'chicken',
blockType: 'core/test-block',
attributes: {}
}
},
order: [ 'chicken' ],
selected: null,
hovered: null
} );
const state = blocks( original, {
type: 'INSERT_BLOCK',
block: {
uid: 'ribs',
blockType: 'core/freeform'
}
} );

expect( Object.keys( state.byUid ) ).to.have.lengthOf( 2 );
expect( values( state.byUid )[ 1 ].uid ).to.equal( 'ribs' );
expect( state.order ).to.eql( [ 'chicken', 'ribs' ] );
expect( state.hovered ).to.be.null();
expect( state.selected ).to.equal( 'ribs' );
} );

it( 'should move the block up', () => {
Expand Down
4 changes: 2 additions & 2 deletions languages/gutenberg.pot
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ msgstr ""
msgid "Text"
msgstr ""

#: editor/components/inserter/index.js:30
#: editor/components/inserter/index.js:37
msgid "Insert block"
msgstr ""

#: editor/components/inserter/menu.js:40
#: editor/components/inserter/menu.js:77
msgid "Search…"
msgstr ""

Expand Down