Skip to content

Commit

Permalink
Template Parts & Reusable Blocks - try overlay element for clickthrou…
Browse files Browse the repository at this point in the history
…gh to edit pattern. (#31109)

* clickthrough for template part

* make store names more consistent and accurate

* remove unnecessary selector change

* move color from hover to selected, add opacity value

* Add hover overlay

* bleh resizing issues

* kind of working...

* remove unnecessary popover goo

* fix for nested template parts and selection bleeding out of boundary

* remove unnecessary effect/state

* fix resizing issue

* try only selected color when focused

* cleanup css

* try overlay for initial selection only

* enable drag and drop

* only show borders on drag and drop

* ensure background color not on drag and drop

* apply to reusable blocks

* fix e2e tests

* try add basic test

* try fix test

* only use the afterEach where needed

* use css vars

* fix border color

* fix block toolbar issue

* fix width on classic themes

* fix width by moving overlay inside block wrapper

* fix bug with selection from list view

* add condition to dismissing

* show overlay when highlighting list view

* use selector for highlight style

* refactor and comments

* position overlay above resize containers

* fix nested entity test

* fix template part test

* fix intermittent errors on conversion tests

* remove box shadow on hover - block hover style will already be present

* add border back to overlay

* use blockProps in innerBlockProps as well

* cleanup which blockProps are passed to innerBlockProps

* use component as wrapper

* remove unnecessary layout selectors on TP e2es

* fix reusable blocks test xpath

Co-authored-by: James Koster <[email protected]>
  • Loading branch information
Addison-Stavlo and jameskoster authored Jun 25, 2021
1 parent 1be1059 commit afee31e
Show file tree
Hide file tree
Showing 14 changed files with 227 additions and 19 deletions.
9 changes: 9 additions & 0 deletions packages/base-styles/_functions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Converts a hex value into the rgb equivalent.
*
* @param {string} hex - the hexadecimal value to convert
* @return {string} comma separated rgb values
*/
@function hex-to-rgb($hex) {
@return red($hex), green($hex), blue($hex);
}
8 changes: 7 additions & 1 deletion packages/base-styles/_mixins.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "./functions";

/**
* Breakpoint mixins
*/
Expand Down Expand Up @@ -440,11 +442,15 @@
}

@mixin admin-scheme($color-primary) {
// Define RGB equivalents for use in rgba function.
// Hexadecimal css vars do not work in the rgba function.
--wp-admin-theme-color: #{$color-primary};

--wp-admin-theme-color--rgb: #{hex-to-rgb($color-primary)};
// Darker shades.
--wp-admin-theme-color-darker-10: #{darken($color-primary, 5%)};
--wp-admin-theme-color-darker-10--rgb: #{hex-to-rgb(darken($color-primary, 5%))};
--wp-admin-theme-color-darker-20: #{darken($color-primary, 10%)};
--wp-admin-theme-color-darker-20--rgb: #{hex-to-rgb(darken($color-primary, 10%))};

// Focus style width.
// Avoid rounding issues by showing a whole 2px for 1x screens, and 1.5px on high resolution screens.
Expand Down
3 changes: 3 additions & 0 deletions packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ $z-layers: (
// The toolbar, when contextual, should be above any adjacent nested block click overlays.
".block-editor-block-contextual-toolbar": 61,

// Ensures content overlay appears higher than resize containers used for image/video/etc.
".block-editor-block-content-overlay__overlay": 10,

// The block mover, particularly in nested contexts,
// should overlap most block content.
".block-editor-block-list__block.is-{selected,hovered} .block-editor-block-mover": 61,
Expand Down
101 changes: 101 additions & 0 deletions packages/block-editor/src/components/block-content-overlay/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';

/**
* External dependencies
*/
import classnames from 'classnames';

export default function BlockContentOverlay( {
clientId,
tagName: TagName = 'div',
wrapperProps,
className,
} ) {
const baseClassName = 'block-editor-block-content-overlay';
const [ isOverlayActive, setIsOverlayActive ] = useState( true );
const [ isHovered, setIsHovered ] = useState( false );

const {
isParentSelected,
hasChildSelected,
isDraggingBlocks,
isParentHighlighted,
} = useSelect(
( select ) => {
const {
isBlockSelected,
hasSelectedInnerBlock,
isDraggingBlocks: _isDraggingBlocks,
isBlockHighlighted,
} = select( blockEditorStore );
return {
isParentSelected: isBlockSelected( clientId ),
hasChildSelected: hasSelectedInnerBlock( clientId, true ),
isDraggingBlocks: _isDraggingBlocks(),
isParentHighlighted: isBlockHighlighted( clientId ),
};
},
[ clientId ]
);

const classes = classnames(
baseClassName,
wrapperProps?.className,
className,
{
'overlay-active': isOverlayActive,
'parent-highlighted': isParentHighlighted,
'is-dragging-blocks': isDraggingBlocks,
}
);

useEffect( () => {
// Reenable when blocks are not in use.
if ( ! isParentSelected && ! hasChildSelected && ! isOverlayActive ) {
setIsOverlayActive( true );
}
// Disable if parent selected by another means (such as list view).
// We check hover to ensure the overlay click interaction is not taking place.
// Trying to click the overlay will select the parent block via its 'focusin'
// listener on the wrapper, so if the block is selected while hovered we will
// let the mouseup disable the overlay instead.
if ( isParentSelected && ! isHovered && isOverlayActive ) {
setIsOverlayActive( false );
}
// Ensure overlay is disabled if a child block is selected.
if ( hasChildSelected && isOverlayActive ) {
setIsOverlayActive( false );
}
}, [ isParentSelected, hasChildSelected, isOverlayActive, isHovered ] );

// Disabled because the overlay div doesn't actually have a role or functionality
// as far as the a11y is concerned. We're just catching the first click so that
// the block can be selected without interacting with its contents.
/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<TagName
{ ...wrapperProps }
className={ classes }
onMouseEnter={ () => setIsHovered( true ) }
onMouseLeave={ () => setIsHovered( false ) }
>
{ isOverlayActive && (
<div
className={ `${ baseClassName }__overlay` }
onMouseUp={ () => setIsOverlayActive( false ) }
/>
) }
{ wrapperProps?.children }
</TagName>
);
}
/* eslint-enable jsx-a11y/no-static-element-interactions */
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Specificity required to ensure overlay width is not restricted to that
// of standard block content. The overlay's width should be as wide as
// its children require.
.editor-styles-wrapper .wp-block .block-editor-block-content-overlay__overlay {
max-width: none;
}

.block-editor-block-content-overlay {
.block-editor-block-content-overlay__overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: transparent;
border: none;
border-radius: $radius-block-ui;
z-index: z-index(".block-editor-block-content-overlay__overlay");
}

&:hover:not(.is-dragging-blocks),
&.parent-highlighted {
> .block-editor-block-content-overlay__overlay {
background: rgba(var(--wp-admin-theme-color--rgb), 0.1);
box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color) inset;
}
}

&.overlay-active:not(.is-dragging-blocks) {
*:not(.block-editor-block-content-overlay__overlay) {
pointer-events: none;
}
}

&.is-dragging-blocks {
box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color);
.block-editor-block-content-overlay__overlay {
pointer-events: none;
}
}
}
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
export { default as __experimentalBlockFullHeightAligmentControl } from './block-full-height-alignment-control';
export { default as __experimentalBlockAlignmentMatrixControl } from './block-alignment-matrix-control';
export { default as BlockBreadcrumb } from './block-breadcrumb';
export { default as __experimentalBlockContentOverlay } from './block-content-overlay';
export { BlockContextProvider } from './block-context';
export {
default as BlockControls,
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@import "./components/block-breadcrumb/style.scss";
@import "./components/block-card/style.scss";
@import "./components/block-compare/style.scss";
@import "./components/block-content-overlay/style.scss";
@import "./components/block-draggable/style.scss";
@import "./components/block-mobile-toolbar/style.scss";
@import "./components/block-mover/style.scss";
Expand Down
13 changes: 8 additions & 5 deletions packages/block-library/src/block/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { __ } from '@wordpress/i18n';
import {
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
__experimentalUseNoRecursiveRenders as useNoRecursiveRenders,
__experimentalBlockContentOverlay as BlockContentOverlay,
InnerBlocks,
BlockControls,
InspectorControls,
Expand Down Expand Up @@ -70,6 +71,8 @@ export default function ReusableBlockEdit( { attributes: { ref }, clientId } ) {
ref
);

const blockProps = useBlockProps();

const innerBlocksProps = useInnerBlocksProps(
{},
{
Expand All @@ -82,8 +85,6 @@ export default function ReusableBlockEdit( { attributes: { ref }, clientId } ) {
}
);

const blockProps = useBlockProps();

if ( hasAlreadyRendered ) {
return (
<div { ...blockProps }>
Expand Down Expand Up @@ -136,9 +137,11 @@ export default function ReusableBlockEdit( { attributes: { ref }, clientId } ) {
/>
</PanelBody>
</InspectorControls>
<div className="block-library-block__reusable-block-container">
{ <div { ...innerBlocksProps } /> }
</div>
<BlockContentOverlay
clientId={ clientId }
wrapperProps={ innerBlocksProps }
className="block-library-block__reusable-block-container"
/>
</div>
</RecursionProvider>
);
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/template-part/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export default function TemplatePartEdit( {
) }
{ isEntityAvailable && (
<TemplatePartInnerBlocks
clientId={ clientId }
tagName={ TagName }
blockProps={ blockProps }
postId={ templatePartId }
Expand Down
14 changes: 12 additions & 2 deletions packages/block-library/src/template-part/edit/inner-blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useEntityBlockEditor } from '@wordpress/core-data';
import {
InnerBlocks,
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
__experimentalBlockContentOverlay as BlockContentOverlay,
useSetting,
store as blockEditorStore,
} from '@wordpress/block-editor';
Expand All @@ -15,8 +16,9 @@ export default function TemplatePartInnerBlocks( {
postId: id,
hasInnerBlocks,
layout,
tagName: TagName,
tagName,
blockProps,
clientId,
} ) {
const themeSupportsLayout = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
Expand Down Expand Up @@ -45,6 +47,7 @@ export default function TemplatePartInnerBlocks( {
'wp_template_part',
{ id }
);

const innerBlocksProps = useInnerBlocksProps( blockProps, {
value: blocks,
onInput,
Expand All @@ -54,5 +57,12 @@ export default function TemplatePartInnerBlocks( {
: InnerBlocks.ButtonBlockAppender,
__experimentalLayout: _layout,
} );
return <TagName { ...innerBlocksProps } />;

return (
<BlockContentOverlay
clientId={ clientId }
tagName={ tagName }
wrapperProps={ innerBlocksProps }
/>
);
}
2 changes: 1 addition & 1 deletion packages/e2e-test-utils/src/inserter.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export async function insertReusableBlock( searchTerm ) {
await waitForInserterCloseAndContentFocus();
// We should wait until the block is loaded
await page.waitForXPath(
'//*[@class="block-library-block__reusable-block-container"]'
'//*[contains(@class,"block-library-block__reusable-block-container")]'
);
}

Expand Down
30 changes: 28 additions & 2 deletions packages/e2e-tests/specs/experiments/multi-entity-editing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
canvas,
openDocumentSettingsSidebar,
pressKeyWithModifier,
selectBlockByClientId,
} from '@wordpress/e2e-test-utils';

/**
Expand Down Expand Up @@ -218,7 +219,7 @@ describe( 'Multi-entity editor states', () => {
removeErrorMocks();
} );

afterEach( async () => {
const saveAndWaitResponse = async () => {
await Promise.all( [
saveAllEntities(),

Expand All @@ -241,7 +242,7 @@ describe( 'Multi-entity editor states', () => {
} ),
] );
removeErrorMocks();
} );
};

it( 'should only dirty the parent entity when editing the parent', async () => {
// Clear selection so that the block is not added to the template part.
Expand All @@ -253,9 +254,12 @@ describe( 'Multi-entity editor states', () => {
expect( await isEntityDirty( templateName ) ).toBe( true );
expect( await isEntityDirty( templatePartName ) ).toBe( false );
expect( await isEntityDirty( nestedTPName ) ).toBe( false );
await saveAndWaitResponse();
} );

it( 'should only dirty the child when editing the child', async () => {
// Select parent TP to unlock selecting content.
await canvas().click( '.wp-block-template-part' );
await canvas().click(
'.wp-block-template-part .wp-block[data-type="core/paragraph"]'
);
Expand All @@ -264,9 +268,16 @@ describe( 'Multi-entity editor states', () => {
expect( await isEntityDirty( templateName ) ).toBe( false );
expect( await isEntityDirty( templatePartName ) ).toBe( true );
expect( await isEntityDirty( nestedTPName ) ).toBe( false );
await saveAndWaitResponse();
} );

it( 'should only dirty the nested entity when editing the nested entity', async () => {
// Select parent TP to unlock selecting child.
await canvas().click( '.wp-block-template-part' );
// Select child TP to unlock selecting content.
await canvas().click(
'.wp-block-template-part .wp-block-template-part'
);
await canvas().click(
'.wp-block-template-part .wp-block-template-part .wp-block[data-type="core/paragraph"]'
);
Expand All @@ -275,6 +286,21 @@ describe( 'Multi-entity editor states', () => {
expect( await isEntityDirty( templateName ) ).toBe( false );
expect( await isEntityDirty( templatePartName ) ).toBe( false );
expect( await isEntityDirty( nestedTPName ) ).toBe( true );
await saveAndWaitResponse();
} );

it( 'should not allow selecting template part content without parent selected', async () => {
// Unselect blocks.
await selectBlockByClientId();
// Try to select a child block of a template part.
await canvas().click(
'.wp-block-template-part .wp-block-template-part .wp-block[data-type="core/paragraph"]'
);

const selectedBlock = await page.evaluate( () => {
return wp.data.select( 'core/block-editor' ).getSelectedBlock();
} );
expect( selectedBlock?.name ).toBe( 'core/template-part' );
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ describe( 'Multi-entity save flow', () => {
);
await createNewButton.click();
await page.waitForSelector( activatedTemplatePartSelector );
await page.keyboard.press( 'Tab' );
await page.keyboard.type( 'test-template-part' );
await page.click( '.block-editor-button-block-appender' );
await page.click( '.editor-block-list-item-paragraph' );
await page.keyboard.type( 'some words...' );
Expand Down Expand Up @@ -163,6 +161,9 @@ describe( 'Multi-entity save flow', () => {

// Update template part.
await page.click( templatePartSelector );
await page.click(
`${ templatePartSelector } .wp-block[data-type="core/paragraph"]`
);
await page.keyboard.type( '...some more words...' );
await page.keyboard.press( 'Enter' );

Expand Down
Loading

0 comments on commit afee31e

Please sign in to comment.