diff --git a/components/dropdown-menu/index.js b/components/dropdown-menu/index.js
index 6faf2d5675bf05..0fc9cfc756dc39 100644
--- a/components/dropdown-menu/index.js
+++ b/components/dropdown-menu/index.js
@@ -2,12 +2,10 @@
* External dependencies
*/
import classnames from 'classnames';
-import clickOutside from 'react-click-outside';
/**
* WordPress dependencies
*/
-import { Component, findDOMNode } from '@wordpress/element';
import { keycodes } from '@wordpress/utils';
/**
@@ -16,226 +14,64 @@ import { keycodes } from '@wordpress/utils';
import './style.scss';
import IconButton from '../icon-button';
import Dashicon from '../dashicon';
-
-const { TAB, ESCAPE, LEFT, UP, RIGHT, DOWN } = keycodes;
-
-export class DropdownMenu extends Component {
- constructor() {
- super( ...arguments );
-
- this.bindReferenceNode = this.bindReferenceNode.bind( this );
- this.closeMenu = this.closeMenu.bind( this );
- this.toggleMenu = this.toggleMenu.bind( this );
- this.focusIndex = this.focusIndex.bind( this );
- this.focusPrevious = this.focusPrevious.bind( this );
- this.focusNext = this.focusNext.bind( this );
- this.handleKeyDown = this.handleKeyDown.bind( this );
- this.calculateMenuPosition = this.calculateMenuPosition.bind( this );
-
- this.nodes = {};
- this.timer = null;
-
- this.state = {
- activeIndex: null,
- open: false,
- menuLeft: 0,
- };
- }
-
- bindReferenceNode( name ) {
- return ( node ) => {
- this.nodes[ name ] = node;
- };
- }
-
- handleClickOutside() {
- if ( ! this.state.open ) {
- return;
- }
-
- this.closeMenu();
- }
-
- closeMenu() {
- this.setState( {
- open: false,
- } );
- }
-
- calculateMenuPosition() {
- const { toggle } = this.nodes;
- if ( ! toggle ) {
- return;
- }
- const node = findDOMNode( toggle );
- let n = node;
- let scrollLeft = 0;
- while ( n !== null && n !== node.offsetParent ) {
- scrollLeft += n.scrollLeft;
- n = n.parentNode;
- }
- const menuLeft = node.offsetLeft - scrollLeft - 4;
- if ( this.state.menuLeft !== menuLeft ) {
- this.setState( { menuLeft } );
- }
- }
-
- toggleMenu() {
- const open = ! this.state.open;
- if ( open ) {
- this.calculateMenuPosition();
- }
- this.setState( { open } );
- }
-
- focusIndex( activeIndex ) {
- this.setState( { activeIndex } );
+import Dropdown from '../dropdown';
+import NavigableMenu from '../navigable-menu';
+
+const { DOWN } = keycodes;
+
+function DropdownMenu( {
+ icon = 'menu',
+ label,
+ menuLabel,
+ controls,
+} ) {
+ if ( ! controls || ! controls.length ) {
+ return null;
}
- focusPrevious() {
- const { activeIndex } = this.state;
- const { controls } = this.props;
- if ( ! controls ) {
- return;
- }
-
- const maxIndex = controls.length - 1;
- const prevIndex = activeIndex <= 0 ? maxIndex : activeIndex - 1;
- this.focusIndex( prevIndex );
- }
-
- focusNext() {
- const { activeIndex } = this.state;
- const { controls } = this.props;
- if ( ! controls ) {
- return;
- }
-
- const nextIndex = ( activeIndex + 1 ) % controls.length;
- this.focusIndex( nextIndex );
- }
-
- handleKeyDown( keydown ) {
- if ( this.state.open ) {
- switch ( keydown.keyCode ) {
- case TAB:
- keydown.stopPropagation();
- this.closeMenu();
- break;
-
- case LEFT:
- case UP:
- keydown.preventDefault();
- keydown.stopPropagation();
- this.focusPrevious();
- break;
-
- case RIGHT:
- case DOWN:
- keydown.preventDefault();
- keydown.stopPropagation();
- this.focusNext();
- break;
- case ESCAPE:
- keydown.preventDefault();
- keydown.stopPropagation();
- // eslint-disable-next-line react/no-find-dom-node
- findDOMNode( this.nodes.toggle ).focus();
- this.closeMenu();
- break;
- default:
- break;
- }
- } else {
- switch ( keydown.keyCode ) {
- case DOWN:
- keydown.preventDefault();
- keydown.stopPropagation();
- this.toggleMenu();
- break;
-
- default:
- break;
- }
- }
- }
-
- componentDidUpdate( prevProps, prevState ) {
- const { open, activeIndex } = this.state;
-
- // Focus the first item when the menu opens.
- if ( ! prevState.open && open ) {
- this.focusIndex( 0 );
- }
-
- // Change focus to active index
- const { menu } = this.nodes;
- if ( prevState.activeIndex !== activeIndex &&
- Number.isInteger( activeIndex ) &&
- menu && menu.children[ activeIndex ] ) {
- menu.children[ activeIndex ].focus();
- }
- }
-
- render() {
- const {
- icon = 'menu',
- label,
- menuLabel,
- controls,
- } = this.props;
- const {
- open,
- menuLeft,
- } = this.state;
-
- if ( ! controls || ! controls.length ) {
- return null;
- }
- // monitor the menu position when open
- if ( open ) {
- if ( this.timer === null ) {
- this.timer = setInterval( this.calculateMenuPosition, 100 );
- }
- } else if ( this.timer !== null ) {
- clearInterval( this.timer );
- this.timer = null;
- }
- /* eslint-disable jsx-a11y/no-static-element-interactions */
- return (
-
-
{
+ const openOnArrowDown = ( event ) => {
+ if ( ! isOpen && event.keyCode === DOWN ) {
+ event.preventDefault();
+ event.stopPropagation();
+ onToggle();
}
- icon={ icon }
- onClick={ this.toggleMenu }
- aria-haspopup="true"
- aria-expanded={ open }
- label={ label }
- ref={ this.bindReferenceNode( 'toggle' ) }
- >
-
-
- { open &&
-
+
+
+ );
+ } }
+ renderContent={ ( { onClose } ) => {
+ return (
+
{ controls.map( ( control, index ) => (
{
event.stopPropagation();
- this.closeMenu();
+ onClose();
if ( control.onClick ) {
control.onClick();
}
@@ -243,17 +79,15 @@ export class DropdownMenu extends Component {
className="components-dropdown-menu__menu-item"
icon={ control.icon }
role="menuitem"
- tabIndex="-1"
>
{ control.title }
) ) }
-
- }
-
- );
- /* eslint-enable jsx-a11y/no-static-element-interactions */
- }
+
+ );
+ } }
+ />
+ );
}
-export default clickOutside( DropdownMenu );
+export default DropdownMenu;
diff --git a/components/dropdown-menu/style.scss b/components/dropdown-menu/style.scss
index 1376eb8c48e596..830703b5e6857d 100644
--- a/components/dropdown-menu/style.scss
+++ b/components/dropdown-menu/style.scss
@@ -40,14 +40,13 @@
}
}
}
+.components-dropdown-menu__popover .components-popover__content {
+ width: 200px;
+}
.components-dropdown-menu__menu {
- position: absolute;
- top: $block-controls-height - 1px;
// note that left is set by react in a style attribute
- box-shadow: $shadow-popover;
- border: 1px solid $light-gray-500;
- background: $white;
+ width: 100%;
padding: 3px 3px 0 3px;
font-family: $default-font;
font-size: $default-font-size;
diff --git a/components/dropdown-menu/test/index.js b/components/dropdown-menu/test/index.js
index 189ac3521bc48c..e8cb9fbf5d1c99 100644
--- a/components/dropdown-menu/test/index.js
+++ b/components/dropdown-menu/test/index.js
@@ -11,9 +11,9 @@ import { keycodes } from '@wordpress/utils';
/**
* Internal dependencies
*/
-import { DropdownMenu } from '../';
+import DropdownMenu from '../';
-const { TAB, ESCAPE, LEFT, UP, RIGHT, DOWN } = keycodes;
+const { DOWN } = keycodes;
describe( 'DropdownMenu', () => {
let controls;
@@ -55,132 +55,19 @@ describe( 'DropdownMenu', () => {
expect( wrapper.type() ).toBeNull();
} );
- it( 'should render a collapsed menu button', () => {
- const wrapper = shallow(
-
- );
-
- expect( wrapper.state( 'open' ) ).toBe( false );
- expect( wrapper.state( 'activeIndex' ) ).toBeNull();
- expect( wrapper.find( '> IconButton' ).prop( 'label' ) ).toBe( 'Select a direction' );
- expect( wrapper.find( '> IconButton' ).prop( 'icon' ) ).toBe( 'menu' );
- expect( wrapper.find( '.components-dropdown-menu__menu' ) ).toHaveLength( 0 );
- } );
-
- it( 'should render an expanded menu upon click', () => {
- const wrapper = shallow( );
-
- // Open menu
- wrapper.find( '> IconButton' ).simulate( 'click' );
-
- const options = wrapper.find( '.components-dropdown-menu__menu > IconButton' );
- expect( wrapper.state( 'open' ) ).toBe( true );
- expect( wrapper.state( 'activeIndex' ) ).toBe( 0 );
- expect( options ).toHaveLength( controls.length );
- expect( options.at( 0 ).prop( 'icon' ) ).toBe( 'arrow-up-alt' );
- expect( options.at( 0 ).children().text() ).toBe( 'Up' );
- } );
-
it( 'should open menu on arrow down', () => {
- const wrapper = shallow( );
+ const wrapper = mount( );
// Close menu by keyup
- wrapper.simulate( 'keydown', {
+ wrapper.find( '.components-dropdown-menu__toggle' ).simulate( 'keydown', {
stopPropagation: () => {},
preventDefault: () => {},
keyCode: DOWN,
} );
- expect( wrapper.state( 'open' ) ).toBe( true );
- } );
-
- it( 'should call the control onClick callback and close menu', () => {
- const wrapper = shallow( );
-
- // Open menu
- wrapper.find( '> IconButton' ).simulate( 'click' );
-
- // Select option
- const options = wrapper.find( '.components-dropdown-menu__menu > IconButton' );
- options.at( 0 ).simulate( 'click', { stopPropagation: () => {} } );
-
- expect( controls[ 0 ].onClick ).toHaveBeenCalled();
- expect( wrapper.state( 'open' ) ).toBe( false );
- } );
-
- it( 'should navigate by keypresses', () => {
- const wrapper = shallow( );
-
- // Open menu
- wrapper.find( '> IconButton' ).simulate( 'click' );
-
- // Navigate options
- function assertKeyDown( keyCode, expectedActiveIndex ) {
- wrapper.simulate( 'keydown', {
- stopPropagation: () => {},
- preventDefault: () => {},
- keyCode,
- } );
-
- const activeIndex = wrapper.state( 'activeIndex' );
- expect( activeIndex ).toBe( expectedActiveIndex );
- }
-
- assertKeyDown( RIGHT, 1 );
- assertKeyDown( DOWN, 2 );
- assertKeyDown( DOWN, 3 );
- assertKeyDown( DOWN, 0 ); // Reset to beginning
- assertKeyDown( DOWN, 1 );
- assertKeyDown( LEFT, 0 );
- assertKeyDown( UP, 3 ); // Reset to end
- } );
-
- it( 'should close menu on escape', () => {
- // Mount: We need to access DOM node of rendered menu IconButton
- const wrapper = mount( );
-
- // Open menu
- wrapper.find( '> IconButton' ).simulate( 'click' );
-
- // Close menu by escape
- wrapper.simulate( 'keydown', {
- stopPropagation: () => {},
- preventDefault: () => {},
- keyCode: ESCAPE,
- } );
-
- expect( wrapper.state( 'open' ) ).toBe( false );
- } );
-
- it( 'should close menu on click outside', () => {
- const wrapper = shallow( );
-
- // Open menu
- wrapper.find( '> IconButton' ).simulate( 'click' );
-
- // Close menu by click outside
- wrapper.instance().handleClickOutside();
-
- expect( wrapper.state( 'open' ) ).toBe( false );
- } );
-
- it( 'should close menu on tab', () => {
- const wrapper = shallow( );
-
- // Open menu
- wrapper.find( '> IconButton' ).simulate( 'click' );
-
- // Close menu by tab
- wrapper.simulate( 'keydown', {
- stopPropagation: () => {},
- preventDefault: () => {},
- keyCode: TAB,
- } );
+ const popover = wrapper.find( 'Popover' );
- expect( wrapper.state( 'open' ) ).toBe( false );
+ expect( popover.prop( 'isOpen' ) ).toBe( true );
} );
} );
} );
diff --git a/components/dropdown/README.md b/components/dropdown/README.md
index df196372b96346..1985439b564c18 100644
--- a/components/dropdown/README.md
+++ b/components/dropdown/README.md
@@ -1,5 +1,5 @@
-Popover
-=======
+Dropdown
+========
Dropdown is a React component to render a button that opens a floating content modal when clicked.
This components takes care of updating the state of the dropdown menu (opened/closed), handles closing the menu when clicking outside
diff --git a/components/index.js b/components/index.js
index 1eb3dd2516e595..7b5facf5b442f0 100644
--- a/components/index.js
+++ b/components/index.js
@@ -14,6 +14,7 @@ export { default as FormToggle } from './form-toggle';
export { default as FormTokenField } from './form-token-field';
export { default as IconButton } from './icon-button';
export { default as KeyboardShortcuts } from './keyboard-shortcuts';
+export { default as NavigableMenu } from './navigable-menu';
export { default as Notice } from './notice';
export { default as NoticeList } from './notice/list';
export { default as Panel } from './panel';
diff --git a/components/navigable-menu/README.md b/components/navigable-menu/README.md
new file mode 100644
index 00000000000000..202180a556fa3d
--- /dev/null
+++ b/components/navigable-menu/README.md
@@ -0,0 +1,40 @@
+NavigableMenu
+=============
+
+NavigableMenu is a React component to render a menu navigable using arrow keys. The children of the menu must be tabbables
+
+## Usage
+
+
+```jsx
+import { NavigableMenu, Button } from '@wordpress/components';
+
+function MyMenu() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+## Props
+
+The component accepts the following props. Props not included in this set will be applied to the element wrapping Navigable Menu.
+
+### orientation
+
+The orientation of the menu. It could be "vertical" or "horizontal"
+
+- Type: `String`
+- Required: No
+- Default: `"vertical"`
+
+## onNavigate
+
+A callback invoked when the menu navigates to one of its children passing the index as an argument
+
+- Type: `Function`
+- Required: No
diff --git a/components/navigable-menu/index.js b/components/navigable-menu/index.js
new file mode 100644
index 00000000000000..11ea3cbe1ebb18
--- /dev/null
+++ b/components/navigable-menu/index.js
@@ -0,0 +1,72 @@
+/**
+ * External Dependencies
+ */
+import { omit, noop } from 'lodash';
+
+/**
+ * WordPress Dependencies
+ */
+import { Component } from '@wordpress/element';
+import { focus, keycodes } from '@wordpress/utils';
+
+/**
+ * Module Constants
+ */
+const { UP, DOWN, LEFT, RIGHT, TAB } = keycodes;
+
+class NavigableMenu extends Component {
+ constructor() {
+ super( ...arguments );
+ this.bindContainer = this.bindContainer.bind( this );
+ this.onKeyDown = this.onKeyDown.bind( this );
+ }
+
+ bindContainer( ref ) {
+ this.container = ref;
+ }
+
+ onKeyDown( event ) {
+ const { orientation = 'vertical', onNavigate = noop } = this.props;
+ if (
+ ( orientation === 'vertical' && [ UP, DOWN, TAB ].indexOf( event.keyCode ) === -1 ) ||
+ ( orientation === 'horizontal' && [ RIGHT, LEFT, TAB ].indexOf( event.keyCode ) === -1 )
+ ) {
+ return;
+ }
+
+ const tabbables = focus.tabbable
+ .find( this.container )
+ .filter( ( node ) => node.parentElement === this.container );
+ const indexOfTabbable = tabbables.indexOf( document.activeElement );
+ if ( indexOfTabbable === -1 ) {
+ return;
+ }
+
+ const offset = (
+ [ UP, LEFT ].indexOf( event.keyCode ) !== -1 ||
+ ( event.keyCode === TAB && event.shiftKey )
+ ) ? -1 : 1;
+ let nextIndex = indexOfTabbable + offset;
+ nextIndex = nextIndex === -1 ? tabbables.length - 1 : nextIndex;
+ nextIndex = nextIndex === tabbables.length ? 0 : nextIndex;
+ const nextTabbable = tabbables[ nextIndex ];
+ event.stopPropagation();
+ event.preventDefault();
+ if ( nextTabbable ) {
+ nextTabbable.focus();
+ onNavigate( nextIndex );
+ }
+ }
+
+ render() {
+ const { children, ...props } = this.props;
+
+ return (
+
+ { children }
+
+ );
+ }
+}
+
+export default NavigableMenu;
diff --git a/components/navigable-menu/test/index.js b/components/navigable-menu/test/index.js
new file mode 100644
index 00000000000000..e14f21bedb7fcb
--- /dev/null
+++ b/components/navigable-menu/test/index.js
@@ -0,0 +1,49 @@
+/**
+ * External dependencies
+ */
+import { mount } from 'enzyme';
+
+/**
+ * WordPress dependencies
+ */
+import { keycodes } from '@wordpress/utils';
+
+/**
+ * Internal dependencies
+ */
+import NavigableMenu from '../';
+
+const { UP, DOWN } = keycodes;
+
+describe( 'NavigableMenu', () => {
+ // Skipping this this because the `isVisible` check in utils/focus/tabbable.js always returns false in tests
+ // Probbably a jsdom issue
+ it.skip( 'should navigate by keypresses', () => {
+ let currentIndex = 0;
+ const wrapper = mount( (
+ currentIndex = index }>
+
+
+
+
+ ) );
+
+ const container = wrapper.find( 'div' );
+ wrapper.find( '#btn1' ).get( 0 ).focus();
+
+ // Navigate options
+ function assertKeyDown( keyCode, expectedActiveIndex ) {
+ container.simulate( 'keydown', {
+ stopPropagation: () => {},
+ preventDefault: () => {},
+ keyCode,
+ } );
+
+ expect( currentIndex ).toBe( expectedActiveIndex );
+ }
+
+ assertKeyDown( DOWN, 1 );
+ assertKeyDown( DOWN, 2 );
+ assertKeyDown( UP, 1 );
+ } );
+} );
diff --git a/components/popover/index.js b/components/popover/index.js
index 884d8d561fc401..b4a80b71a1255e 100644
--- a/components/popover/index.js
+++ b/components/popover/index.js
@@ -188,6 +188,7 @@ export class Popover extends Component {
// Close on escape
if ( event.keyCode === ESCAPE && onClose ) {
+ event.stopPropagation();
onClose();
}
diff --git a/editor/assets/stylesheets/_z-index.scss b/editor/assets/stylesheets/_z-index.scss
index 7352b15a88e409..fde57453120ed1 100644
--- a/editor/assets/stylesheets/_z-index.scss
+++ b/editor/assets/stylesheets/_z-index.scss
@@ -15,7 +15,6 @@ $z-layers: (
'.editor-inserter__tab.is-active': 1,
'.components-panel__header': 1,
'.blocks-format-toolbar__link-modal': 1,
- '.editor-block-switcher__menu': 2,
'.editor-block-mover': 10,
'.blocks-gallery-image__inline-menu': 10,
'.editor-header': 20,
diff --git a/editor/block-switcher/index.js b/editor/block-switcher/index.js
index bc38d3f59dc569..7d78f6082ce957 100644
--- a/editor/block-switcher/index.js
+++ b/editor/block-switcher/index.js
@@ -3,15 +3,14 @@
*/
import { connect } from 'react-redux';
import { uniq, get, reduce, find } from 'lodash';
-import clickOutside from 'react-click-outside';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { Component } from '@wordpress/element';
-import { Dashicon, IconButton } from '@wordpress/components';
+import { Dropdown, Dashicon, IconButton, Toolbar, NavigableMenu } from '@wordpress/components';
import { getBlockType, getBlockTypes, switchToBlockType } from '@wordpress/blocks';
+import { keycodes } from '@wordpress/utils';
/**
* Internal dependencies
@@ -20,85 +19,77 @@ import './style.scss';
import { replaceBlocks } from '../actions';
import { getBlock } from '../selectors';
-class BlockSwitcher extends Component {
- constructor() {
- super( ...arguments );
- this.toggleMenu = this.toggleMenu.bind( this );
- this.state = {
- open: false,
- };
- }
-
- handleClickOutside() {
- if ( ! this.state.open ) {
- return;
- }
-
- this.toggleMenu();
- }
+/**
+ * Module Constants
+ */
+const { DOWN } = keycodes;
- toggleMenu() {
- this.setState( ( state ) => ( {
- open: ! state.open,
- } ) );
- }
+function BlockSwitcher( { block, onTransform } ) {
+ const blockType = getBlockType( block.name );
+ const blocksToBeTransformedFrom = reduce( getBlockTypes(), ( memo, type ) => {
+ const transformFrom = get( type, 'transforms.from', [] );
+ const transformation = find( transformFrom, t => t.type === 'block' && t.blocks.indexOf( block.name ) !== -1 );
+ return transformation ? memo.concat( [ type.name ] ) : memo;
+ }, [] );
+ const blocksToBeTransformedTo = get( blockType, 'transforms.to', [] )
+ .reduce( ( memo, transformation ) => memo.concat( transformation.blocks ), [] );
+ const allowedBlocks = uniq( blocksToBeTransformedFrom.concat( blocksToBeTransformedTo ) )
+ .reduce( ( memo, name ) => {
+ const type = getBlockType( name );
+ return !! type ? memo.concat( type ) : memo;
+ }, [] );
- switchBlockType( name ) {
- return () => {
- this.setState( {
- open: false,
- } );
- this.props.onTransform( this.props.block, name );
- };
+ if ( ! allowedBlocks.length ) {
+ return null;
}
- render() {
- const blockType = getBlockType( this.props.block.name );
- const blocksToBeTransformedFrom = reduce( getBlockTypes(), ( memo, block ) => {
- const transformFrom = get( block, 'transforms.from', [] );
- const transformation = find( transformFrom, t => t.type === 'block' && t.blocks.indexOf( this.props.block.name ) !== -1 );
- return transformation ? memo.concat( [ block.name ] ) : memo;
- }, [] );
- const blocksToBeTransformedTo = get( blockType, 'transforms.to', [] )
- .reduce( ( memo, transformation ) => memo.concat( transformation.blocks ), [] );
- const allowedBlocks = uniq( blocksToBeTransformedFrom.concat( blocksToBeTransformedTo ) )
- .reduce( ( memo, name ) => {
- const block = getBlockType( name );
- return !! block ? memo.concat( block ) : memo;
- }, [] );
-
- if ( ! allowedBlocks.length ) {
- return null;
- }
+ return (
+ {
+ const openOnArrowDown = ( event ) => {
+ if ( ! isOpen && event.keyCode === DOWN ) {
+ event.preventDefault();
+ event.stopPropagation();
+ onToggle();
+ }
+ };
- return (
-
-
-
-
- { this.state.open &&
-
+
+
+
+
+ );
+ } }
+ renderContent={ ( { onClose } ) => (
+
+
+ { __( 'Transform into:' ) }
+
+
-
- { __( 'Transform into:' ) }
-
{ allowedBlocks.map( ( { name, title, icon } ) => (
{
+ onTransform( block, name );
+ onClose();
+ } }
className="editor-block-switcher__menu-item"
icon={ icon }
role="menuitem"
@@ -106,11 +97,11 @@ class BlockSwitcher extends Component {
{ title }
) ) }
-
- }
-
- );
- }
+
+
+ ) }
+ />
+ );
}
export default connect(
@@ -125,4 +116,4 @@ export default connect(
) );
},
} )
-)( clickOutside( BlockSwitcher ) );
+)( BlockSwitcher );
diff --git a/editor/block-switcher/style.scss b/editor/block-switcher/style.scss
index 2f7f428b3f543b..233d3de4971bcd 100644
--- a/editor/block-switcher/style.scss
+++ b/editor/block-switcher/style.scss
@@ -1,10 +1,12 @@
.editor-block-switcher {
+ position: relative;
border: 1px solid $light-gray-500;
background-color: $white;
font-family: $default-font;
font-size: $default-font-size;
line-height: $default-line-height;
margin-right: -1px;
+ margin-bottom: -1px;
}
.editor-block-switcher__toggle {
@@ -21,15 +23,15 @@
}
}
+.editor-block-switcher__popover .components-popover__content {
+ width: 200px;
+}
+
.editor-block-switcher__menu {
- position: absolute;
- top: $block-controls-height - 1px;
- left: 0;
box-shadow: $shadow-popover;
border: 1px solid $light-gray-500;
background: $white;
padding: 3px 3px 0 3px;
- z-index: z-index( '.editor-block-switcher__menu' );
}
.editor-block-switcher__menu-title {