Skip to content
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 @@ -34,7 +34,10 @@ function getBlockIconVariant( { select, clientIds } ) {
getSettings,
} = unlock( select( blockEditorStore ) );
const { getBlockStyles } = select( blocksStore );
const isIsolatedEditor = !! getSettings()?.[ isIsolatedEditorKey ];
const settings = getSettings();
const isIsolatedEditor = !! settings?.[ isIsolatedEditorKey ];
const disableContentOnlyForUnsyncedPatterns =
!! settings?.disableContentOnlyForUnsyncedPatterns;

const hasTemplateLock = clientIds.some(
( id ) => getTemplateLock( id ) === 'contentOnly'
Expand All @@ -49,7 +52,8 @@ function getBlockIconVariant( { select, clientIds } ) {
( id ) =>
!! getBlockAttributes( id )?.metadata?.patternName &&
! isWithinEditedContentOnlySection( id ) &&
! isIsolatedEditor
! isIsolatedEditor &&
! disableContentOnlyForUnsyncedPatterns
);
const hasPatternOverrides = clientIds.every( ( clientId ) =>
hasPatternOverridesDefaultBinding(
Expand Down Expand Up @@ -99,7 +103,10 @@ function getBlockIcon( { select, clientIds } ) {
isWithinEditedContentOnlySection,
getSettings,
} = unlock( select( blockEditorStore ) );
const isIsolatedEditor = !! getSettings()?.[ isIsolatedEditorKey ];
const settings = getSettings();
const isIsolatedEditor = !! settings?.[ isIsolatedEditorKey ];
const disableContentOnlyForUnsyncedPatterns =
!! settings?.disableContentOnlyForUnsyncedPatterns;

const _isSingleBlock = clientIds.length === 1;
const firstClientId = clientIds[ 0 ];
Expand All @@ -109,7 +116,8 @@ function getBlockIcon( { select, clientIds } ) {
_isSingleBlock &&
blockAttributes?.metadata?.patternName &&
! isWithinEditedContentOnlySection( firstClientId ) &&
! isIsolatedEditor
! isIsolatedEditor &&
! disableContentOnlyForUnsyncedPatterns

@talldan talldan Jun 25, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if it should be checking isSectionBlock instead, as that should already encapsulate many of these conditions:

const isIsolatedEditor = state.settings?.[ isIsolatedEditorKey ];
const disableContentOnlyForUnsyncedPatterns =
state.settings?.disableContentOnlyForUnsyncedPatterns;
const disableContentOnlyForTemplateParts =
state.settings?.disableContentOnlyForTemplateParts;

Same applies to the code above on line 55-56.

Would need to make sure this doesn't apply the symbol to synced patterns and template parts too, though there's already a blockAttributes?.metadata?.patternName check, maybe that's enough.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point! I did keep a local diff to do exactly that (isSectionBlock originally suggested to me by LLM), but kept the changes narrow to only deal with the toggle.

I can do a follow up to this PR to change it out and test for side effects.

) {
return symbol;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,23 @@ describe( 'BlockToolbarIcon', () => {
'core/group-icon'
);
} );

it( 'should show the block switcher when content-only editing is disabled', () => {
setupToolbarSelectors( {
settings: {
disableContentOnlyForUnsyncedPatterns: true,
},
} );

render( <BlockToolbarIcon { ...defaultProps } /> );

expect(
screen.getByTestId( 'block-switcher' )
).toBeInTheDocument();
expect( screen.getByTestId( 'block-icon' ) ).toHaveTextContent(
'core/group-icon'
);
} );
} );

describe( 'label calculation', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ export default function useBlockDisplayInformation( clientId ) {
const attributes = getBlockAttributes( clientId );
const { isWithinEditedContentOnlySection } =
unlock( blockEditorSelect );
const isIsolatedEditor = !! getSettings()?.[ isIsolatedEditorKey ];
const settings = getSettings();
const isIsolatedEditor = !! settings?.[ isIsolatedEditorKey ];
const disableContentOnlyForUnsyncedPatterns =
!! settings?.disableContentOnlyForUnsyncedPatterns;

// Check if this block is a pattern
const patternName = attributes?.metadata?.patternName;
Expand All @@ -101,7 +104,8 @@ export default function useBlockDisplayInformation( clientId ) {
if (
patternName &&
! isEditedContentOnlySection &&
! isIsolatedEditor
! isIsolatedEditor &&
! disableContentOnlyForUnsyncedPatterns
) {
const pattern = __experimentalGetParsedPattern( patternName );
const positionLabel = getPositionTypeLabel( attributes );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,24 @@ describe( 'useBlockDisplayInformation', () => {
} )
);
} );

it( 'displays block information for a pattern wrapper when content-only editing is disabled', () => {
setupUseSelect( {
settings: {
disableContentOnlyForUnsyncedPatterns: true,
},
} );
const onChange = jest.fn();

render( <TestComponent onChange={ onChange } /> );

expect( onChange ).toHaveBeenCalledWith(
expect.objectContaining( {
title: 'Group',
icon: groupIcon,
description: 'Gather blocks in a layout container.',
name: 'Hero pattern',
} )
);
} );
} );
34 changes: 34 additions & 0 deletions packages/editor/src/components/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const getEditorCommandLoader = () =>
isCodeEditingEnabled,
isRichEditingEnabled,
isPublishSidebarEnabled,
disableContentOnlyForUnsyncedPatterns,
disableContentOnlyForTemplateParts,
} = useSelect( ( select ) => {
const { get } = select( preferencesStore );
const { isListViewOpened, getCurrentPostType, getEditorSettings } =
Expand All @@ -74,6 +76,11 @@ const getEditorCommandLoader = () =>
isRichEditingEnabled: getEditorSettings().richEditingEnabled,
isPublishSidebarEnabled:
select( editorStore ).isPublishSidebarEnabled(),
disableContentOnlyForUnsyncedPatterns:
!! getEditorSettings()
.disableContentOnlyForUnsyncedPatterns,
disableContentOnlyForTemplateParts:
!! getEditorSettings().disableContentOnlyForTemplateParts,
};
}, [] );
const { getActiveComplementaryArea } = useSelect( interfaceStore );
Expand All @@ -86,7 +93,12 @@ const getEditorCommandLoader = () =>
toggleDistractionFree,
toggleSpotlightMode,
toggleTopToolbar,
updateEditorSettings,
} = useDispatch( editorStore );
// eslint-disable-next-line @wordpress/no-unused-vars-before-return
const { stopEditingContentOnlySection } = unlock(
useDispatch( blockEditorStore )
);
const { openModal, enableComplementaryArea, disableComplementaryArea } =
useDispatch( interfaceStore );
const { getCurrentPostId } = useSelect( editorStore );
Expand All @@ -98,6 +110,9 @@ const getEditorCommandLoader = () =>
}

const commands = [];
const disableContentOnlyForPatternsAndTemplateParts =
disableContentOnlyForUnsyncedPatterns &&
disableContentOnlyForTemplateParts;

commands.push( {
name: 'core/open-shortcut-help',
Expand Down Expand Up @@ -176,6 +191,25 @@ const getEditorCommandLoader = () =>
},
} );

commands.push( {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Look into pushing it into the default commands when a pattern is selected

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I tested adding this to the command suggestions on pattern/template-part selection. The suggestions list is already very long and this just gets lost at the bottom.

Kapture.2026-06-24.at.15.03.47.mp4

I'm thinking it should be a follow up anyway.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It'd be nice to make it contextual, but I don't think it's a blocker. If anyone uses it regularly then it appears in 'Recent'.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'll make a note to follow up with this.

name: 'core/toggle-pattern-editing',
label: disableContentOnlyForPatternsAndTemplateParts
? __( 'Disable editing all patterns' )
: __( 'Enable editing all patterns' ),
icon: symbol,
category: 'command',
callback: ( { close } ) => {
const disableContentOnly =
! disableContentOnlyForPatternsAndTemplateParts;
stopEditingContentOnlySection();
updateEditorSettings( {
Comment thread
ramonjd marked this conversation as resolved.
disableContentOnlyForUnsyncedPatterns: disableContentOnly,
disableContentOnlyForTemplateParts: disableContentOnly,
} );
close();
},
} );

if ( allowSwitchEditorMode ) {
commands.push( {
name: 'core/toggle-code-editor',
Expand Down
52 changes: 52 additions & 0 deletions test/e2e/specs/editor/various/patterns.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,58 @@ test.describe( 'Unsynced pattern', () => {
await expect( blockHeading ).toHaveAccessibleName( 'Header Group' );
} );

test( 'shows the source block in the inspector when full pattern editing is enabled', async ( {
editor,
page,
pageUtils,
} ) => {
await editor.setContent( `<!-- wp:group {"metadata":{"patternName":"theme/header-wrapper","name":"Header"},"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph -->
<p>Pattern content</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->` );

await pageUtils.pressKeys( 'access+o' );
const listView = page.getByRole( 'treegrid', {
name: 'Block navigation structure',
} );
await listView
.getByRole( 'gridcell', { name: 'Header', exact: true } )
.click();

await editor.openDocumentSettingsSidebar();
const editorSettings = page.getByRole( 'region', {
name: 'Editor settings',
} );
const blockHeading = editorSettings
.getByRole( 'tabpanel', { name: 'Block' } )
.getByRole( 'heading' )
.first();

await expect( blockHeading ).toHaveAccessibleName( 'Header Pattern' );

await editorSettings
.getByRole( 'button', { name: 'Edit pattern' } )
.click();
await expect( blockHeading ).toHaveAccessibleName( 'Header Group' );
await expect(
editorSettings.getByRole( 'button', { name: 'Exit pattern' } )
).toBeVisible();

await pageUtils.pressKeys( 'primary+k' );
await page
.getByRole( 'combobox', { name: 'Search commands and settings' } )
.fill( 'editing all patterns' );
await page
.getByRole( 'option', { name: 'Enable editing all patterns' } )
.click();

await expect(
editorSettings.getByRole( 'button', { name: 'Exit pattern' } )
).toBeHidden();
await expect( blockHeading ).toHaveAccessibleName( 'Header Group' );
} );

test( 'detaches an unsynced pattern via the block options menu', async ( {
editor,
page,
Expand Down
Loading