From 41d62f437c119efa35b5c40143a1a032b832b652 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 3 May 2023 14:35:16 +1000 Subject: [PATCH 1/5] List View: Allow right-click to open block settings dropdown, add setting --- .../list-view/block-select-button.js | 2 ++ .../src/components/list-view/block.js | 36 +++++++++++++++++++ .../src/components/preferences-modal/index.js | 9 +++++ packages/edit-post/src/editor.js | 6 ++++ packages/edit-post/src/index.js | 1 + .../block-editor/use-site-editor-settings.js | 7 ++++ .../src/components/preferences-modal/index.js | 7 ++++ packages/edit-site/src/index.js | 1 + .../provider/use-block-editor-settings.js | 1 + 9 files changed, 70 insertions(+) diff --git a/packages/block-editor/src/components/list-view/block-select-button.js b/packages/block-editor/src/components/list-view/block-select-button.js index 25de5483f5192e..728eb885c51752 100644 --- a/packages/block-editor/src/components/list-view/block-select-button.js +++ b/packages/block-editor/src/components/list-view/block-select-button.js @@ -38,6 +38,7 @@ function ListViewBlockSelectButton( className, block: { clientId }, onClick, + onContextMenu, onToggleExpanded, tabIndex, onFocus, @@ -237,6 +238,7 @@ function ListViewBlockSelectButton( className ) } onClick={ onClick } + onContextMenu={ onContextMenu } onKeyDown={ onKeyDownHandler } ref={ ref } tabIndex={ tabIndex } diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js index 4957f79fa0d481..1aa1ac5549fc1d 100644 --- a/packages/block-editor/src/components/list-view/block.js +++ b/packages/block-editor/src/components/list-view/block.js @@ -53,7 +53,9 @@ function ListViewBlock( { } ) { const cellRef = useRef( null ); const rowRef = useRef( null ); + const settingsRef = useRef( null ); const [ isHovered, setIsHovered ] = useState( false ); + const [ settingsAnchorRect, setSettingsAnchorRect ] = useState(); const { isLocked, canEdit } = useBlockLock( clientId ); @@ -83,6 +85,13 @@ function ListViewBlock( { [ clientId ] ); + const { allowRightClickOverrides } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return { + allowRightClickOverrides: getSettings().allowRightClickOverrides, + }; + } ); + const showBlockActions = // When a block hides its toolbar it also hides the block settings menu, // since that menu is part of the toolbar in the editor canvas. @@ -190,6 +199,27 @@ function ListViewBlock( { [ clientId, expand, collapse, isExpanded ] ); + // Allow right-clicking an item in the List View to open up the block settings dropdown. + const onContextMenu = useCallback( + ( event ) => { + if ( showBlockActions && allowRightClickOverrides ) { + settingsRef.current?.click(); + // Ensure the position of the settings dropdown is at the cursor. + setSettingsAnchorRect( + new window.DOMRect( event.clientX, event.clientY, 0, 0 ) + ); + event.preventDefault(); + } + }, + [ settingsRef, showBlockActions ] + ); + + const clearSettingsAnchorRect = useCallback( () => { + // Clear the custom position for the settings dropdown so that it is restored back + // to being anchored to the DropdownMenu toggle button. + setSettingsAnchorRect( undefined ); + }, [ setSettingsAnchorRect ] ); + let colSpan; if ( hasRenderedMovers ) { colSpan = 2; @@ -257,6 +287,7 @@ function ListViewBlock( { { ( { ref, tabIndex, onFocus } ) => ( ) } + + ), }, diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 82014ad06eb493..9dcdb2ce99b5bc 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -52,6 +52,7 @@ export function initializeEditor( id, settings ) { // We dispatch actions and update the store synchronously before rendering // so that we won't trigger unnecessary re-renders with useEffect. dispatch( preferencesStore ).setDefaults( 'core/edit-site', { + allowRightClickOverrides: true, editorMode: 'visual', fixedToolbar: false, focusMode: false, diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index de5d9cf43437d4..34aa472a9921d5 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -29,6 +29,7 @@ const BLOCK_EDITOR_SETTINGS = [ '__unstableGalleryWithImageBlocks', 'alignWide', 'allowedBlockTypes', + 'allowRightClickOverrides', 'blockInspectorTabs', 'allowedMimeTypes', 'bodyPlaceholder', From 7c232648c45d6e4eb969c2eb42ef225054fbb336 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 25 May 2023 10:06:20 +1000 Subject: [PATCH 2/5] Fix flicker of focus state when initially right clicking --- .../list-view/block-select-button.js | 2 + .../src/components/list-view/block.js | 42 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/list-view/block-select-button.js b/packages/block-editor/src/components/list-view/block-select-button.js index 728eb885c51752..6b9de943ea0bf2 100644 --- a/packages/block-editor/src/components/list-view/block-select-button.js +++ b/packages/block-editor/src/components/list-view/block-select-button.js @@ -39,6 +39,7 @@ function ListViewBlockSelectButton( block: { clientId }, onClick, onContextMenu, + onMouseDown, onToggleExpanded, tabIndex, onFocus, @@ -240,6 +241,7 @@ function ListViewBlockSelectButton( onClick={ onClick } onContextMenu={ onContextMenu } onKeyDown={ onKeyDownHandler } + onMouseDown={ onMouseDown } ref={ ref } tabIndex={ tabIndex } onFocus={ onFocus } diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js index 1aa1ac5549fc1d..0532ecc04d27c0 100644 --- a/packages/block-editor/src/components/list-view/block.js +++ b/packages/block-editor/src/components/list-view/block.js @@ -13,7 +13,13 @@ import { } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; import { moreVertical } from '@wordpress/icons'; -import { useState, useRef, useCallback, memo } from '@wordpress/element'; +import { + useCallback, + useMemo, + useState, + useRef, + memo, +} from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { sprintf, __ } from '@wordpress/i18n'; import { ESCAPE } from '@wordpress/keycodes'; @@ -211,9 +217,38 @@ function ListViewBlock( { event.preventDefault(); } }, - [ settingsRef, showBlockActions ] + [ allowRightClickOverrides, settingsRef, showBlockActions ] + ); + + const onMouseDown = useCallback( + ( event ) => { + // Prevent right-click from focusing the block, + // because focus will be handled when opening the block settings dropdown. + if ( allowRightClickOverrides && event.button === 2 ) { + event.preventDefault(); + } + }, + [ allowRightClickOverrides ] ); + const settingsPopoverAnchor = useMemo( () => { + const { ownerDocument } = rowRef?.current || {}; + + // If no custom position is set, the settings dropdown will be anchored to the + // DropdownMenu toggle button. + if ( ! settingsAnchorRect || ! ownerDocument ) { + return undefined; + } + + // Position the settings dropdown at the cursor when right-clicking a block. + return { + ownerDocument, + getBoundingClientRect() { + return settingsAnchorRect; + }, + }; + }, [ settingsAnchorRect ] ); + const clearSettingsAnchorRect = useCallback( () => { // Clear the custom position for the settings dropdown so that it is restored back // to being anchored to the DropdownMenu toggle button. @@ -288,6 +323,7 @@ function ListViewBlock( { block={ block } onClick={ selectEditorBlock } onContextMenu={ onContextMenu } + onMouseDown={ onMouseDown } onToggleExpanded={ toggleExpanded } isSelected={ isSelected } position={ position } @@ -355,7 +391,7 @@ function ListViewBlock( { icon={ moreVertical } label={ settingsAriaLabel } popoverProps={ { - anchorRect: settingsAnchorRect, // Used to position the settings at the cursor on right-click. + anchor: settingsPopoverAnchor, // Used to position the settings at the cursor on right-click. } } toggleProps={ { ref, From 007a1edc56a1e8e3524701e7da32ab7b1f988673 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:56:29 +1000 Subject: [PATCH 3/5] Tidy up useSelect --- .../block-editor/src/components/list-view/block.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js index 0532ecc04d27c0..a90bf116e1d085 100644 --- a/packages/block-editor/src/components/list-view/block.js +++ b/packages/block-editor/src/components/list-view/block.js @@ -90,13 +90,11 @@ function ListViewBlock( { }, [ clientId ] ); - - const { allowRightClickOverrides } = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - return { - allowRightClickOverrides: getSettings().allowRightClickOverrides, - }; - } ); + const allowRightClickOverrides = useSelect( + ( select ) => + select( blockEditorStore ).getSettings().allowRightClickOverrides, + [] + ); const showBlockActions = // When a block hides its toolbar it also hides the block settings menu, From 35d42be5a8a05746ea9a340948c1044dccf59fa8 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:09:16 +1100 Subject: [PATCH 4/5] Fix e2e test (hopefully) --- test/e2e/specs/editor/various/a11y.spec.js | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/test/e2e/specs/editor/various/a11y.spec.js b/test/e2e/specs/editor/various/a11y.spec.js index 05c4ea3b8e97e3..3ec7318ab89e78 100644 --- a/test/e2e/specs/editor/various/a11y.spec.js +++ b/test/e2e/specs/editor/various/a11y.spec.js @@ -124,6 +124,13 @@ test.describe( 'a11y (@firefox, @webkit)', () => { page, pageUtils, } ) => { + // Note: this test depends on a particular viewport height to determine whether or not + // the modal content is scrollable. If this tests fails and needs to be debugged locally, + // double-check the viewport height when running locally versus in CI. Additionally, + // when adding or removing items from the preference menu, this test may need to be updated + // if the height of panels has changed. It would be good to find a more robust way to test + // this behavior. + // Open the top bar Options menu. await page.click( 'role=region[name="Editor top bar"i] >> role=button[name="Options"i]' @@ -145,6 +152,9 @@ test.describe( 'a11y (@firefox, @webkit)', () => { const generalTab = preferencesModal.locator( 'role=tab[name="General"i]' ); + const accessibilityTab = preferencesModal.locator( + 'role=tab[name="Accessibility"i]' + ); const blocksTab = preferencesModal.locator( 'role=tab[name="Blocks"i]' ); @@ -165,9 +175,20 @@ test.describe( 'a11y (@firefox, @webkit)', () => { await tab.focus(); } - // The General tab panel content is short and not scrollable. - // Check it's not focusable. + // The Accessibility tab is currently short and not scrollable. + // Check that it cannot be focused by tabbing. Note: this test depends + // on a particular viewport height to determine whether or not the + // modal content is scrollable. If additional Accessibility options are + // added, then eventually this test will fail. + // TODO: find a more robust way to test this behavior. await clickAndFocusTab( generalTab ); + // Navigate down to the Accessibility tab. + await pageUtils.pressKeys( 'ArrowDown', { times: 2 } ); + // Check the Accessibility tab panel is visible. + await expect( + preferencesModal.locator( 'role=tabpanel[name="Accessibility"i]' ) + ).toBeVisible(); + await expect( accessibilityTab ).toBeFocused(); await pageUtils.pressKeys( 'Shift+Tab' ); await expect( closeButton ).toBeFocused(); await pageUtils.pressKeys( 'Shift+Tab' ); From 452376760e91e594a877428b9de1a1b30e7c9e56 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:21:47 +1100 Subject: [PATCH 5/5] Update help text for preferences items --- packages/edit-post/src/components/preferences-modal/index.js | 2 +- packages/edit-site/src/components/preferences-modal/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-post/src/components/preferences-modal/index.js b/packages/edit-post/src/components/preferences-modal/index.js index a4c41c0c14bbc4..2e46572efec0a1 100644 --- a/packages/edit-post/src/components/preferences-modal/index.js +++ b/packages/edit-post/src/components/preferences-modal/index.js @@ -119,7 +119,7 @@ export default function EditPostPreferencesModal() {