Skip to content

Commit

Permalink
Refactor <BlockToolbar />: Add variant prop and <NavigableToolbar /> …
Browse files Browse the repository at this point in the history
…wrapper (#56335)

- Combine `<BlockContextualToolbar />` into `<BlockToolbar />`. This brings all the necessary functionality for `<BlockToolbar />` to be used, mainly by wrapping it in a `<NavigableToolbar />`.
- Remove `<BlockContextualToolbar />`. This is no longer needed, and was originally intended for Contextual to mean Popover.
- Replace usage of `<BlockContextualToobar />` in edit-site, edit-post, edit-widget, etc headers with `<BlockToolbar />`
- Refactor `<SelectedBlockTools />` to become `<BlockToolbarPopover />` to better reflect its purpose.
- Add `<PrivateBlockToolbar />` with new props of `__experimentalInitialIndex`, `__experimentalOnIndexChange`, and `focusOnMount`.
- Export `<BlockToolbar />` from  `<PrivateBlockToolbar />` with locked `__experimentalInitialIndex`, `__experimentalOnIndexChange`, and `focusOnMount` as `undefined`. Public API props are `hideDragHandles` and `variant` with a default of `unstyled`.
- Remove concept of `isFixed` from the `<BlockToolbar />`. The styles of the current small-screen fixed toolbar will become the default. The popover and top toolbars will have its own CSS overrides applied. 
- Split `<BlockToolbarPopover />` and `<BlockBreadcrumbPopover />` into separate components for simplicity.
- Top Toolbar now means. You have to implement the `<BlockToolbar />` on your own.
  • Loading branch information
jeryj authored Dec 7, 2023
1 parent f48ac05 commit b45f22c
Show file tree
Hide file tree
Showing 37 changed files with 725 additions and 759 deletions.
8 changes: 8 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,18 @@ _Returns_

### BlockToolbar

Renders the block toolbar.

_Related_

- <https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-toolbar/README.md>

_Parameters_

- _props_ `Object`: Components props.
- _props.hideDragHandle_ `boolean`: Show or hide the Drag Handle for drag and drop functionality.
- _props.variant_ `string`: Style variant of the toolbar, also passed to the Dropdowns rendered from Block Toolbar Buttons.

### BlockTools

Renders block tools (the block toolbar, select/navigation mode toolbar, the insertion point and a slot for the inline rich text toolbar). Must be wrapped around the block content and editor styles wrapper or iframe.
Expand Down

This file was deleted.

275 changes: 180 additions & 95 deletions packages/block-editor/src/components/block-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { useRef } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';
Expand All @@ -32,38 +33,82 @@ import BlockEditVisuallyButton from '../block-edit-visually-button';
import { useShowHoveredOrFocusedGestures } from './utils';
import { store as blockEditorStore } from '../../store';
import __unstableBlockNameContext from './block-name-context';
import NavigableToolbar from '../navigable-toolbar';
import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls';

const BlockToolbar = ( { hideDragHandle } ) => {
const { blockClientIds, blockType, isValid, isVisual, blockEditingMode } =
useSelect( ( select ) => {
const {
getBlockName,
getBlockMode,
getSelectedBlockClientIds,
isBlockValid,
getBlockRootClientId,
getBlockEditingMode,
} = select( blockEditorStore );
const selectedBlockClientIds = getSelectedBlockClientIds();
const selectedBlockClientId = selectedBlockClientIds[ 0 ];
const blockRootClientId = getBlockRootClientId(
selectedBlockClientId
);
return {
blockClientIds: selectedBlockClientIds,
blockType:
selectedBlockClientId &&
getBlockType( getBlockName( selectedBlockClientId ) ),
rootClientId: blockRootClientId,
isValid: selectedBlockClientIds.every( ( id ) =>
isBlockValid( id )
),
isVisual: selectedBlockClientIds.every(
( id ) => getBlockMode( id ) === 'visual'
),
blockEditingMode: getBlockEditingMode( selectedBlockClientId ),
};
}, [] );
/**
* Renders the block toolbar.
*
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-toolbar/README.md
*
* @param {Object} props Components props.
* @param {boolean} props.hideDragHandle Show or hide the Drag Handle for drag and drop functionality.
* @param {boolean} props.focusOnMount Focus the toolbar when mounted.
* @param {number} props.__experimentalInitialIndex The initial index of the toolbar item to focus.
* @param {Function} props.__experimentalOnIndexChange Callback function to be called when the index of the focused toolbar item changes.
* @param {string} props.variant Style variant of the toolbar, also passed to the Dropdowns rendered from Block Toolbar Buttons.
*/
export function PrivateBlockToolbar( {
hideDragHandle,
focusOnMount,
__experimentalInitialIndex,
__experimentalOnIndexChange,
variant = 'unstyled',
} ) {
const {
blockClientId,
blockClientIds,
isDefaultEditingMode,
blockType,
shouldShowVisualToolbar,
showParentSelector,
} = useSelect( ( select ) => {
const {
getBlockName,
getBlockMode,
getBlockParents,
getSelectedBlockClientIds,
isBlockValid,
getBlockRootClientId,
getBlockEditingMode,
} = select( blockEditorStore );
const selectedBlockClientIds = getSelectedBlockClientIds();
const selectedBlockClientId = selectedBlockClientIds[ 0 ];
const blockRootClientId = getBlockRootClientId( selectedBlockClientId );
const parents = getBlockParents( selectedBlockClientId );
const firstParentClientId = parents[ parents.length - 1 ];
const parentBlockName = getBlockName( firstParentClientId );
const parentBlockType = getBlockType( parentBlockName );
const _isDefaultEditingMode =
getBlockEditingMode( selectedBlockClientId ) === 'default';
const isValid = selectedBlockClientIds.every( ( id ) =>
isBlockValid( id )
);
const isVisual = selectedBlockClientIds.every(
( id ) => getBlockMode( id ) === 'visual'
);
return {
blockClientId: selectedBlockClientId,
blockClientIds: selectedBlockClientIds,
isDefaultEditingMode: _isDefaultEditingMode,
blockType:
selectedBlockClientId &&
getBlockType( getBlockName( selectedBlockClientId ) ),

shouldShowVisualToolbar: isValid && isVisual,
rootClientId: blockRootClientId,
showParentSelector:
parentBlockType &&
getBlockEditingMode( firstParentClientId ) === 'default' &&
hasBlockSupport(
parentBlockType,
'__experimentalParentSelector',
true
) &&
selectedBlockClientIds.length === 1 &&
_isDefaultEditingMode,
};
}, [] );

const toolbarWrapperRef = useRef( null );

Expand All @@ -76,86 +121,126 @@ const BlockToolbar = ( { hideDragHandle } ) => {

const isLargeViewport = ! useViewportMatch( 'medium', '<' );

if ( blockType ) {
if ( ! hasBlockSupport( blockType, '__experimentalToolbar', true ) ) {
return null;
}
}
const isToolbarEnabled =
blockType &&
hasBlockSupport( blockType, '__experimentalToolbar', true );
const hasAnyBlockControls = useHasAnyBlockControls();

if ( blockClientIds.length === 0 ) {
if (
! isToolbarEnabled ||
( ! isDefaultEditingMode && ! hasAnyBlockControls )
) {
return null;
}

const shouldShowVisualToolbar = isValid && isVisual;
const isMultiToolbar = blockClientIds.length > 1;
const isSynced =
isReusableBlock( blockType ) || isTemplatePart( blockType );

const classes = classnames( 'block-editor-block-toolbar', {
// Shifts the toolbar to make room for the parent block selector.
const classes = classnames( 'block-editor-block-contextual-toolbar', {
'has-parent': showParentSelector,
} );

const innerClasses = classnames( 'block-editor-block-toolbar', {
'is-synced': isSynced,
} );

return (
<div className={ classes } ref={ toolbarWrapperRef }>
{ ! isMultiToolbar &&
isLargeViewport &&
blockEditingMode === 'default' && <BlockParentSelector /> }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
blockEditingMode === 'default' && (
<div ref={ nodeRef } { ...showHoveredOrFocusedGestures }>
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
<BlockSwitcher clientIds={ blockClientIds } />
{ ! isMultiToolbar && (
<BlockLockToolbar
clientId={ blockClientIds[ 0 ] }
wrapperRef={ toolbarWrapperRef }
<NavigableToolbar
focusEditorOnEscape
className={ classes }
/* translators: accessibility text for the block toolbar */
aria-label={ __( 'Block tools' ) }
// The variant is applied as "toolbar" when undefined, which is the black border style of the dropdown from the toolbar popover.
variant={ variant === 'toolbar' ? undefined : variant }
focusOnMount={ focusOnMount }
__experimentalInitialIndex={ __experimentalInitialIndex }
__experimentalOnIndexChange={ __experimentalOnIndexChange }
// Resets the index whenever the active block changes so
// this is not persisted. See https://github.com/WordPress/gutenberg/pull/25760#issuecomment-717906169
key={ blockClientId }
>
<div ref={ toolbarWrapperRef } className={ innerClasses }>
{ ! isMultiToolbar &&
isLargeViewport &&
isDefaultEditingMode && <BlockParentSelector /> }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
isDefaultEditingMode && (
<div
ref={ nodeRef }
{ ...showHoveredOrFocusedGestures }
>
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
<BlockSwitcher clientIds={ blockClientIds } />
{ ! isMultiToolbar && (
<BlockLockToolbar
clientId={ blockClientIds[ 0 ] }
wrapperRef={ toolbarWrapperRef }
/>
) }
<BlockMover
clientIds={ blockClientIds }
hideDragHandle={ hideDragHandle }
/>
) }
<BlockMover
clientIds={ blockClientIds }
hideDragHandle={ hideDragHandle }
/>
</ToolbarGroup>
</div>
</ToolbarGroup>
</div>
) }
{ shouldShowVisualToolbar && isMultiToolbar && (
<BlockGroupToolbar />
) }
{ shouldShowVisualToolbar && (
<>
<BlockControls.Slot
group="parent"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="block"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot className="block-editor-block-toolbar__slot" />
<BlockControls.Slot
group="inline"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="other"
className="block-editor-block-toolbar__slot"
/>
<__unstableBlockNameContext.Provider
value={ blockType?.name }
>
<__unstableBlockToolbarLastItem.Slot />
</__unstableBlockNameContext.Provider>
</>
) }
<BlockEditVisuallyButton clientIds={ blockClientIds } />
{ isDefaultEditingMode && (
<BlockSettingsMenu clientIds={ blockClientIds } />
) }
{ shouldShowVisualToolbar && isMultiToolbar && (
<BlockGroupToolbar />
) }
{ shouldShowVisualToolbar && (
<>
<BlockControls.Slot
group="parent"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="block"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot className="block-editor-block-toolbar__slot" />
<BlockControls.Slot
group="inline"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="other"
className="block-editor-block-toolbar__slot"
/>
<__unstableBlockNameContext.Provider
value={ blockType?.name }
>
<__unstableBlockToolbarLastItem.Slot />
</__unstableBlockNameContext.Provider>
</>
) }
<BlockEditVisuallyButton clientIds={ blockClientIds } />
{ blockEditingMode === 'default' && (
<BlockSettingsMenu clientIds={ blockClientIds } />
) }
</div>
</div>
</NavigableToolbar>
);
};
}

/**
* Renders the block toolbar.
*
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-toolbar/README.md
*
* @param {Object} props Components props.
* @param {boolean} props.hideDragHandle Show or hide the Drag Handle for drag and drop functionality.
* @param {string} props.variant Style variant of the toolbar, also passed to the Dropdowns rendered from Block Toolbar Buttons.
*/
export default BlockToolbar;
export default function BlockToolbar( { hideDragHandle, variant } ) {
return (
<PrivateBlockToolbar
hideDragHandle={ hideDragHandle }
variant={ variant }
focusOnMount={ undefined }
__experimentalInitialIndex={ undefined }
__experimentalOnIndexChange={ undefined }
/>
);
}
Loading

1 comment on commit b45f22c

@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 b45f22c.
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/7134648752
📝 Reported issues:

Please sign in to comment.