Skip to content

Commit

Permalink
Split content view with meta boxes (#64351)
Browse files Browse the repository at this point in the history
* Use iframe even with metaboxes

* Split content view when metaboxes are open

* Fix styling issues

Co-authored-by: t-hamano <[email protected]>

* Update e2e tests

Co-authored-by: ellatrix <[email protected]>

* Make metaboxes a details element in short viewports

* Make metabox area resizable when viewport isn’t short

* Tweak details element usage

* Add max-height when resized by user

* Hide metabox area if all metaboxes are hidden

* Persist toggle and height state in user preferences

* Wrap contents and rename things

The added wrapping element was due to Safari clipping the drop-shadow
when `overflow: auto` was on the component root.

* Make visual editor stacking context when iframed with metaboxes

* Omit meta box area when empty instead of hiding it

* Fix meta boxes content appearing in front of UI

* Use split views only when canvas is iframed

* Don’t omit main meta box area unless all locations aren’t visible

---------

Co-authored-by: t-hamano <[email protected]>
Co-authored-by: ellatrix <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: ciampo <[email protected]>
Co-authored-by: torounit <[email protected]>
Co-authored-by: talldan <[email protected]>
Co-authored-by: arnaudbroes <[email protected]>
  • Loading branch information
8 people authored Sep 13, 2024
1 parent 12660ca commit b0abc74
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 32 deletions.
128 changes: 121 additions & 7 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import {
} from '@wordpress/block-editor';
import { PluginArea } from '@wordpress/plugins';
import { __, sprintf } from '@wordpress/i18n';
import { useCallback, useMemo } from '@wordpress/element';
import {
useCallback,
useLayoutEffect,
useMemo,
useRef,
} from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { store as preferencesStore } from '@wordpress/preferences';
import {
Expand All @@ -36,8 +41,8 @@ import { privateApis as blockLibraryPrivateApis } from '@wordpress/block-library
import { addQueryArgs } from '@wordpress/url';
import { decodeEntities } from '@wordpress/html-entities';
import { store as coreStore } from '@wordpress/core-data';
import { SlotFillProvider } from '@wordpress/components';
import { useViewportMatch } from '@wordpress/compose';
import { ResizableBox, SlotFillProvider } from '@wordpress/components';
import { useMediaQuery, useViewportMatch } from '@wordpress/compose';

/**
* Internal dependencies
Expand Down Expand Up @@ -151,6 +156,118 @@ function useEditorStyles() {
] );
}

/**
* @param {Object} props
* @param {boolean} props.isLegacy True when the editor canvas is not in an iframe.
*/
function MetaBoxesMain( { isLegacy } ) {
const [ isOpen, openHeight, hasAnyVisible ] = useSelect( ( select ) => {
const { get } = select( preferencesStore );
const { isMetaBoxLocationVisible } = select( editPostStore );
return [
get( 'core/edit-post', 'metaBoxesMainIsOpen' ),
get( 'core/edit-post', 'metaBoxesMainOpenHeight' ),
isMetaBoxLocationVisible( 'normal' ) ||
isMetaBoxLocationVisible( 'advanced' ) ||
isMetaBoxLocationVisible( 'side' ),
];
}, [] );
const { set: setPreference } = useDispatch( preferencesStore );
const resizableBoxRef = useRef();
const isShort = useMediaQuery( '(max-height: 549px)' );

const isAutoHeight = openHeight === undefined;
// In case a user size is set stops the default max-height from applying.
useLayoutEffect( () => {
if ( ! isLegacy && hasAnyVisible && ! isShort && ! isAutoHeight ) {
resizableBoxRef.current.resizable.classList.add( 'has-user-size' );
}
}, [ isAutoHeight, isShort, hasAnyVisible, isLegacy ] );

if ( ! hasAnyVisible ) {
return;
}

const className = 'edit-post-meta-boxes-main';
const contents = (
<div
className={ clsx(
// The class name 'edit-post-layout__metaboxes' is retained because some plugins use it.
'edit-post-layout__metaboxes',
! isLegacy && 'edit-post-meta-boxes-main__liner'
) }
>
<MetaBoxes location="normal" />
<MetaBoxes location="advanced" />
</div>
);

if ( isLegacy ) {
return contents;
}

if ( isShort ) {
return (
<details
className={ className }
open={ isOpen }
onToggle={ ( { target } ) => {
setPreference(
'core/edit-post',
'metaBoxesMainIsOpen',
target.open
);
} }
>
<summary>{ __( 'Meta Boxes' ) }</summary>
{ contents }
</details>
);
}
return (
<ResizableBox
className={ className }
defaultSize={ { height: openHeight } }
ref={ resizableBoxRef }
enable={ {
top: true,
right: false,
bottom: false,
left: false,
topLeft: false,
topRight: false,
bottomRight: false,
bottomLeft: false,
} }
// This is overriden by an !important rule that applies until user resizes.
maxHeight="100%"
bounds="parent"
boundsByDirection
// Avoids hiccups while dragging over objects like iframes and ensures that
// the event to end the drag is captured by the target (resize handle)
// whether or not it’s under the pointer.
onPointerDown={ ( { pointerId, target } ) => {
target.setPointerCapture( pointerId );
} }
onResizeStart={ ( event, direction, elementRef ) => {
// Avoids height jumping in case it’s limited by max-height.
elementRef.style.height = `${ elementRef.offsetHeight }px`;
// Stops initial max-height from being applied.
elementRef.classList.add( 'has-user-size' );
} }
onResizeStop={ () => {
setPreference(
'core/edit-post',
'metaBoxesMainOpenHeight',
resizableBoxRef.current.state.height
);
} }
>
{ contents }
</ResizableBox>
);
}

function Layout( {
postId: initialPostId,
postType: initialPostType,
Expand Down Expand Up @@ -355,10 +472,7 @@ function Layout( {
extraContent={
! isDistractionFree &&
showMetaBoxes && (
<div className="edit-post-layout__metaboxes">
<MetaBoxes location="normal" />
<MetaBoxes location="advanced" />
</div>
<MetaBoxesMain isLegacy={ ! shouldIframe } />
)
}
>
Expand Down
70 changes: 67 additions & 3 deletions packages/edit-post/src/components/layout/style.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,70 @@
.edit-post-layout__metaboxes {
flex-shrink: 0;
clear: both;
.edit-post-meta-boxes-main {
filter: drop-shadow(0 -1px rgba($color: #000, $alpha: 0.133)); // 0.133 = $gray-200 but with alpha.
background-color: $white;
clear: both; // This is seemingly only needed in case the canvas is not iframe’d.

&:not(details) {
padding-top: 23px;
max-height: 100%;

&:not(.has-user-size) {
max-height: 50% !important;
}
}

// The component renders as a details element in short viewports.
&:is(details) {
& > summary {
cursor: pointer;
color: $gray-900;
background-color: $white;
height: $button-size-compact;
line-height: $button-size-compact;
font-size: 13px;
padding-left: $grid-unit-30;
box-shadow: 0 $border-width $gray-300;
}

&[open] > summary {
position: sticky;
top: 0;
z-index: 1;
}
}

& .components-resizable-box__handle-top {
top: 0;
box-shadow: 0 $border-width $gray-300;
}
& .components-resizable-box__side-handle::before {
border-radius: 0;
top: 0;
height: $border-width;
}
& .components-resizable-box__handle::after {
background-color: $gray-300;
box-shadow: none;
border-radius: 4px;
height: $grid-unit-05;
top: calc(50% - #{$grid-unit-05} / 2);
width: 100px;
right: calc(50% - 50px);
}
}

.edit-post-meta-boxes-main__liner {
overflow: auto;
max-height: 100%;
// Keep the contents behind the resize handle or details summary.
isolation: isolate;
}

.has-metaboxes .editor-visual-editor {
flex: 1;

&.is-iframed {
isolation: isolate;
}
}

// Adjust the position of the notices
Expand Down
11 changes: 2 additions & 9 deletions packages/edit-post/src/components/layout/use-should-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ import { useSelect } from '@wordpress/data';
import { store as blocksStore } from '@wordpress/blocks';
import { store as blockEditorStore } from '@wordpress/block-editor';

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

const isGutenbergPlugin = globalThis.IS_GUTENBERG_PLUGIN ? true : false;

export function useShouldIframe() {
const {
isBlockBasedTheme,
hasV3BlocksOnly,
isEditingTemplate,
hasMetaBoxes,
isZoomOutMode,
} = useSelect( ( select ) => {
const { getEditorSettings, getCurrentPostType } = select( editorStore );
Expand All @@ -31,14 +25,13 @@ export function useShouldIframe() {
return type.apiVersion >= 3;
} ),
isEditingTemplate: getCurrentPostType() === 'wp_template',
hasMetaBoxes: select( editPostStore ).hasMetaBoxes(),
isZoomOutMode: __unstableGetEditorMode() === 'zoom-out',
};
}, [] );

return (
( ( hasV3BlocksOnly || ( isGutenbergPlugin && isBlockBasedTheme ) ) &&
! hasMetaBoxes ) ||
hasV3BlocksOnly ||
( isGutenbergPlugin && isBlockBasedTheme ) ||
isEditingTemplate ||
isZoomOutMode
);
Expand Down
1 change: 0 additions & 1 deletion packages/edit-post/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ export const isMetaBoxLocationVisible = createRegistrySelector(
isMetaBoxLocationActive( state, location ) &&
getMetaBoxesPerLocation( state, location )?.some( ( { id } ) => {
return select( editorStore ).isEditorPanelEnabled(
state,
`meta-box-${ id }`
);
} )
Expand Down
18 changes: 11 additions & 7 deletions test/e2e/specs/editor/plugins/meta-boxes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ test.describe( 'Meta boxes', () => {
await expect( saveDraft ).toBeDisabled();

// Add title to enable valid non-empty post save.
await page
await editor.canvas
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'Hello Meta' );

Expand All @@ -44,7 +44,7 @@ test.describe( 'Meta boxes', () => {
page,
} ) => {
// Publish a post so there's something for the latest posts dynamic block to render.
await page
await editor.canvas
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'A published post' );
await page.keyboard.press( 'Enter' );
Expand All @@ -53,7 +53,7 @@ test.describe( 'Meta boxes', () => {

// Publish a post with the latest posts dynamic block.
await admin.createNewPost();
await page
await editor.canvas
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'Dynamic block test' );
await editor.insertBlock( { name: 'core/latest-posts' } );
Expand All @@ -70,10 +70,12 @@ test.describe( 'Meta boxes', () => {
editor,
page,
} ) => {
await page
await editor.canvas
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'A published post' );
await page.getByRole( 'button', { name: 'Add default block' } ).click();
await editor.canvas
.getByRole( 'button', { name: 'Add default block' } )
.click();
await page.keyboard.type( 'Excerpt from content.' );

const postId = await editor.publishPost();
Expand All @@ -89,9 +91,11 @@ test.describe( 'Meta boxes', () => {
page,
} ) => {
await editor.openDocumentSettingsSidebar();
await page.getByRole( 'button', { name: 'Add default block' } ).click();
await editor.canvas
.getByRole( 'button', { name: 'Add default block' } )
.click();
await page.keyboard.type( 'Excerpt from content.' );
await page
await editor.canvas
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'A published post' );

Expand Down
2 changes: 1 addition & 1 deletion test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test.describe( 'WP Editor Meta Boxes', () => {
await admin.createNewPost();

// Add title to enable valid non-empty post save.
await page
await editor.canvas
.locator( 'role=textbox[name="Add title"i]' )
.type( 'Hello Meta' );

Expand Down
7 changes: 3 additions & 4 deletions test/e2e/specs/editor/various/publish-button.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,12 @@ test.describe( 'Post publish button', () => {
admin,
page,
requestUtils,
editor,
} ) => {
await requestUtils.activatePlugin( 'gutenberg-test-plugin-meta-box' );
await admin.createNewPost();
await page
.getByRole( 'textbox', {
name: 'Add title',
} )
await editor.canvas
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'Test post' );

const topBar = page.getByRole( 'region', { name: 'Editor top bar' } );
Expand Down

0 comments on commit b0abc74

Please sign in to comment.