Skip to content

Commit

Permalink
One hook to rule them all: preparation for a block supports API (#56862)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Dec 7, 2023
1 parent 0fa652a commit a9cbc06
Show file tree
Hide file tree
Showing 21 changed files with 229 additions and 578 deletions.
30 changes: 2 additions & 28 deletions packages/block-editor/src/components/block-controls/hook.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,19 @@
/**
* WordPress dependencies
*/
import { store as blocksStore } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import groups from './groups';
import { store as blockEditorStore } from '../../store';
import { useBlockEditContext } from '../block-edit/context';
import useDisplayBlockControls from '../use-display-block-controls';

export default function useBlockControlsFill( group, shareWithChildBlocks ) {
const isDisplayed = useDisplayBlockControls();
const { clientId } = useBlockEditContext();
const isParentDisplayed = useSelect(
( select ) => {
if ( ! shareWithChildBlocks ) {
return false;
}

const { getBlockName, hasSelectedInnerBlock } =
select( blockEditorStore );
const { hasBlockSupport } = select( blocksStore );

return (
hasBlockSupport(
getBlockName( clientId ),
'__experimentalExposeControlsToChildren',
false
) && hasSelectedInnerBlock( clientId )
);
},
[ shareWithChildBlocks, clientId ]
);

const { isDisplayed, isParentDisplayed } = useDisplayBlockControls();
if ( isDisplayed ) {
return groups[ group ]?.Fill;
}
if ( isParentDisplayed ) {
if ( isParentDisplayed && shareWithChildBlocks ) {
return groups.parent.Fill;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const { createPrivateSlotFill } = unlock( componentsPrivateApis );
const { Fill, Slot } = createPrivateSlotFill( 'BlockInformation' );

const BlockInfo = ( props ) => {
const isDisplayed = useDisplayBlockControls();
const { isDisplayed } = useDisplayBlockControls();
if ( ! isDisplayed ) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function InspectorControlsFill( {
group = __experimentalGroup;
}

const isDisplayed = useDisplayBlockControls();
const { isDisplayed } = useDisplayBlockControls();
const Fill = groups[ group ]?.Fill;
if ( ! Fill ) {
warning( `Unknown InspectorControls group "${ group }" provided.` );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function InspectorControlsFill( {
);
group = __experimentalGroup;
}
const isDisplayed = useDisplayBlockControls();
const { isDisplayed } = useDisplayBlockControls();

const Fill = groups[ group ]?.Fill;
if ( ! Fill ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { store as blocksStore } from '@wordpress/blocks';

/**
* Internal dependencies
Expand All @@ -13,23 +14,28 @@ export default function useDisplayBlockControls() {
const { isSelected, clientId, name } = useBlockEditContext();
return useSelect(
( select ) => {
if ( isSelected ) {
return true;
}

const {
getBlockName,
isFirstMultiSelectedBlock,
getMultiSelectedBlockClientIds,
hasSelectedInnerBlock,
} = select( blockEditorStore );
const { hasBlockSupport } = select( blocksStore );

if ( isFirstMultiSelectedBlock( clientId ) ) {
return getMultiSelectedBlockClientIds().every(
( id ) => getBlockName( id ) === name
);
}

return false;
return {
isDisplayed:
isSelected ||
( isFirstMultiSelectedBlock( clientId ) &&
getMultiSelectedBlockClientIds().every(
( id ) => getBlockName( id ) === name
) ),
isParentDisplayed:
hasBlockSupport(
getBlockName( clientId ),
'__experimentalExposeControlsToChildren',
false
) && hasSelectedInnerBlock( clientId ),
};
},
[ clientId, isSelected, name ]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ export default function useDisplayBlockControls() {
false
);

if ( ! hideControls && isSelected ) {
return true;
}

return false;
return { isDisplayed: ! hideControls && isSelected };
},
[ clientId, isSelected, name ]
);
Expand Down
54 changes: 9 additions & 45 deletions packages/block-editor/src/hooks/align.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { createHigherOrderComponent, pure } from '@wordpress/compose';
import { createHigherOrderComponent } from '@wordpress/compose';
import { addFilter } from '@wordpress/hooks';
import {
getBlockSupport,
Expand Down Expand Up @@ -109,7 +109,7 @@ export function addAttribute( settings ) {
}

function BlockEditAlignmentToolbarControlsPure( {
blockName,
name: blockName,
align,
setAttributes,
} ) {
Expand Down Expand Up @@ -152,45 +152,14 @@ function BlockEditAlignmentToolbarControlsPure( {
);
}

// We don't want block controls to re-render when typing inside a block. `pure`
// will prevent re-renders unless props change, so only pass the needed props
// and not the whole attributes object.
const BlockEditAlignmentToolbarControls = pure(
BlockEditAlignmentToolbarControlsPure
);

/**
* Override the default edit UI to include new toolbar controls for block
* alignment, if block defines support.
*
* @param {Function} BlockEdit Original component.
*
* @return {Function} Wrapped component.
*/
export const withAlignmentControls = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const hasAlignmentSupport = hasBlockSupport(
props.name,
'align',
false
);

return (
<>
{ hasAlignmentSupport && (
<BlockEditAlignmentToolbarControls
blockName={ props.name }
// This component is pure, so only pass needed props!
align={ props.attributes.align }
setAttributes={ props.setAttributes }
/>
) }
<BlockEdit key="edit" { ...props } />
</>
);
export default {
shareWithChildBlocks: true,
edit: BlockEditAlignmentToolbarControlsPure,
attributeKeys: [ 'align' ],
hasSupport( name ) {
return hasBlockSupport( name, 'align', false );
},
'withAlignmentControls'
);
};

function BlockListBlockWithDataAlign( { block: BlockListBlock, props } ) {
const { name, attributes } = props;
Expand Down Expand Up @@ -273,11 +242,6 @@ addFilter(
'core/editor/align/with-data-align',
withDataAlign
);
addFilter(
'editor.BlockEdit',
'core/editor/align/with-toolbar-controls',
withAlignmentControls
);
addFilter(
'blocks.getSaveContent.extraProps',
'core/editor/align/addAssignedAlign',
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/align.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { WIDE_ALIGNMENTS } from '@wordpress/components';
const ALIGNMENTS = [ 'left', 'center', 'right' ];

export * from './align.js';
export { default } from './align.js';

// Used to filter out blocks that don't support wide/full alignment on mobile
addFilter(
Expand Down
51 changes: 12 additions & 39 deletions packages/block-editor/src/hooks/anchor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { addFilter } from '@wordpress/hooks';
import { PanelBody, TextControl, ExternalLink } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { hasBlockSupport } from '@wordpress/blocks';
import { createHigherOrderComponent, pure } from '@wordpress/compose';
import { Platform } from '@wordpress/element';

/**
Expand Down Expand Up @@ -52,7 +51,11 @@ export function addAttribute( settings ) {
return settings;
}

function BlockEditAnchorControlPure( { blockName, anchor, setAttributes } ) {
function BlockEditAnchorControlPure( {
name: blockName,
anchor,
setAttributes,
} ) {
const blockEditingMode = useBlockEditingMode();

const isWeb = Platform.OS === 'web';
Expand Down Expand Up @@ -116,38 +119,13 @@ function BlockEditAnchorControlPure( { blockName, anchor, setAttributes } ) {
);
}

// We don't want block controls to re-render when typing inside a block. `pure`
// will prevent re-renders unless props change, so only pass the needed props
// and not the whole attributes object.
const BlockEditAnchorControl = pure( BlockEditAnchorControlPure );

/**
* Override the default edit UI to include a new block inspector control for
* assigning the anchor ID, if block supports anchor.
*
* @param {Component} BlockEdit Original component.
*
* @return {Component} Wrapped component.
*/
export const withAnchorControls = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
return (
<>
<BlockEdit { ...props } />
{ props.isSelected &&
hasBlockSupport( props.name, 'anchor' ) && (
<BlockEditAnchorControl
blockName={ props.name }
// This component is pure, so only pass needed
// props!
anchor={ props.attributes.anchor }
setAttributes={ props.setAttributes }
/>
) }
</>
);
};
}, 'withAnchorControls' );
export default {
edit: BlockEditAnchorControlPure,
attributeKeys: [ 'anchor' ],
hasSupport( name ) {
return hasBlockSupport( name, 'anchor' );
},
};

/**
* Override props assigned to save component to inject anchor ID, if block
Expand All @@ -169,11 +147,6 @@ export function addSaveProps( extraProps, blockType, attributes ) {
}

addFilter( 'blocks.registerBlockType', 'core/anchor/attribute', addAttribute );
addFilter(
'editor.BlockEdit',
'core/editor/anchor/with-inspector-controls',
withAnchorControls
);
addFilter(
'blocks.getSaveContent.extraProps',
'core/editor/anchor/save-props',
Expand Down
Loading

1 comment on commit a9cbc06

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in a9cbc06.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7132630952
📝 Reported issues:

Please sign in to comment.