Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the inserter behave as a popover #24429

Merged
merged 5 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,12 @@ function QuickInserter( {
const onBrowseAll = () => {
// We have to select the previous block because the menu inserter
// inserts the new block after the selected one.
// Ideally, this selection shouldn't focus the block to avoid the setTimeout.
selectBlock( previousBlockClientId );
setInserterIsOpened( true );
// eslint-disable-next-line @wordpress/react-no-unsafe-timeout
setTimeout( () => {
setInserterIsOpened( true );
} );
};

// Disable reason (no-autofocus): The inserter menu is a modal display, not one which
Expand Down
25 changes: 23 additions & 2 deletions packages/e2e-test-utils/src/inserter.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ async function toggleGlobalBlockInserter() {
*/
export async function searchForBlock( searchTerm ) {
await openGlobalBlockInserter();
await page.waitForSelector( INSERTER_SEARCH_SELECTOR );
await page.focus( INSERTER_SEARCH_SELECTOR );
await pressKeyWithModifier( 'primary', 'a' );
await page.keyboard.type( searchTerm );
Expand All @@ -71,10 +72,11 @@ export async function searchForBlock( searchTerm ) {
export async function searchForPattern( searchTerm ) {
await openGlobalBlockInserter();
// Select the patterns tab
const [ tab ] = await page.$x(
const tab = await page.waitForXPath(
'//div[contains(@class, "block-editor-inserter__tabs")]//button[.="Patterns"]'
);
await tab.click();
await page.waitForSelector( INSERTER_SEARCH_SELECTOR );
await page.focus( INSERTER_SEARCH_SELECTOR );
await pressKeyWithModifier( 'primary', 'a' );
await page.keyboard.type( searchTerm );
Expand All @@ -96,10 +98,11 @@ export async function searchForReusableBlock( searchTerm ) {
);

// Select the reusable blocks tab.
const [ tab ] = await page.$x(
const tab = await page.waitForXPath(
'//div[contains(@class, "block-editor-inserter__tabs")]//button[text()="Reusable"]'
);
await tab.click();
await page.waitForSelector( INSERTER_SEARCH_SELECTOR );
await page.focus( INSERTER_SEARCH_SELECTOR );
await pressKeyWithModifier( 'primary', 'a' );
await page.keyboard.type( searchTerm );
Expand All @@ -117,6 +120,12 @@ export async function insertBlock( searchTerm ) {
await page.$x( `//button//span[contains(text(), '${ searchTerm }')]` )
)[ 0 ];
await insertButton.click();
// We should wait until the inserter closes and the focus moves to the content.
await page.waitForFunction( () =>
document.body
.querySelector( '.block-editor-block-list__layout' )
.contains( document.activeElement )
);
}

/**
Expand All @@ -133,6 +142,12 @@ export async function insertPattern( searchTerm ) {
)
)[ 0 ];
await insertButton.click();
// We should wait until the inserter closes and the focus moves to the content.
await page.waitForFunction( () =>
document.body
.querySelector( '.block-editor-block-list__layout' )
.contains( document.activeElement )
);
}

/**
Expand All @@ -148,4 +163,10 @@ export async function insertReusableBlock( searchTerm ) {
await page.$x( `//button//span[contains(text(), '${ searchTerm }')]` )
)[ 0 ];
await insertButton.click();
// We should wait until the inserter closes and the focus moves to the content.
await page.waitForFunction( () =>
document.body
.querySelector( '.block-editor-block-list__layout' )
.contains( document.activeElement )
);
}
43 changes: 12 additions & 31 deletions packages/e2e-tests/specs/editor/plugins/align-hook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
insertBlock,
selectBlockByClientId,
setPostContent,
clickBlockToolbarButton,
} from '@wordpress/e2e-test-utils';

const alignLabels = {
Expand All @@ -21,9 +22,6 @@ const alignLabels = {
};

describe( 'Align Hook Works As Expected', () => {
const CHANGE_ALIGNMENT_BUTTON_SELECTOR =
'.block-editor-block-toolbar .components-dropdown-menu__toggle[aria-label="Change alignment"]';

beforeAll( async () => {
await activatePlugin( 'gutenberg-test-align-hook' );
} );
Expand All @@ -37,11 +35,7 @@ describe( 'Align Hook Works As Expected', () => {
} );

const getAlignmentToolbarLabels = async () => {
const element = await page.waitForSelector(
CHANGE_ALIGNMENT_BUTTON_SELECTOR
);
await element.click();

await clickBlockToolbarButton( 'Change alignment' );
const buttonLabels = await page.evaluate( () => {
return Array.from(
document.querySelectorAll(
Expand All @@ -57,20 +51,14 @@ describe( 'Align Hook Works As Expected', () => {
const createShowsTheExpectedButtonsTest = ( blockName, buttonLabels ) => {
it( 'Shows the expected buttons on the alignment toolbar', async () => {
await insertBlock( blockName );

expect( await getAlignmentToolbarLabels() ).toEqual( buttonLabels );
} );
};

const createDoesNotApplyAlignmentByDefaultTest = ( blockName ) => {
it( 'Does not apply any alignment by default', async () => {
await insertBlock( blockName );

// verify no alignment button is in pressed state
const element = await page.waitForSelector(
CHANGE_ALIGNMENT_BUTTON_SELECTOR
);
await element.click();
await clickBlockToolbarButton( 'Change alignment' );
const pressedButtons = await page.$$(
'.components-dropdown-menu__menu button.is-active'
);
Expand All @@ -95,14 +83,11 @@ describe( 'Align Hook Works As Expected', () => {
'.components-dropdown-menu__menu button.is-active';
// set the specified alignment.
await insertBlock( blockName );
const element = await page.waitForSelector(
CHANGE_ALIGNMENT_BUTTON_SELECTOR
);
await element.click();
await clickBlockToolbarButton( 'Change alignment' );
await ( await page.$x( BUTTON_XPATH ) )[ 0 ].click();

// verify the button of the specified alignment is pressed.
await page.click( CHANGE_ALIGNMENT_BUTTON_SELECTOR );
await clickBlockToolbarButton( 'Change alignment' );
let pressedButtons = await page.$$( BUTTON_PRESSED_SELECTOR );
expect( pressedButtons ).toHaveLength( 1 );

Expand All @@ -119,11 +104,11 @@ describe( 'Align Hook Works As Expected', () => {
);

// remove the alignment.
await page.click( CHANGE_ALIGNMENT_BUTTON_SELECTOR );
await clickBlockToolbarButton( 'Change alignment' );
await ( await page.$x( BUTTON_XPATH ) )[ 0 ].click();

// verify no alignment button is in pressed state.
await page.click( CHANGE_ALIGNMENT_BUTTON_SELECTOR );
await clickBlockToolbarButton( 'Change alignment' );
pressedButtons = await page.$$( BUTTON_PRESSED_SELECTOR );
expect( pressedButtons ).toHaveLength( 0 );

Expand All @@ -139,7 +124,7 @@ describe( 'Align Hook Works As Expected', () => {
);

// verify no alignment button is in pressed state after parsing the block.
await page.click( CHANGE_ALIGNMENT_BUTTON_SELECTOR );
await clickBlockToolbarButton( 'Change alignment' );
pressedButtons = await page.$$( BUTTON_PRESSED_SELECTOR );
expect( pressedButtons ).toHaveLength( 0 );
} );
Expand All @@ -149,6 +134,8 @@ describe( 'Align Hook Works As Expected', () => {
const BLOCK_NAME = 'Test No Alignment Set';
it( 'Shows no alignment buttons on the alignment toolbar', async () => {
await insertBlock( BLOCK_NAME );
const CHANGE_ALIGNMENT_BUTTON_SELECTOR =
'.block-editor-block-toolbar .components-dropdown-menu__toggle[aria-label="Change alignment"]';
const changeAlignmentButton = await page.$(
CHANGE_ALIGNMENT_BUTTON_SELECTOR
);
Expand Down Expand Up @@ -205,10 +192,7 @@ describe( 'Align Hook Works As Expected', () => {
it( 'Applies the selected alignment by default', async () => {
await insertBlock( BLOCK_NAME );
// verify the correct alignment button is pressed
const element = await page.waitForSelector(
CHANGE_ALIGNMENT_BUTTON_SELECTOR
);
await element.click();
await clickBlockToolbarButton( 'Change alignment' );
const selectedAlignmentControls = await page.$x(
SELECTED_ALIGNMENT_CONTROL_SELECTOR
);
Expand All @@ -225,10 +209,7 @@ describe( 'Align Hook Works As Expected', () => {
it( 'Can remove the default alignment and the align attribute equals none but alignnone class is not applied', async () => {
await insertBlock( BLOCK_NAME );
// remove the alignment.
const element = await page.waitForSelector(
CHANGE_ALIGNMENT_BUTTON_SELECTOR
);
await element.click();
await clickBlockToolbarButton( 'Change alignment' );
const [ selectedAlignmentControl ] = await page.$x(
SELECTED_ALIGNMENT_CONTROL_SELECTOR
);
Expand Down
7 changes: 3 additions & 4 deletions packages/e2e-tests/specs/editor/various/adding-blocks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,12 @@ describe( 'adding blocks', () => {
);
await browseAll.click();
const inserterMenuInputSelector =
'.block-editor-inserter__menu .block-editor-inserter__search-input';
await page.waitForSelector( inserterMenuInputSelector );
const inserterMenuSearchInput = await page.$(
'.edit-post-layout__inserter-panel .block-editor-inserter__search-input';
const inserterMenuSearchInput = await page.waitForSelector(
inserterMenuInputSelector
);
inserterMenuSearchInput.type( 'cover' );
const coverBlock = await page.$(
const coverBlock = await page.waitForSelector(
'.block-editor-block-types-list .editor-block-list-item-cover'
);
await coverBlock.click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const navigateToContentEditorTop = async () => {
// Use 'Ctrl+`' to return to the top of the editor
await pressKeyWithModifier( 'ctrl', '`' );
await pressKeyWithModifier( 'ctrl', '`' );
await pressKeyWithModifier( 'ctrl', '`' );
};

const tabThroughParagraphBlock = async ( paragraphText ) => {
Expand Down
15 changes: 14 additions & 1 deletion packages/edit-post/src/components/header/header-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import {
__experimentalToolbarItem as ToolbarItem,
} from '@wordpress/components';
import { plus } from '@wordpress/icons';
import { useRef } from '@wordpress/element';

function HeaderToolbar() {
const inserterButton = useRef();
const { setIsInserterOpened } = useDispatch( 'core/edit-post' );
const {
hasFixedToolbar,
Expand Down Expand Up @@ -72,11 +74,22 @@ function HeaderToolbar() {
aria-label={ toolbarAriaLabel }
>
<ToolbarItem
ref={ inserterButton }
as={ Button }
className="edit-post-header-toolbar__inserter-toggle"
isPrimary
isPressed={ isInserterOpened }
onClick={ () => setIsInserterOpened( ! isInserterOpened ) }
onMouseDown={ ( event ) => {
event.preventDefault();
} }
onClick={ () => {
if ( isInserterOpened ) {
// Focusing the inserter button closes the inserter popover
inserterButton.current.focus();
} else {
setIsInserterOpened( true );
}
} }
disabled={ ! isInserterEnabled }
icon={ plus }
label={ _x(
Expand Down
50 changes: 29 additions & 21 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import SettingsSidebar from '../sidebar/settings-sidebar';
import MetaBoxes from '../meta-boxes';
import WelcomeGuide from '../welcome-guide';
import ActionsPanel from './actions-panel';
import PopoverWrapper from './popover-wrapper';

const interfaceLabels = {
leftSidebar: __( 'Block library' ),
Expand Down Expand Up @@ -178,29 +179,36 @@ function Layout() {
leftSidebar={
mode === 'visual' &&
isInserterOpened && (
<div className="edit-post-layout__inserter-panel">
<div className="edit-post-layout__inserter-panel-header">
<Button
icon={ close }
onClick={ () =>
setIsInserterOpened( false )
}
/>
</div>
<div className="edit-post-layout__inserter-panel-content">
<Library
showMostUsedBlocks={
showMostUsedBlocks
}
showInserterHelpPanel
onSelect={ () => {
if ( isMobileViewport ) {
setIsInserterOpened( false );
<PopoverWrapper
className="edit-post-layout__inserter-panel-popover-wrapper"
onClose={ () => setIsInserterOpened( false ) }
>
<div className="edit-post-layout__inserter-panel">
<div className="edit-post-layout__inserter-panel-header">
<Button
icon={ close }
onClick={ () =>
setIsInserterOpened( false )
}
/>
</div>
<div className="edit-post-layout__inserter-panel-content">
<Library
showMostUsedBlocks={
showMostUsedBlocks
}
} }
/>
showInserterHelpPanel
onSelect={ () => {
if ( isMobileViewport ) {
setIsInserterOpened(
false
);
}
} }
/>
</div>
</div>
</div>
</PopoverWrapper>
)
}
sidebar={
Expand Down
57 changes: 57 additions & 0 deletions packages/edit-post/src/components/layout/popover-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* WordPress dependencies
*/
import {
withConstrainedTabbing,
withFocusReturn,
withFocusOutside,
} from '@wordpress/components';
import { Component } from '@wordpress/element';
import { ESCAPE } from '@wordpress/keycodes';

function stopPropagation( event ) {
event.stopPropagation();
}

const DetectOutside = withFocusOutside(
class extends Component {
handleFocusOutside( event ) {
this.props.onFocusOutside( event );
}

render() {
return this.props.children;
}
}
);

const FocusManaged = withConstrainedTabbing(
withFocusReturn( ( { children } ) => children )
);

export default function PopoverWrapper( { onClose, children, className } ) {
// Event handlers
const maybeClose = ( event ) => {
// Close on escape
if ( event.keyCode === ESCAPE && onClose ) {
event.stopPropagation();
onClose();
}
};

// Disable reason: this stops certain events from propagating outside of the component.
// - onMouseDown is disabled as this can cause interactions with other DOM elements
/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<div
className={ className }
onKeyDown={ maybeClose }
onMouseDown={ stopPropagation }
>
<DetectOutside onFocusOutside={ onClose }>
<FocusManaged>{ children }</FocusManaged>
</DetectOutside>
</div>
);
/* eslint-enable jsx-a11y/no-static-element-interactions */
}
Loading