From 38a1227e7c5817141dd0907b762eb4ff00dc770e Mon Sep 17 00:00:00 2001 From: andrei draganescu Date: Tue, 29 Oct 2019 08:46:12 +0200 Subject: [PATCH] Smart block appender (#16708) * if thre is only one there is only one * made a new insertion point selector, some code review refactoring * better handling of inserter * refactoring and named block insertion * updates to the appender * update snapshots * update docs * default inserter label is used in so many tests * fixed allowed blocks test * snapshot updated * better naming and removed the need for es-lint disabling * improved the inserter label construction * improved the doc of getTheOnlyAllowedItem selector * reverting test patches becasue patching without understanding is bad, bad, bad - don't do it * moved getInsertionIndex out of selectos and back into each component that used it * docs generated * added experimental labels to new selectors, added es-lint comment back * updated docs * Update packages/block-editor/src/store/selectors.js Co-Authored-By: Miguel Fonseca * Update packages/block-editor/src/store/selectors.js Co-Authored-By: Miguel Fonseca * refactored and fixed some coding errors * small code move * small code move * removes aria attrs for autoinserted items * fixes typo, adds translators comment * simplifies the intserter logic * fix for the simplification * simplifies by using one selector and passing props in compose * small code updates * lint * renamed insertedBlock * small doc update * adds tooltip to the default button appender * refactores for more self documenting varnames --- .../components/button-block-appender/index.js | 40 ++++-- .../test/__snapshots__/index.js.snap | 6 +- .../src/components/inserter/index.js | 118 +++++++++++++++--- packages/block-editor/src/store/selectors.js | 16 +++ 4 files changed, 146 insertions(+), 34 deletions(-) diff --git a/packages/block-editor/src/components/button-block-appender/index.js b/packages/block-editor/src/components/button-block-appender/index.js index 7fd751165f5b4..1cbbb5980903a 100644 --- a/packages/block-editor/src/components/button-block-appender/index.js +++ b/packages/block-editor/src/components/button-block-appender/index.js @@ -6,8 +6,8 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { Button, Icon } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { Button, Icon, Tooltip } from '@wordpress/components'; +import { _x, sprintf } from '@wordpress/i18n'; /** * Internal dependencies @@ -21,17 +21,31 @@ function ButtonBlockAppender( { rootClientId, className } ) { ( - - ) } + renderToggle={ ( { onToggle, disabled, isOpen, blockTitle, hasSingleBlockType } ) => { + let label; + if ( hasSingleBlockType ) { + // translators: %s: the name of the block when there is only one + label = sprintf( _x( 'Add %s', 'directly add the only allowed block' ), blockTitle ); + } else { + label = _x( 'Add block', 'Generic label for block inserter button' ); + } + const isToggleButton = ! hasSingleBlockType; + return ( + + + + ); + } } isAppender /> diff --git a/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap index 9a09dae0d4767..08a0f7df9efe7 100644 --- a/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap @@ -29,7 +29,7 @@ exports[`DefaultBlockAppender should append a default block when input focused 1 rows={1} value="Start writing or type / to choose a block" /> - @@ -53,7 +53,7 @@ exports[`DefaultBlockAppender should match snapshot 1`] = ` rows={1} value="Start writing or type / to choose a block" /> - @@ -77,7 +77,7 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = ` rows={1} value="" /> - diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 5abb1ae4ef923..b9fd9dead4b37 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -1,29 +1,46 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, _x, sprintf } from '@wordpress/i18n'; import { Dropdown, IconButton } from '@wordpress/components'; import { Component } from '@wordpress/element'; -import { withSelect } from '@wordpress/data'; +import { withDispatch, withSelect } from '@wordpress/data'; import { compose, ifCondition } from '@wordpress/compose'; +import { + createBlock, + getBlockType, +} from '@wordpress/blocks'; /** * Internal dependencies */ import InserterMenu from './menu'; -const defaultRenderToggle = ( { onToggle, disabled, isOpen } ) => ( - -); +const defaultRenderToggle = ( { onToggle, disabled, isOpen, blockTitle, hasSingleBlockType } ) => { + let label; + if ( hasSingleBlockType ) { + // translators: %s: the name of the block when there is only one + label = sprintf( _x( 'Add %s', 'directly add the only allowed block' ), blockTitle ); + } else { + label = _x( 'Add block', 'Generic label for block inserter button' ); + } + return ( + + ); +}; class Inserter extends Component { constructor() { @@ -56,10 +73,12 @@ class Inserter extends Component { renderToggle( { onToggle, isOpen } ) { const { disabled, + blockTitle, + hasSingleBlockType, renderToggle = defaultRenderToggle, } = this.props; - return renderToggle( { onToggle, isOpen, disabled } ); + return renderToggle( { onToggle, isOpen, disabled, blockTitle, hasSingleBlockType } ); } /** @@ -86,8 +105,10 @@ class Inserter extends Component { } render() { - const { position } = this.props; - + const { position, hasSingleBlockType, insertOnlyAllowedBlock } = this.props; + if ( hasSingleBlockType ) { + return this.renderToggle( { onToggle: insertOnlyAllowedBlock } ); + } return ( { - const { hasInserterItems } = select( 'core/block-editor' ); + const { + hasInserterItems, + __experimentalGetAllowedBlocks, + } = select( 'core/block-editor' ); + + const allowedBlocks = __experimentalGetAllowedBlocks( rootClientId ); + const hasSingleBlockType = allowedBlocks && ( get( allowedBlocks, [ 'length' ], 0 ) === 1 ); + let allowedBlockType = false; + if ( hasSingleBlockType ) { + allowedBlockType = getBlockType( allowedBlocks ); + } return { hasItems: hasInserterItems( rootClientId ), + hasSingleBlockType, + blockTitle: allowedBlockType ? allowedBlockType.title : '', + allowedBlockType, + }; + } ), + withDispatch( ( dispatch, ownProps, { select } ) => { + return { + insertOnlyAllowedBlock() { + const { rootClientId, clientId, isAppender, destinationRootClientId } = ownProps; + const { + hasSingleBlockType, + allowedBlockType, + } = ownProps; + + if ( ! hasSingleBlockType ) { + return; + } + + function getInsertionIndex() { + const { + getBlockIndex, + getBlockSelectionEnd, + getBlockOrder, + } = select( 'core/block-editor' ); + + // If the clientId is defined, we insert at the position of the block. + if ( clientId ) { + return getBlockIndex( clientId, destinationRootClientId ); + } + + // If there a selected block, we insert after the selected block. + const end = getBlockSelectionEnd(); + if ( ! isAppender && end ) { + return getBlockIndex( end, destinationRootClientId ) + 1; + } + + // Otherwise, we insert at the end of the current rootClientId + return getBlockOrder( destinationRootClientId ).length; + } + + const { + insertBlock, + } = dispatch( 'core/block-editor' ); + + const blockToInsert = createBlock( allowedBlockType.name ); + insertBlock( + blockToInsert, + getInsertionIndex(), + rootClientId + ); + }, }; } ), ifCondition( ( { hasItems } ) => hasItems ), diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index a0fd75a3a7b81..76bec295096bc 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1310,6 +1310,22 @@ export const hasInserterItems = createSelector( ], ); +/** + * Returns the list of allowed inserter blocks for inner blocks children + * + * @param {Object} state Editor state. + * @param {?string} rootClientId Optional root client ID of block list. + * + * @return {Array?} The list of allowed block types or false. + */ +export const __experimentalGetAllowedBlocks = ( state, rootClientId = null ) => { + if ( ! rootClientId ) { + return false; + } + const { allowedBlocks } = getBlockListSettings( state, rootClientId ); + return allowedBlocks; +}; + /** * Returns the Block List settings of a block, if any exist. *