diff --git a/packages/blocks/src/api/index.js b/packages/blocks/src/api/index.js index 8d1fae98aa56d7..127b329df4fe9b 100644 --- a/packages/blocks/src/api/index.js +++ b/packages/blocks/src/api/index.js @@ -39,6 +39,7 @@ export { isReusableBlock, getChildBlockNames, hasChildBlocks, + hasChildBlocksWithInserterSupport, unstable__bootstrapServerSideBlockDefinitions, // eslint-disable-line camelcase registerBlockStyle, } from './registration'; diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index af23347818a98b..267ab2443b90de 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -235,17 +235,11 @@ export function getBlockTypes() { * @param {string} feature Feature to retrieve * @param {*} defaultSupports Default value to return if not * explicitly defined - * @return {?*} Block support value + * + * @return {?*} Block support value */ export function getBlockSupport( nameOrType, feature, defaultSupports ) { - const blockType = 'string' === typeof nameOrType ? - getBlockType( nameOrType ) : - nameOrType; - - return get( blockType, [ - 'supports', - feature, - ], defaultSupports ); + return select( 'core/blocks' ).getBlockSupport( nameOrType, feature, defaultSupports ); } /** @@ -259,7 +253,7 @@ export function getBlockSupport( nameOrType, feature, defaultSupports ) { * @return {boolean} Whether block supports feature. */ export function hasBlockSupport( nameOrType, feature, defaultSupports ) { - return !! getBlockSupport( nameOrType, feature, defaultSupports ); + return select( 'core/blocks' ).hasBlockSupport( nameOrType, feature, defaultSupports ); } /** @@ -297,6 +291,18 @@ export const hasChildBlocks = ( blockName ) => { return select( 'core/blocks' ).hasChildBlocks( blockName ); }; +/** + * Returns a boolean indicating if a block has at least one child block with inserter support. + * + * @param {string} blockName Block type name. + * + * @return {boolean} True if a block contains at least one child blocks with inserter support + * and false otherwise. + */ +export const hasChildBlocksWithInserterSupport = ( blockName ) => { + return select( 'core/blocks' ).hasChildBlocksWithInserterSupport( blockName ); +}; + /** * Registers a new block style variation for the given block. * diff --git a/packages/blocks/src/store/selectors.js b/packages/blocks/src/store/selectors.js index bf6541ae3a1950..92950ffe43a11a 100644 --- a/packages/blocks/src/store/selectors.js +++ b/packages/blocks/src/store/selectors.js @@ -2,7 +2,7 @@ * External dependencies */ import createSelector from 'rememo'; -import { filter, includes, map } from 'lodash'; +import { filter, get, includes, map, some } from 'lodash'; /** * Returns all the available block types. @@ -85,6 +85,43 @@ export const getChildBlockNames = createSelector( ] ); +/** + * Returns the block support value for a feature, if defined. + * + * @param {Object} state Data state. + * @param {(string|Object)} nameOrType Block name or type object + * @param {string} feature Feature to retrieve + * @param {*} defaultSupports Default value to return if not + * explicitly defined + * + * @return {?*} Block support value + */ +export const getBlockSupport = ( state, nameOrType, feature, defaultSupports ) => { + const blockType = 'string' === typeof nameOrType ? + getBlockType( state, nameOrType ) : + nameOrType; + + return get( blockType, [ + 'supports', + feature, + ], defaultSupports ); +}; + +/** + * Returns true if the block defines support for a feature, or false otherwise. + * + * @param {Object} state Data state. + * @param {(string|Object)} nameOrType Block name or type object. + * @param {string} feature Feature to test. + * @param {boolean} defaultSupports Whether feature is supported by + * default if not explicitly defined. + * + * @return {boolean} Whether block supports feature. + */ +export function hasBlockSupport( state, nameOrType, feature, defaultSupports ) { + return !! getBlockSupport( state, nameOrType, feature, defaultSupports ); +} + /** * Returns a boolean indicating if a block has child blocks or not. * @@ -96,3 +133,18 @@ export const getChildBlockNames = createSelector( export const hasChildBlocks = ( state, blockName ) => { return getChildBlockNames( state, blockName ).length > 0; }; + +/** + * Returns a boolean indicating if a block has at least one child block with inserter support. + * + * @param {Object} state Data state. + * @param {string} blockName Block type name. + * + * @return {boolean} True if a block contains at least one child blocks with inserter support + * and false otherwise. + */ +export const hasChildBlocksWithInserterSupport = ( state, blockName ) => { + return some( getChildBlockNames( state, blockName ), ( childBlockName ) => { + return hasBlockSupport( state, childBlockName, 'inserter', true ); + } ); +}; diff --git a/packages/editor/src/components/block-switcher/index.js b/packages/editor/src/components/block-switcher/index.js index 734415632805d0..9c8efa4eb05d3f 100644 --- a/packages/editor/src/components/block-switcher/index.js +++ b/packages/editor/src/components/block-switcher/index.js @@ -8,7 +8,7 @@ import { castArray, filter, first, get, mapKeys, orderBy } from 'lodash'; */ import { __, _n, sprintf } from '@wordpress/i18n'; import { Dropdown, IconButton, Toolbar, PanelBody, AccessibleSVG } from '@wordpress/components'; -import { getBlockType, getPossibleBlockTransformations, switchToBlockType, hasChildBlocks } from '@wordpress/blocks'; +import { getBlockType, getPossibleBlockTransformations, switchToBlockType, hasChildBlocksWithInserterSupport } from '@wordpress/blocks'; import { Component, Fragment } from '@wordpress/element'; import { DOWN } from '@wordpress/keycodes'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -128,7 +128,7 @@ export class BlockSwitcher extends Component { id: destinationBlockType.name, icon: destinationBlockType.icon, title: destinationBlockType.title, - hasChildBlocks: hasChildBlocks( destinationBlockType.name ), + hasChildBlocksWithInserterSupport: hasChildBlocksWithInserterSupport( destinationBlockType.name ), } ) ) } onSelect={ ( item ) => { onTransform( blocks, item.id ); diff --git a/packages/editor/src/components/block-types-list/index.js b/packages/editor/src/components/block-types-list/index.js index 381587ae0f068e..d27d0d96ea61ac 100644 --- a/packages/editor/src/components/block-types-list/index.js +++ b/packages/editor/src/components/block-types-list/index.js @@ -42,7 +42,7 @@ class BlockTypesList extends Component { 'editor-block-types-list__item', getBlockMenuDefaultClassName( item.id ), { - 'editor-block-types-list__item-has-children': item.hasChildBlocks, + 'editor-block-types-list__item-has-children': item.hasChildBlocksWithInserterSupport, } ) } @@ -62,7 +62,7 @@ class BlockTypesList extends Component { style={ itemIconStyle } > - { item.hasChildBlocks && + { item.hasChildBlocksWithInserterSupport && { isDisabled: false, utility: 0, frecency: 0, - hasChildBlocks: false, + hasChildBlocksWithInserterSupport: false, } ); const reusableBlockItem = items.find( ( item ) => item.id === 'core/block/1' ); expect( reusableBlockItem ).toEqual( {