diff --git a/packages/block-editor/src/components/block-parent-selector/README.md b/packages/block-editor/src/components/block-parent-selector/README.md deleted file mode 100644 index cff6aa946918af..00000000000000 --- a/packages/block-editor/src/components/block-parent-selector/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Block Parent Selector - -Block parent selector component displays the hierarchy of the current block selection as a single icon to "go up" a level. The parent selector icon appears when hovering over the icon of the selected block. In addition, it appears only if the selected block in question has a parent. - -A click on the selector triggers the selection of the parent block. - -In practice the BlockParentSelector component renders a component that contains the parent selector icon. - -![Block parent selector test](https://make.wordpress.org/core/files/2020/09/block-parent-selector-test.gif) - - -## Table of contents - -1. [Development guidelines](#development-guidelines) -2. [Related components](#related-components) - -## Development guidelines - -### Usage - -Renders block parent selector icon in a component. - -```jsx -import { BlockParentSelector } from '@wordpress/block-editor'; - -const MyBlockParentSelector = () => ( - -); -``` - -### Props - -#### clientIds - -Blocks IDs - -- Type: `Array` - -## Related components - -Block Editor components are components that can be used to compose the UI of your block editor. Thus, they can only be used under a [BlockEditorProvider](https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/provider/README.md) in the components tree. diff --git a/packages/block-editor/src/components/block-parent-selector/index.js b/packages/block-editor/src/components/block-parent-selector/index.js deleted file mode 100644 index c6b7a458ea3887..00000000000000 --- a/packages/block-editor/src/components/block-parent-selector/index.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * WordPress dependencies - */ -import { getBlockType } from '@wordpress/blocks'; -import { ToolbarButton } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import BlockIcon from '../block-icon'; - -/** - * Block parent selector component, displaying the hierarchy of the - * current block selection as a single icon to "go up" a level. - * - * @return {WPComponent} Parent block selector. - */ -export default function BlockParentSelector() { - const { selectBlock } = useDispatch( 'core/block-editor' ); - const { parentBlockType, firstParentClientId } = useSelect( ( select ) => { - const { - getBlockName, - getBlockParents, - getSelectedBlockClientId, - } = select( 'core/block-editor' ); - const selectedBlockClientId = getSelectedBlockClientId(); - const parents = getBlockParents( selectedBlockClientId ); - const _firstParentClientId = parents[ parents.length - 1 ]; - const parentBlockName = getBlockName( _firstParentClientId ); - return { - parentBlockType: getBlockType( parentBlockName ), - firstParentClientId: _firstParentClientId, - }; - }, [] ); - - if ( firstParentClientId !== undefined ) { - return ( -
- selectBlock( firstParentClientId ) } - label={ sprintf( - /* translators: %s: Name of the block's parent. */ - __( 'Select parent (%s)' ), - parentBlockType.title - ) } - showTooltip - icon={ } - /> -
- ); - } - - return null; -} diff --git a/packages/block-editor/src/components/block-parent-selector/style.scss b/packages/block-editor/src/components/block-parent-selector/style.scss deleted file mode 100644 index c5a1869835188c..00000000000000 --- a/packages/block-editor/src/components/block-parent-selector/style.scss +++ /dev/null @@ -1,11 +0,0 @@ -.block-editor-block-parent-selector { - background: $white; - border-radius: $radius-block-ui; - - .block-editor-block-parent-selector__button { - width: $grid-unit-60; - height: $grid-unit-60; - border: $border-width solid $gray-900; - border-radius: $radius-block-ui; - } -} diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index 0a01e3945519f3..8e652da33064e9 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -6,23 +6,24 @@ import { castArray, flow, noop } from 'lodash'; /** * WordPress dependencies */ -import { __, _n } from '@wordpress/i18n'; +import { __, _n, sprintf } from '@wordpress/i18n'; import { DropdownMenu, MenuGroup, MenuItem, ClipboardButton, } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { moreVertical } from '@wordpress/icons'; import { Children, cloneElement, useCallback } from '@wordpress/element'; -import { serialize } from '@wordpress/blocks'; +import { getBlockType, serialize } from '@wordpress/blocks'; /** * Internal dependencies */ import BlockActions from '../block-actions'; +import BlockIcon from '../block-icon'; import BlockModeToggle from './block-mode-toggle'; import BlockHTMLConvertButton from './block-html-convert-button'; import __experimentalBlockSettingsMenuFirstItem from './block-settings-menu-first-item'; @@ -44,23 +45,41 @@ export function BlockSettingsDropdown( { const count = blockClientIds.length; const firstBlockClientId = blockClientIds[ 0 ]; - const shortcuts = useSelect( ( select ) => { - const { getShortcutRepresentation } = select( - 'core/keyboard-shortcuts' - ); - return { - duplicate: getShortcutRepresentation( - 'core/block-editor/duplicate' - ), - remove: getShortcutRepresentation( 'core/block-editor/remove' ), - insertAfter: getShortcutRepresentation( - 'core/block-editor/insert-after' - ), - insertBefore: getShortcutRepresentation( - 'core/block-editor/insert-before' - ), - }; - }, [] ); + const { selectBlock } = useDispatch( 'core/block-editor' ); + + const { parentBlockType, firstParentClientId, shortcuts } = useSelect( + ( select ) => { + const { getBlockName, getBlockParents } = select( + 'core/block-editor' + ); + const { getShortcutRepresentation } = select( + 'core/keyboard-shortcuts' + ); + const parents = getBlockParents( firstBlockClientId ); + const _firstParentClientId = parents[ parents.length - 1 ]; + const parentBlockName = getBlockName( _firstParentClientId ); + + return { + parentBlockType: getBlockType( parentBlockName ), + firstParentClientId: _firstParentClientId, + shortcuts: { + duplicate: getShortcutRepresentation( + 'core/block-editor/duplicate' + ), + remove: getShortcutRepresentation( + 'core/block-editor/remove' + ), + insertAfter: getShortcutRepresentation( + 'core/block-editor/insert-after' + ), + insertBefore: getShortcutRepresentation( + 'core/block-editor/insert-before' + ), + }, + }; + }, + [ firstBlockClientId ] + ); const updateSelection = useCallback( __experimentalSelectBlock @@ -105,6 +124,24 @@ export function BlockSettingsDropdown( { <__experimentalBlockSettingsMenuFirstItem.Slot fillProps={ { onClose } } /> + { firstParentClientId !== undefined && ( + + } + onClick={ () => + selectBlock( firstParentClientId ) + } + > + { sprintf( + /* translators: %s: Name of the block's parent. */ + __( 'Select parent (%s)' ), + parentBlockType.title + ) } + + ) } { count === 1 && ( @@ -71,20 +62,15 @@ export default function BlockToolbar( { const { toggleBlockHighlight } = useDispatch( 'core/block-editor' ); const nodeRef = useRef(); - const { showMovers, gestures: showMoversGestures } = useShowMoversGestures( - { - ref: nodeRef, - onChange( isFocused ) { - if ( isFocused && hasReducedUI ) { - return; - } - toggleBlockHighlight( blockClientId, isFocused ); - }, - } - ); - - const displayHeaderToolbar = - useViewportMatch( 'medium', '<' ) || hasFixedToolbar; + const showBlockHighlightGestures = useElementHoverFocusGestures( { + ref: nodeRef, + onChange( isFocused ) { + if ( isFocused && hasReducedUI ) { + return; + } + toggleBlockHighlight( blockClientId, isFocused ); + }, + } ); if ( blockType ) { if ( ! hasBlockSupport( blockType, '__experimentalToolbar', true ) ) { @@ -92,8 +78,6 @@ export default function BlockToolbar( { } } - const shouldShowMovers = displayHeaderToolbar || showMovers; - if ( blockClientIds.length === 0 ) { return null; } @@ -101,23 +85,13 @@ export default function BlockToolbar( { const shouldShowVisualToolbar = isValid && isVisual; const isMultiToolbar = blockClientIds.length > 1; - const classes = classnames( - 'block-editor-block-toolbar', - shouldShowMovers && 'is-showing-movers' - ); - const Wrapper = __experimentalExpandedControl ? ExpandedBlockControlsContainer : 'div'; return ( - -
- { ! isMultiToolbar && ( -
- -
- ) } + +
{ ( shouldShowVisualToolbar || isMultiToolbar ) && ( diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index bffe229b714e04..bed3ac0dfbd7e3 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -92,18 +92,9 @@ } .block-editor-block-toolbar__block-parent-selector-wrapper { - position: absolute; - top: -1px; - left: -1px; - opacity: 0; - transition: all 60ms linear; - z-index: -1; // This makes it slide out from underneath the toolbar. - - @include reduce-motion("transition"); - - .is-showing-movers & { - opacity: 1; - transform: translateY(-($block-toolbar-height + $grid-unit-15)); + // Hide the Parent button in Top Toolbar mode. + .edit-post-header-toolbar__block-toolbar & { + display: none; } } diff --git a/packages/block-editor/src/components/block-toolbar/utils.js b/packages/block-editor/src/components/block-toolbar/utils.js index 8f665be1153195..976702603c9da1 100644 --- a/packages/block-editor/src/components/block-toolbar/utils.js +++ b/packages/block-editor/src/components/block-toolbar/utils.js @@ -13,26 +13,28 @@ const { clearTimeout, setTimeout } = window; const DEBOUNCE_TIMEOUT = 200; /** - * Hook that creates a showMover state, as well as debounced show/hide callbacks. + * Hook that creates debounced activate/deactivate callbacks. * * @param {Object} props Component props. * @param {Object} props.ref Element reference. - * @param {boolean} props.isFocused Whether the component has current focus. - * @param {number} [props.debounceTimeout=250] Debounce timeout in milliseconds. + * @param {boolean} props.isFocused Whether the component has + * current focus. + * @param {number} [props.debounceTimeout=250] Debounce timeout in + * milliseconds. * @param {Function} [props.onChange=noop] Callback function. */ -export function useDebouncedShowMovers( { +export function useDebouncedActivateCallbacks( { ref, isFocused, debounceTimeout = DEBOUNCE_TIMEOUT, onChange = noop, } ) { - const [ showMovers, setShowMovers ] = useState( false ); + const [ active, setActive ] = useState( false ); const timeoutRef = useRef(); const handleOnChange = ( nextIsFocused ) => { if ( ref?.current ) { - setShowMovers( nextIsFocused ); + setActive( nextIsFocused ); } onChange( nextIsFocused ); @@ -56,19 +58,19 @@ export function useDebouncedShowMovers( { } }; - const debouncedShowMovers = ( event ) => { + const debouncedActivate = ( event ) => { if ( event ) { event.stopPropagation(); } clearTimeoutRef(); - if ( ! showMovers ) { + if ( ! active ) { handleOnChange( true ); } }; - const debouncedHideMovers = ( event ) => { + const debouncedDeactivate = ( event ) => { if ( event ) { event.stopPropagation(); } @@ -85,32 +87,34 @@ export function useDebouncedShowMovers( { useEffect( () => () => clearTimeoutRef(), [] ); return { - showMovers, - debouncedShowMovers, - debouncedHideMovers, + debouncedActivate, + debouncedDeactivate, }; } /** - * Hook that provides a showMovers state and gesture events for DOM elements - * that interact with the showMovers state. + * Hook that provides hover/focus gesture events for a given DOM element. * * @param {Object} props Component props. * @param {Object} props.ref Element reference. * @param {number} [props.debounceTimeout=250] Debounce timeout in milliseconds. * @param {Function} [props.onChange=noop] Callback function. */ -export function useShowMoversGestures( { +export function useElementHoverFocusGestures( { ref, debounceTimeout = DEBOUNCE_TIMEOUT, onChange = noop, } ) { const [ isFocused, setIsFocused ] = useState( false ); const { - showMovers, - debouncedShowMovers, - debouncedHideMovers, - } = useDebouncedShowMovers( { ref, debounceTimeout, isFocused, onChange } ); + debouncedActivate, + debouncedDeactivate, + } = useDebouncedActivateCallbacks( { + ref, + debounceTimeout, + isFocused, + onChange, + } ); const registerRef = useRef( false ); @@ -127,14 +131,14 @@ export function useShowMoversGestures( { const handleOnFocus = () => { if ( isFocusedWithin() ) { setIsFocused( true ); - debouncedShowMovers(); + debouncedActivate(); } }; const handleOnBlur = () => { if ( ! isFocusedWithin() ) { setIsFocused( false ); - debouncedHideMovers(); + debouncedDeactivate(); } }; @@ -158,15 +162,12 @@ export function useShowMoversGestures( { ref, registerRef, setIsFocused, - debouncedShowMovers, - debouncedHideMovers, + debouncedActivate, + debouncedDeactivate, ] ); return { - showMovers, - gestures: { - onMouseMove: debouncedShowMovers, - onMouseLeave: debouncedHideMovers, - }, + onMouseMove: debouncedActivate, + onMouseLeave: debouncedDeactivate, }; } diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 096eb3bacb7969..dfc7eb5012ef00 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -18,7 +18,6 @@ @import "./components/block-mobile-toolbar/style.scss"; @import "./components/block-mover/style.scss"; @import "./components/block-navigation/style.scss"; -@import "./components/block-parent-selector/style.scss"; @import "./components/block-patterns-list/style.scss"; @import "./components/block-preview/style.scss"; @import "./components/block-settings-menu/style.scss"; diff --git a/packages/components/src/menu-item/style.scss b/packages/components/src/menu-item/style.scss index e2c30395dc9042..cc4d6520e68771 100644 --- a/packages/components/src/menu-item/style.scss +++ b/packages/components/src/menu-item/style.scss @@ -12,12 +12,6 @@ .components-menu-item__shortcut + .components-menu-items__item-icon { margin-left: $grid-unit-10; } - - // If a block item is shown inline (such as transforms), space it correctly. - .block-editor-block-icon { - margin-left: -2px; // This optically balances the icon. - margin-right: $grid-unit-10; - } } .components-menu-item__info-wrapper { diff --git a/packages/e2e-tests/specs/editor/various/toolbar-roving-tabindex.test.js b/packages/e2e-tests/specs/editor/various/toolbar-roving-tabindex.test.js index 9e2cc3528d6d4f..0d858036986cbe 100644 --- a/packages/e2e-tests/specs/editor/various/toolbar-roving-tabindex.test.js +++ b/packages/e2e-tests/specs/editor/various/toolbar-roving-tabindex.test.js @@ -30,23 +30,6 @@ async function testBlockToolbarKeyboardNavigation( currentBlockLabel ) { await expectLabelToHaveFocus( 'Move up' ); } -async function wrapCurrentBlockWithGroup() { - await page.click( '[aria-label="Change block type or style"]' ); - await page.evaluate( () => { - document.querySelector( '.editor-block-list-item-group' ).click(); - } ); -} - -async function testGroupKeyboardNavigation( currentBlockLabel ) { - await expectLabelToHaveFocus( 'Block: Group' ); - await page.keyboard.press( 'Tab' ); - await expectLabelToHaveFocus( currentBlockLabel ); - await pressKeyWithModifier( 'shift', 'Tab' ); - await expectLabelToHaveFocus( 'Select parent (Group)' ); - await page.keyboard.press( 'ArrowRight' ); - await expectLabelToHaveFocus( 'Change block type or style' ); -} - describe( 'Toolbar roving tabindex', () => { beforeEach( async () => { await createNewPost(); @@ -58,31 +41,23 @@ describe( 'Toolbar roving tabindex', () => { await insertBlock( 'Paragraph' ); await page.keyboard.type( 'Paragraph' ); await testBlockToolbarKeyboardNavigation( 'Paragraph block' ); - await wrapCurrentBlockWithGroup(); - await testGroupKeyboardNavigation( 'Paragraph block' ); } ); it( 'ensures heading block toolbar uses roving tabindex', async () => { await insertBlock( 'Heading' ); await page.keyboard.type( 'Heading' ); await testBlockToolbarKeyboardNavigation( 'Block: Heading' ); - await wrapCurrentBlockWithGroup(); - await testGroupKeyboardNavigation( 'Block: Heading' ); } ); it( 'ensures list block toolbar uses roving tabindex', async () => { await insertBlock( 'List' ); await page.keyboard.type( 'List' ); await testBlockToolbarKeyboardNavigation( 'Block: List' ); - await wrapCurrentBlockWithGroup(); - await testGroupKeyboardNavigation( 'Block: List' ); } ); it( 'ensures image block toolbar uses roving tabindex', async () => { await insertBlock( 'Image' ); await testBlockToolbarKeyboardNavigation( 'Block: Image' ); - await wrapCurrentBlockWithGroup(); - await testGroupKeyboardNavigation( 'Block: Image' ); } ); it( 'ensures table block toolbar uses roving tabindex', async () => { @@ -93,14 +68,10 @@ describe( 'Toolbar roving tabindex', () => { await expectLabelToHaveFocus( 'Change block type or style' ); await page.click( '.blocks-table__placeholder-button' ); await testBlockToolbarKeyboardNavigation( 'Block: Table' ); - await wrapCurrentBlockWithGroup(); - await testGroupKeyboardNavigation( 'Block: Table' ); } ); it( 'ensures custom html block toolbar uses roving tabindex', async () => { await insertBlock( 'Custom HTML' ); await testBlockToolbarKeyboardNavigation( 'Block: Custom HTML' ); - await wrapCurrentBlockWithGroup(); - await testGroupKeyboardNavigation( 'Block: Custom HTML' ); } ); } );