Skip to content

Commit

Permalink
Allow selecting template parts in page content focus mode (#60010)
Browse files Browse the repository at this point in the history
* Allow selecting template parts in page content focus mode

* Show block name/icon in toolbar when editing mode is contentOnly

* Only show page content blocks in PageContent sidebar section

* Fix pages.spec.js

* Improve clarify of if()

* Move template part Edit button from @wordpress/edit-site to @wordpress/editor

* Rendering mode should be 'all' in post editor when editing a template or template part

* Revert "Rendering mode should be 'all' in post editor when editing a template or template part"

This reverts commit 348889e.

* Reset and remember rendering mode when navigating in post editor

* Remove unsupported rendering mode from docs

* Add 'Edit' button to Template Part block natively

* Disable appending into a template part

* Always show purple outline on template parts

Co-authored-by: noisysocks <[email protected]>
Co-authored-by: SaxonF <[email protected]>
Co-authored-by: andrewserong <[email protected]>
Co-authored-by: richtabor <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: jasmussen <[email protected]>
Co-authored-by: jameskoster <[email protected]>
Co-authored-by: ramonjd <[email protected]>
Co-authored-by: bacoords <[email protected]>
  • Loading branch information
10 people authored Mar 22, 2024
1 parent 4f239fd commit e7ad4b5
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 213 deletions.
1 change: 0 additions & 1 deletion docs/reference-guides/data/data-core-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1415,7 +1415,6 @@ _Returns_

Returns an action used to set the rendering mode of the post editor. We support multiple rendering modes:

- `all`: This is the default mode. It renders the post editor with all the features available. If a template is provided, it's preferred over the post.
- `post-only`: This mode extracts the post blocks from the template and renders only those. The idea is to allow the user to edit the post/page in isolation without the wrapping template.
- `template-locked`: This mode renders both the template and the post blocks but the template blocks are locked and can't be edited. The post blocks are editable.

Expand Down
4 changes: 2 additions & 2 deletions packages/block-editor/src/components/block-switcher/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function BlockSwitcherDropdownMenuContents( {
);
}

export const BlockSwitcher = ( { clientIds } ) => {
export const BlockSwitcher = ( { clientIds, disabled } ) => {
const {
canRemove,
hasBlockStyles,
Expand Down Expand Up @@ -229,8 +229,8 @@ export const BlockSwitcher = ( { clientIds } ) => {
const blockSwitcherLabel = isSingleBlock
? blockTitle
: __( 'Multiple blocks selected' );
const hideDropdown = ! hasBlockStyles && ! canRemove;

const hideDropdown = disabled || ( ! hasBlockStyles && ! canRemove );
if ( hideDropdown ) {
return (
<ToolbarGroup>
Expand Down
41 changes: 22 additions & 19 deletions packages/block-editor/src/components/block-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,26 +170,29 @@ export function PrivateBlockToolbar( {
{ isUsingBindings && canBindBlock( blockName ) && (
<BlockBindingsIndicator />
) }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
isDefaultEditingMode && (
<div
ref={ nodeRef }
{ ...showHoveredOrFocusedGestures }
>
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
<BlockSwitcher clientIds={ blockClientIds } />
{ ! isMultiToolbar && (
<BlockLockToolbar
clientId={ blockClientId }
{ ( shouldShowVisualToolbar || isMultiToolbar ) && (
<div ref={ nodeRef } { ...showHoveredOrFocusedGestures }>
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
<BlockSwitcher
clientIds={ blockClientIds }
disabled={ ! isDefaultEditingMode }
/>
{ isDefaultEditingMode && (
<>
{ ! isMultiToolbar && (
<BlockLockToolbar
clientId={ blockClientId }
/>
) }
<BlockMover
clientIds={ blockClientIds }
hideDragHandle={ hideDragHandle }
/>
) }
<BlockMover
clientIds={ blockClientIds }
hideDragHandle={ hideDragHandle }
/>
</ToolbarGroup>
</div>
) }
</>
) }
</ToolbarGroup>
</div>
) }
<Shuffle clientId={ blockClientId } />
{ shouldShowVisualToolbar && isMultiToolbar && (
<BlockGroupToolbar />
Expand Down
35 changes: 32 additions & 3 deletions packages/block-library/src/template-part/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ import {
useHasRecursion,
InspectorControls,
__experimentalBlockPatternsList as BlockPatternsList,
BlockControls,
} from '@wordpress/block-editor';
import { PanelBody, Spinner, Modal, MenuItem } from '@wordpress/components';
import {
PanelBody,
Spinner,
Modal,
MenuItem,
ToolbarButton,
} from '@wordpress/components';
import { useAsyncList } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
import { store as coreStore } from '@wordpress/core-data';
Expand Down Expand Up @@ -104,11 +111,17 @@ export default function TemplatePartEdit( {
const [ isTemplatePartSelectionOpen, setIsTemplatePartSelectionOpen ] =
useState( false );

const { isResolved, hasInnerBlocks, isMissing, area } = useSelect(
const {
isResolved,
hasInnerBlocks,
isMissing,
area,
onNavigateToEntityRecord,
} = useSelect(
( select ) => {
const { getEditedEntityRecord, hasFinishedResolution } =
select( coreStore );
const { getBlockCount } = select( blockEditorStore );
const { getBlockCount, getSettings } = select( blockEditorStore );

const getEntityArgs = [
'postType',
Expand All @@ -134,6 +147,8 @@ export default function TemplatePartEdit( {
( ! entityRecord ||
Object.keys( entityRecord ).length === 0 ),
area: _area,
onNavigateToEntityRecord:
getSettings().onNavigateToEntityRecord,
};
},
[ templatePartId, attributes.area, clientId ]
Expand Down Expand Up @@ -217,6 +232,20 @@ export default function TemplatePartEdit( {
return (
<>
<RecursionProvider uniqueId={ templatePartId }>
{ isEntityAvailable && onNavigateToEntityRecord && (
<BlockControls group="other">
<ToolbarButton
onClick={ () =>
onNavigateToEntityRecord( {
postId: templatePartId,
postType: 'wp_template_part',
} )
}
>
{ __( 'Edit' ) }
</ToolbarButton>
</BlockControls>
) }
<InspectorControls group="advanced">
<TemplatePartAdvancedControls
tagName={ tagName }
Expand Down
39 changes: 28 additions & 11 deletions packages/block-library/src/template-part/edit/inner-blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,42 @@ import {
useInnerBlocksProps,
useSettings,
store as blockEditorStore,
useBlockEditingMode,
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';

function useRenderAppender( hasInnerBlocks ) {
const blockEditingMode = useBlockEditingMode();
// Disable appending when the editing mode is 'contentOnly'. This is so that the user can't
// append into a template part when editing a page in the site editor. See
// DisableNonPageContentBlocks. Ideally instead of (mis)using editing mode there would be a
// block editor API for achieving this.
if ( blockEditingMode === 'contentOnly' ) {
return false;
}
if ( ! hasInnerBlocks ) {
return InnerBlocks.ButtonBlockAppender;
}
}

function useLayout( layout ) {
const themeSupportsLayout = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
return getSettings()?.supportsLayout;
}, [] );
const [ defaultLayout ] = useSettings( 'layout' );
if ( themeSupportsLayout ) {
return layout?.inherit ? defaultLayout || {} : layout;
}
}

export default function TemplatePartInnerBlocks( {
postId: id,
hasInnerBlocks,
layout,
tagName: TagName,
blockProps,
} ) {
const themeSupportsLayout = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
return getSettings()?.supportsLayout;
}, [] );
const [ defaultLayout ] = useSettings( 'layout' );
const usedLayout = layout?.inherit ? defaultLayout || {} : layout;

const [ blocks, onInput, onChange ] = useEntityBlockEditor(
'postType',
'wp_template_part',
Expand All @@ -34,10 +53,8 @@ export default function TemplatePartInnerBlocks( {
value: blocks,
onInput,
onChange,
renderAppender: hasInnerBlocks
? undefined
: InnerBlocks.ButtonBlockAppender,
layout: themeSupportsLayout ? usedLayout : undefined,
renderAppender: useRenderAppender( hasInnerBlocks ),
layout: useLayout( layout ),
} );

return <TagName { ...innerBlocksProps } />;
Expand Down
14 changes: 11 additions & 3 deletions packages/block-library/src/template-part/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@

.is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part,
.is-outline-mode .block-editor-block-list__block:not(.remove-outline).is-reusable {
&.is-highlighted,
&.is-selected {
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-block-synced-color);
&.is-highlighted::after,
&.is-selected::after {
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-block-synced-color);
}

&.is-hovered::after {
box-shadow: 0 0 0 $border-width var(--wp-block-synced-color);
}

&.block-editor-block-list__block:not([contenteditable]):focus {
Expand All @@ -40,3 +44,7 @@
}
}
}

.is-outline-mode .block-editor-block-list__block:not(.remove-outline).wp-block-template-part.has-editable-outline::after {
border: none;
}
14 changes: 12 additions & 2 deletions packages/edit-post/src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ function Editor( {
...props
} ) {
const {
initialPost,
currentPost,
onNavigateToEntityRecord,
onNavigateToPreviousEntityRecord,
} = useNavigateToEntityRecord( initialPostId, initialPostType );
} = useNavigateToEntityRecord(
initialPostId,
initialPostType,
'post-only'
);

const { post, template } = useSelect(
( select ) => {
Expand Down Expand Up @@ -80,6 +83,13 @@ function Editor( {
[ settings, onNavigateToEntityRecord, onNavigateToPreviousEntityRecord ]
);

const initialPost = useMemo( () => {
return {
type: initialPostType,
id: initialPostId,
};
}, [ initialPostType, initialPostId ] );

if ( ! post ) {
return null;
}
Expand Down
61 changes: 37 additions & 24 deletions packages/edit-post/src/hooks/use-navigate-to-entity-record.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/**
* WordPress dependencies
*/
import { useCallback, useReducer, useMemo } from '@wordpress/element';
import { useCallback, useReducer } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';

/**
* A hook that records the 'entity' history in the post editor as a user
Expand All @@ -11,20 +13,22 @@ import { useCallback, useReducer, useMemo } from '@wordpress/element';
*
* Used to control displaying UI elements like the back button.
*
* @param {number} initialPostId The post id of the post when the editor loaded.
* @param {string} initialPostType The post type of the post when the editor loaded.
* @param {number} initialPostId The post id of the post when the editor loaded.
* @param {string} initialPostType The post type of the post when the editor loaded.
* @param {string} defaultRenderingMode The rendering mode to switch to when navigating.
*
* @return {Object} An object containing the `currentPost` variable and
* `onNavigateToEntityRecord` and `onNavigateToPreviousEntityRecord` functions.
*/
export default function useNavigateToEntityRecord(
initialPostId,
initialPostType
initialPostType,
defaultRenderingMode
) {
const [ postHistory, dispatch ] = useReducer(
( historyState, { type, post } ) => {
( historyState, { type, post, previousRenderingMode } ) => {
if ( type === 'push' ) {
return [ ...historyState, post ];
return [ ...historyState, { post, previousRenderingMode } ];
}
if ( type === 'pop' ) {
// Try to leave one item in the history.
Expand All @@ -34,32 +38,41 @@ export default function useNavigateToEntityRecord(
}
return historyState;
},
[ { postId: initialPostId, postType: initialPostType } ]
[
{
post: { postId: initialPostId, postType: initialPostType },
},
]
);

const initialPost = useMemo( () => {
return {
type: initialPostType,
id: initialPostId,
};
}, [ initialPostType, initialPostId ] );
const { post, previousRenderingMode } =
postHistory[ postHistory.length - 1 ];

const onNavigateToEntityRecord = useCallback( ( params ) => {
dispatch( {
type: 'push',
post: { postId: params.postId, postType: params.postType },
} );
}, [] );
const { getRenderingMode } = useSelect( editorStore );
const { setRenderingMode } = useDispatch( editorStore );

const onNavigateToEntityRecord = useCallback(
( params ) => {
dispatch( {
type: 'push',
post: { postId: params.postId, postType: params.postType },
// Save the current rendering mode so we can restore it when navigating back.
previousRenderingMode: getRenderingMode(),
} );
setRenderingMode( defaultRenderingMode );
},
[ getRenderingMode, setRenderingMode, defaultRenderingMode ]
);

const onNavigateToPreviousEntityRecord = useCallback( () => {
dispatch( { type: 'pop' } );
}, [] );

const currentPost = postHistory[ postHistory.length - 1 ];
if ( previousRenderingMode ) {
setRenderingMode( previousRenderingMode );
}
}, [ setRenderingMode, previousRenderingMode ] );

return {
currentPost,
initialPost,
currentPost: post,
onNavigateToEntityRecord,
onNavigateToPreviousEntityRecord:
postHistory.length > 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
store as blockEditorStore,
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -15,15 +14,16 @@ import { unlock } from '../../../lock-unlock';

const { BlockQuickNavigation } = unlock( blockEditorPrivateApis );

const PAGE_CONTENT_BLOCKS = [
'core/post-content',
'core/post-featured-image',
'core/post-title',
];

export default function PageContent() {
const clientIdsTree = useSelect(
( select ) =>
unlock( select( blockEditorStore ) ).getEnabledClientIdsTree(),
[]
);
const clientIds = useMemo(
() => clientIdsTree.map( ( { clientId } ) => clientId ),
[ clientIdsTree ]
);
const clientIds = useSelect( ( select ) => {
const { getBlocksByName } = select( blockEditorStore );
return getBlocksByName( PAGE_CONTENT_BLOCKS );
}, [] );
return <BlockQuickNavigation clientIds={ clientIds } />;
}
1 change: 0 additions & 1 deletion packages/edit-site/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@
*/
import './components';
import './push-changes-to-global-styles';
import './template-part-edit';
Loading

1 comment on commit e7ad4b5

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in e7ad4b5.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/8385588109
📝 Reported issues:

Please sign in to comment.