Skip to content

Commit

Permalink
Revert "Cleanup: Remove the quick inserter code and state deps"
Browse files Browse the repository at this point in the history
This reverts commit 7265186.
  • Loading branch information
youknowriad committed Nov 27, 2017
1 parent f51ec08 commit 159c2c9
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 3 deletions.
88 changes: 88 additions & 0 deletions editor/edit-post/modes/visual-editor/inserter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { IconButton } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { createBlock, BlockIcon } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { Inserter } from '../../../components';
import { insertBlock } from '../../../actions';
import { getMostFrequentlyUsedBlocks, getBlockCount } from '../../../selectors';

export class VisualEditorInserter extends Component {
constructor() {
super( ...arguments );

this.showControls = this.toggleControls.bind( this, true );
this.hideControls = this.toggleControls.bind( this, false );

this.state = {
isShowingControls: false,
};
}

toggleControls( isShowingControls ) {
this.setState( { isShowingControls } );
}

insertBlock( name ) {
const { onInsertBlock } = this.props;
onInsertBlock( createBlock( name ) );
}

render() {
const { blockCount } = this.props;
const { isShowingControls } = this.state;
const { mostFrequentlyUsedBlocks } = this.props;
const classes = classnames( 'editor-visual-editor__inserter', {
'is-showing-controls': isShowingControls,
} );

return (
<div
className={ classes }
onFocus={ this.showControls }
onBlur={ this.hideControls }
>
<Inserter
insertIndex={ blockCount }
position="top right" />
{ mostFrequentlyUsedBlocks && mostFrequentlyUsedBlocks.map( ( block ) => (
<IconButton
key={ 'frequently_used_' + block.name }
className="editor-inserter__block"
onClick={ () => this.insertBlock( block.name ) }
label={ sprintf( __( 'Insert %s' ), block.title ) }
icon={ (
<span className="editor-visual-editor__inserter-block-icon">
<BlockIcon icon={ block.icon } />
</span>
) }
>
{ block.title }
</IconButton>
) ) }
</div>
);
}
}

export default connect(
( state ) => {
return {
mostFrequentlyUsedBlocks: getMostFrequentlyUsedBlocks( state ),
blockCount: getBlockCount( state ),
};
},
{ onInsertBlock: insertBlock },
)( VisualEditorInserter );
39 changes: 39 additions & 0 deletions editor/edit-post/modes/visual-editor/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,45 @@
}
}

.editor-visual-editor__inserter {
display: flex;
align-items: baseline;
max-width: $visual-editor-max-width + ( 2 * $block-mover-padding-visible );
margin: 0 auto;
clear: both;

padding: $block-padding;
padding-left: $block-padding - 8px; // Offset by left button's own padding
@include break-small {
padding: $block-padding ( $block-padding + $block-mover-padding-visible );
padding-left: $block-padding + $block-mover-padding-visible - 8px;
}

> .editor-inserter__block {
flex-direction: row;
opacity: 0;
transition: opacity 150ms;
margin: 0 10px;
width: auto;
font-family: $default-font;
font-size: $default-font-size;
box-shadow: none;
padding: 6px;
align-items: center;
}

&:hover > .editor-inserter__block,
&.is-showing-controls > .editor-inserter__block {
opacity: 1;
}
}

.editor-visual-editor__inserter-block-icon {
display: inline-block;
margin-right: 8px;
height: 20px;
}

.editor-visual-editor .editor-post-title {
margin-left: auto;
margin-right: auto;
Expand Down
54 changes: 54 additions & 0 deletions editor/edit-post/modes/visual-editor/test/inserter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* WordPress dependencies
*/
import { getBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { VisualEditorInserter } from '../inserter';

describe( 'VisualEditorInserter', () => {
it( 'should show controls when receiving focus', () => {
const wrapper = shallow( <VisualEditorInserter /> );

wrapper.simulate( 'focus' );

expect( wrapper.state( 'isShowingControls' ) ).toBe( true );
} );

it( 'should hide controls when losing focus', () => {
const wrapper = shallow( <VisualEditorInserter /> );

wrapper.simulate( 'focus' );
wrapper.simulate( 'blur' );

expect( wrapper.state( 'isShowingControls' ) ).toBe( false );
} );

it( 'should insert frequently used blocks', () => {
const onInsertBlock = jest.fn();
const mostFrequentlyUsedBlocks = [ getBlockType( 'core/paragraph' ), getBlockType( 'core/image' ) ];
const wrapper = shallow(
<VisualEditorInserter onInsertBlock={ onInsertBlock } mostFrequentlyUsedBlocks={ mostFrequentlyUsedBlocks } />
);
wrapper.state.preferences = {
blockUsage: {
'core/paragraph': 42,
'core/image': 34,
},
};

wrapper
.findWhere( ( node ) => node.prop( 'children' ) === 'Paragraph' )
.simulate( 'click' );

expect( onInsertBlock ).toHaveBeenCalled();
expect( onInsertBlock.mock.calls[ 0 ][ 0 ].name ).toBe( 'core/paragraph' );
} );
} );
11 changes: 10 additions & 1 deletion editor/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
get,
reduce,
keyBy,
keys,
first,
last,
omit,
pick,
without,
mapValues,
findIndex,
Expand Down Expand Up @@ -490,18 +492,24 @@ export function preferences( state = PREFERENCES_DEFAULTS, action ) {
mode: action.mode,
};
case 'INSERT_BLOCKS':
// put the block in the recently used blocks
// record the block usage and put the block in the recently used blocks
let blockUsage = state.blockUsage;
let recentlyUsedBlocks = [ ...state.recentlyUsedBlocks ];
action.blocks.forEach( ( block ) => {
const uses = ( blockUsage[ block.name ] || 0 ) + 1;
blockUsage = omit( blockUsage, block.name );
blockUsage[ block.name ] = uses;
recentlyUsedBlocks = [ block.name, ...without( recentlyUsedBlocks, block.name ) ].slice( 0, MAX_RECENT_BLOCKS );
} );
return {
...state,
blockUsage,
recentlyUsedBlocks,
};
case 'SETUP_EDITOR':
const isBlockDefined = name => getBlockType( name ) !== undefined;
const filterInvalidBlocksFromList = list => list.filter( isBlockDefined );
const filterInvalidBlocksFromObject = obj => pick( obj, keys( obj ).filter( isBlockDefined ) );
const commonBlocks = getBlockTypes()
.filter( ( blockType ) => 'common' === blockType.category )
.map( ( blockType ) => blockType.name );
Expand All @@ -512,6 +520,7 @@ export function preferences( state = PREFERENCES_DEFAULTS, action ) {
recentlyUsedBlocks: filterInvalidBlocksFromList( [ ...state.recentlyUsedBlocks ] )
.concat( difference( commonBlocks, state.recentlyUsedBlocks ) )
.slice( 0, MAX_RECENT_BLOCKS ),
blockUsage: filterInvalidBlocksFromObject( state.blockUsage ),
};
case 'TOGGLE_FEATURE':
return {
Expand Down
28 changes: 28 additions & 0 deletions editor/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
has,
last,
reduce,
keys,
without,
compact,
} from 'lodash';
import createSelector from 'rememo';
Expand All @@ -19,6 +21,11 @@ import { serialize, getBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';

/***
* Module constants
*/
const MAX_FREQUENT_BLOCKS = 3;

/**
* Returns the current editing mode.
*
Expand Down Expand Up @@ -1002,6 +1009,27 @@ export function getRecentlyUsedBlocks( state ) {
return compact( state.preferences.recentlyUsedBlocks.map( blockType => getBlockType( blockType ) ) );
}

/**
* Resolves the block usage stats into a list of the most frequently used blocks.
* Memoized so we're not generating block lists every time we render the list
* in the inserter.
*
* @param {Object} state Global application state
* @return {Array} List of block type settings
*/
export const getMostFrequentlyUsedBlocks = createSelector(
( state ) => {
const { blockUsage } = state.preferences;
const orderedByUsage = keys( blockUsage ).sort( ( a, b ) => blockUsage[ b ] - blockUsage[ a ] );
// add in paragraph and image blocks if they're not already in the usage data
return compact(
[ ...orderedByUsage, ...without( [ 'core/paragraph', 'core/image' ], ...orderedByUsage ) ]
.map( blockType => getBlockType( blockType ) )
).slice( 0, MAX_FREQUENT_BLOCKS );
},
( state ) => state.preferences.blockUsage
);

/**
* Returns whether the given feature is enabled or not
*
Expand Down
1 change: 1 addition & 0 deletions editor/store-defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const PREFERENCES_DEFAULTS = {
isSidebarOpened: ! viewPort.isExtraSmall(),
panels: { 'post-status': true },
recentlyUsedBlocks: [],
blockUsage: {},
features: {
fixedToolbar: false,
},
Expand Down
30 changes: 28 additions & 2 deletions editor/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,7 @@ describe( 'state', () => {
const state = preferences( undefined, {} );

expect( state ).toEqual( {
blockUsage: {},
recentlyUsedBlocks: [],
mode: 'visual',
isSidebarOpened: true,
Expand Down Expand Up @@ -934,7 +935,7 @@ describe( 'state', () => {
} );

it( 'should record recently used blocks', () => {
const state = preferences( deepFreeze( { recentlyUsedBlocks: [] } ), {
const state = preferences( deepFreeze( { recentlyUsedBlocks: [], blockUsage: {} } ), {
type: 'INSERT_BLOCKS',
blocks: [ {
uid: 'bacon',
Expand All @@ -944,7 +945,7 @@ describe( 'state', () => {

expect( state.recentlyUsedBlocks[ 0 ] ).toEqual( 'core-embed/twitter' );

const twoRecentBlocks = preferences( deepFreeze( { recentlyUsedBlocks: [] } ), {
const twoRecentBlocks = preferences( deepFreeze( { recentlyUsedBlocks: [], blockUsage: {} } ), {
type: 'INSERT_BLOCKS',
blocks: [ {
uid: 'eggs',
Expand All @@ -959,6 +960,24 @@ describe( 'state', () => {
expect( twoRecentBlocks.recentlyUsedBlocks[ 1 ] ).toEqual( 'core-embed/twitter' );
} );

it( 'should record block usage', () => {
const state = preferences( deepFreeze( { recentlyUsedBlocks: [], blockUsage: {} } ), {
type: 'INSERT_BLOCKS',
blocks: [ {
uid: 'eggs',
name: 'core-embed/twitter',
}, {
uid: 'bacon',
name: 'core-embed/youtube',
}, {
uid: 'milk',
name: 'core-embed/youtube',
} ],
} );

expect( state.blockUsage ).toEqual( { 'core-embed/youtube': 2, 'core-embed/twitter': 1 } );
} );

it( 'should populate recentlyUsedBlocks, filling up with common blocks, on editor setup', () => {
const state = preferences( deepFreeze( { recentlyUsedBlocks: [ 'core-embed/twitter', 'core-embed/youtube' ] } ), {
type: 'SETUP_EDITOR',
Expand All @@ -980,6 +999,13 @@ describe( 'state', () => {
expect( state.recentlyUsedBlocks[ 0 ] ).toEqual( 'core-embed/youtube' );
} );

it( 'should remove unregistered blocks from persisted block usage stats', () => {
const state = preferences( deepFreeze( { recentlyUsedBlocks: [], blockUsage: { 'core/i-do-not-exist': 42, 'core-embed/youtube': 88 } } ), {
type: 'SETUP_EDITOR',
} );
expect( state.blockUsage ).toEqual( { 'core-embed/youtube': 88 } );
} );

it( 'should toggle a feature flag', () => {
const state = preferences( deepFreeze( { features: { chicken: true } } ), {
type: 'TOGGLE_FEATURE',
Expand Down
Loading

0 comments on commit 159c2c9

Please sign in to comment.