diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/index.js b/packages/block-editor/src/components/inserter/block-patterns-tab/index.js index d2f302683ed2da..ebf816a9e2decb 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/index.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/index.js @@ -14,6 +14,7 @@ import MobileTabNavigation from '../mobile-tab-navigation'; import { PatternCategoryPreviews } from './pattern-category-previews'; import { usePatternCategories } from './use-pattern-categories'; import CategoryTabs from '../category-tabs'; +import InserterNoResults from '../no-results'; function BlockPatternsTab( { onSelectCategory, @@ -28,6 +29,10 @@ function BlockPatternsTab( { const isMobile = useViewportMatch( 'medium', '<' ); + if ( ! categories.length ) { + return ; + } + return ( <> { ! isMobile && ( diff --git a/packages/block-editor/src/components/inserter/block-types-tab.js b/packages/block-editor/src/components/inserter/block-types-tab.js index 60e1ff4beb6d54..fc742c3bf64897 100644 --- a/packages/block-editor/src/components/inserter/block-types-tab.js +++ b/packages/block-editor/src/components/inserter/block-types-tab.js @@ -13,6 +13,7 @@ import InserterPanel from './panel'; import useBlockTypesState from './hooks/use-block-types-state'; import InserterListbox from '../inserter-listbox'; import { orderBy } from '../../utils/sorting'; +import InserterNoResults from './no-results'; const getBlockNamespace = ( item ) => item.name.split( '/' )[ 0 ]; @@ -102,6 +103,10 @@ export function BlockTypesTab( { didRenderAllCategories ? collectionEntries : EMPTY_ARRAY ); + if ( ! items.length ) { + return ; + } + return (
diff --git a/packages/block-editor/src/components/inserter/media-tab/media-tab.js b/packages/block-editor/src/components/inserter/media-tab/media-tab.js index c5f462210fa4a4..2f3985aef569cb 100644 --- a/packages/block-editor/src/components/inserter/media-tab/media-tab.js +++ b/packages/block-editor/src/components/inserter/media-tab/media-tab.js @@ -16,6 +16,7 @@ import { useMediaCategories } from './hooks'; import { getBlockAndPreviewFromMedia } from './utils'; import MobileTabNavigation from '../mobile-tab-navigation'; import CategoryTabs from '../category-tabs'; +import InserterNoResults from '../no-results'; const ALLOWED_MEDIA_TYPES = [ 'image', 'video', 'audio' ]; @@ -48,6 +49,10 @@ function MediaTab( { [ mediaCategories ] ); + if ( ! categories.length ) { + return ; + } + return ( <> { ! isMobile && ( diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 33737c16bcb50f..672f625465117f 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -11,28 +11,25 @@ import { useState, useCallback, useMemo, - useImperativeHandle, useRef, + useLayoutEffect, } from '@wordpress/element'; import { VisuallyHidden, SearchControl, Popover } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; import { useDebouncedInput } from '@wordpress/compose'; /** * Internal dependencies */ -import { unlock } from '../../lock-unlock'; import Tips from './tips'; import InserterPreviewPanel from './preview-panel'; import BlockTypesTab from './block-types-tab'; import BlockPatternsTab from './block-patterns-tab'; import { PatternCategoryPreviewPanel } from './block-patterns-tab/pattern-category-preview-panel'; -import { MediaTab, MediaCategoryPanel, useMediaCategories } from './media-tab'; +import { MediaTab, MediaCategoryPanel } from './media-tab'; import InserterSearchResults from './search-results'; import useInsertionPoint from './hooks/use-insertion-point'; import InserterTabs from './tabs'; -import { store as blockEditorStore } from '../../store'; import { useZoomOut } from '../../hooks/use-zoom-out'; const NOOP = () => {}; @@ -59,7 +56,7 @@ function InserterMenu( const [ patternFilter, setPatternFilter ] = useState( 'all' ); const [ selectedMediaCategory, setSelectedMediaCategory ] = useState( null ); - const [ selectedTab, setSelectedTab ] = useState( null ); + const [ selectedTab, setSelectedTab ] = useState( 'blocks' ); const [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ] = useInsertionPoint( { @@ -69,18 +66,6 @@ function InserterMenu( insertionIndex: __experimentalInsertionIndex, shouldFocusBlock, } ); - const { showPatterns } = useSelect( - ( select ) => { - const { hasAllowedPatterns } = unlock( select( blockEditorStore ) ); - return { - showPatterns: hasAllowedPatterns( destinationRootClientId ), - }; - }, - [ destinationRootClientId ] - ); - - const mediaCategories = useMediaCategories( destinationRootClientId ); - const showMedia = mediaCategories.length > 0; const onInsert = useCallback( ( blocks, meta, shouldForceFocusBlock ) => { @@ -135,29 +120,86 @@ function InserterMenu( ! delayedFilterValue && selectedPatternCategory; - const showMediaPanel = - selectedTab === 'media' && - ! delayedFilterValue && - selectedMediaCategory; + const showMediaPanel = selectedTab === 'media' && selectedMediaCategory; - const blocksTab = useMemo( - () => ( + const inserterSearch = useMemo( () => { + if ( selectedTab === 'media' ) { + return null; + } + return ( <> -
- { + if ( hoveredItem ) { + setHoveredItem( null ); + } + setFilterValue( value ); + } } + value={ filterValue } + label={ __( 'Search for blocks and patterns' ) } + placeholder={ __( 'Search' ) } + /> + { !! delayedFilterValue && ( + -
- { showInserterHelpPanel && ( -
- - { __( 'A tip for using the block editor' ) } - - -
+ ) } + + ); + }, [ + selectedTab, + hoveredItem, + setHoveredItem, + setFilterValue, + filterValue, + delayedFilterValue, + onSelect, + onHover, + onHoverPattern, + shouldFocusBlock, + clientId, + rootClientId, + __experimentalInsertionIndex, + isAppender, + ] ); + + const blocksTab = useMemo( + () => ( + <> + { inserterSearch } + { ! delayedFilterValue && ( + <> +
+ +
+ { showInserterHelpPanel && ( +
+ + { __( 'A tip for using the block editor' ) } + + +
+ ) } + ) } ), @@ -167,28 +209,35 @@ function InserterMenu( onHover, showMostUsedBlocks, showInserterHelpPanel, + inserterSearch, + delayedFilterValue, ] ); const patternsTab = useMemo( () => ( - - { showPatternPanel && ( - + { inserterSearch } + { ! delayedFilterValue && ( + + onSelectCategory={ onClickPatternCategory } + selectedCategory={ selectedPatternCategory } + > + { showPatternPanel && ( + + ) } + ) } - + ), [ destinationRootClientId, @@ -198,6 +247,8 @@ function InserterMenu( patternFilter, selectedPatternCategory, showPatternPanel, + inserterSearch, + delayedFilterValue, ] ); @@ -236,15 +287,6 @@ function InserterMenu( [ blocksTab, mediaTab, patternsTab ] ); - const searchRef = useRef(); - useImperativeHandle( ref, () => ( { - focusSearch: () => { - searchRef.current.focus(); - }, - } ) ); - - const showAsTabs = ! delayedFilterValue && ( showPatterns || showMedia ); - // When the pattern panel is showing, we want to use zoom out mode useZoomOut( showPatternPanel ); @@ -256,62 +298,31 @@ function InserterMenu( setSelectedTab( value ); }; + // Focus first active tab, if any + const tabsRef = useRef(); + useLayoutEffect( () => { + if ( tabsRef.current ) { + window.requestAnimationFrame( () => { + tabsRef.current + .querySelector( '[role="tab"][aria-selected="true"]' ) + ?.focus(); + } ); + } + }, [] ); + return (
-
- { - if ( hoveredItem ) { - setHoveredItem( null ); - } - setFilterValue( value ); - } } - value={ filterValue } - label={ __( 'Search for blocks and patterns' ) } - placeholder={ __( 'Search' ) } - ref={ searchRef } +
+ - { !! delayedFilterValue && ( -
- -
- ) } - { showAsTabs && ( - - ) } - { ! delayedFilterValue && ! showAsTabs && ( -
- { blocksTab } -
- ) }
{ showInserterHelpPanel && hoveredItem && ( +
{ tabs.map( ( tab ) => ( @@ -69,4 +61,4 @@ function InserterTabs( { ); } -export default InserterTabs; +export default forwardRef( InserterTabs ); diff --git a/packages/edit-widgets/src/components/layout/style.scss b/packages/edit-widgets/src/components/layout/style.scss index 1aed3d3eefc86f..a10665f7cafe7d 100644 --- a/packages/edit-widgets/src/components/layout/style.scss +++ b/packages/edit-widgets/src/components/layout/style.scss @@ -18,6 +18,10 @@ // Leave space for the close button height: calc(100% - #{$button-size} - #{$grid-unit-10}); + .block-editor-inserter__tab { + display: none; + } + @include break-medium() { height: 100%; } diff --git a/packages/edit-widgets/src/components/secondary-sidebar/inserter-sidebar.js b/packages/edit-widgets/src/components/secondary-sidebar/inserter-sidebar.js index b01481748ee887..2374b35ad2d699 100644 --- a/packages/edit-widgets/src/components/secondary-sidebar/inserter-sidebar.js +++ b/packages/edit-widgets/src/components/secondary-sidebar/inserter-sidebar.js @@ -8,7 +8,7 @@ import { useViewportMatch, __experimentalUseDialog as useDialog, } from '@wordpress/compose'; -import { useCallback, useEffect, useRef } from '@wordpress/element'; +import { useCallback, useRef } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -31,13 +31,10 @@ export default function InserterSidebar() { const TagName = ! isMobileViewport ? VisuallyHidden : 'div'; const [ inserterDialogRef, inserterDialogProps ] = useDialog( { onClose: closeInserter, - focusOnMount: null, + focusOnMount: true, } ); const libraryRef = useRef(); - useEffect( () => { - libraryRef.current.focusSearch(); - }, [] ); return (
setIsInserterOpened( false ), - focusOnMount: null, + focusOnMount: true, } ); const libraryRef = useRef(); - useEffect( () => { - libraryRef.current.focusSearch(); - }, [] ); return (
{ } ); test( 'should insert a block pattern', async ( { page, editor } ) => { - await page.click( - 'role=region[name="Editor top bar"i] >> role=button[name="Toggle block inserter"i]' - ); + await page.getByLabel( 'Toggle block inserter' ).click(); + await page.getByRole( 'tab', { name: 'Patterns' } ).click(); await page.fill( 'role=region[name="Block Library"i] >> role=searchbox[name="Search for blocks and patterns"i]', 'Social links with a shared background color' diff --git a/test/e2e/specs/editor/various/inserting-blocks.spec.js b/test/e2e/specs/editor/various/inserting-blocks.spec.js index 6e19fd31a71b05..c277b056a323ce 100644 --- a/test/e2e/specs/editor/various/inserting-blocks.spec.js +++ b/test/e2e/specs/editor/various/inserting-blocks.spec.js @@ -598,6 +598,7 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => { .getByRole( 'searchbox', { name: 'Search for blocks and patterns', } ) + .first() .fill( 'Verse' ); await page.getByRole( 'button', { name: 'Browse All' } ).click(); @@ -607,9 +608,10 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => { .getByRole( 'searchbox', { name: 'Search for blocks and patterns', } ) + .first() ).toHaveValue( 'Verse' ); await expect( - page.getByRole( 'listbox', { name: 'Blocks' } ) + page.getByRole( 'listbox', { name: 'Blocks' } ).first() ).toHaveCount( 1 ); } ); diff --git a/test/performance/specs/post-editor.spec.js b/test/performance/specs/post-editor.spec.js index c8010c79b15508..b82d3a31d4cb69 100644 --- a/test/performance/specs/post-editor.spec.js +++ b/test/performance/specs/post-editor.spec.js @@ -418,9 +418,11 @@ test.describe( 'Post Editor Performance', () => { const globalInserterToggle = page.getByRole( 'button', { name: 'Toggle block inserter', } ); - // Open Inserter. await globalInserterToggle.click(); + + await page.getByRole( 'searchbox' ).click(); + await perfUtils.expectExpandedState( globalInserterToggle, 'true' ); const samples = 10;