diff --git a/packages/block-editor/src/components/block-toolbar/block-toolbar-icon.js b/packages/block-editor/src/components/block-toolbar/block-toolbar-icon.js index f9b95504c312b7..0ee3192bac7810 100644 --- a/packages/block-editor/src/components/block-toolbar/block-toolbar-icon.js +++ b/packages/block-editor/src/components/block-toolbar/block-toolbar-icon.js @@ -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' @@ -49,7 +52,8 @@ function getBlockIconVariant( { select, clientIds } ) { ( id ) => !! getBlockAttributes( id )?.metadata?.patternName && ! isWithinEditedContentOnlySection( id ) && - ! isIsolatedEditor + ! isIsolatedEditor && + ! disableContentOnlyForUnsyncedPatterns ); const hasPatternOverrides = clientIds.every( ( clientId ) => hasPatternOverridesDefaultBinding( @@ -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 ]; @@ -109,7 +116,8 @@ function getBlockIcon( { select, clientIds } ) { _isSingleBlock && blockAttributes?.metadata?.patternName && ! isWithinEditedContentOnlySection( firstClientId ) && - ! isIsolatedEditor + ! isIsolatedEditor && + ! disableContentOnlyForUnsyncedPatterns ) { return symbol; } diff --git a/packages/block-editor/src/components/block-toolbar/test/block-toolbar-icon.js b/packages/block-editor/src/components/block-toolbar/test/block-toolbar-icon.js index df6fa4d07b6991..878f3355249970 100644 --- a/packages/block-editor/src/components/block-toolbar/test/block-toolbar-icon.js +++ b/packages/block-editor/src/components/block-toolbar/test/block-toolbar-icon.js @@ -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( ); + + expect( + screen.getByTestId( 'block-switcher' ) + ).toBeInTheDocument(); + expect( screen.getByTestId( 'block-icon' ) ).toHaveTextContent( + 'core/group-icon' + ); + } ); } ); describe( 'label calculation', () => { diff --git a/packages/block-editor/src/components/use-block-display-information/index.js b/packages/block-editor/src/components/use-block-display-information/index.js index b2516a2d41d243..fd83292e8fed18 100644 --- a/packages/block-editor/src/components/use-block-display-information/index.js +++ b/packages/block-editor/src/components/use-block-display-information/index.js @@ -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; @@ -101,7 +104,8 @@ export default function useBlockDisplayInformation( clientId ) { if ( patternName && ! isEditedContentOnlySection && - ! isIsolatedEditor + ! isIsolatedEditor && + ! disableContentOnlyForUnsyncedPatterns ) { const pattern = __experimentalGetParsedPattern( patternName ); const positionLabel = getPositionTypeLabel( attributes ); diff --git a/packages/block-editor/src/components/use-block-display-information/test/index.js b/packages/block-editor/src/components/use-block-display-information/test/index.js index 0f47170f7f89d5..66bce469a66cbb 100644 --- a/packages/block-editor/src/components/use-block-display-information/test/index.js +++ b/packages/block-editor/src/components/use-block-display-information/test/index.js @@ -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( ); + + expect( onChange ).toHaveBeenCalledWith( + expect.objectContaining( { + title: 'Group', + icon: groupIcon, + description: 'Gather blocks in a layout container.', + name: 'Hero pattern', + } ) + ); + } ); } ); diff --git a/packages/editor/src/components/commands/index.js b/packages/editor/src/components/commands/index.js index cd57ab6bc0ae61..a54b37fa58e3e0 100644 --- a/packages/editor/src/components/commands/index.js +++ b/packages/editor/src/components/commands/index.js @@ -54,6 +54,8 @@ const getEditorCommandLoader = () => isCodeEditingEnabled, isRichEditingEnabled, isPublishSidebarEnabled, + disableContentOnlyForUnsyncedPatterns, + disableContentOnlyForTemplateParts, } = useSelect( ( select ) => { const { get } = select( preferencesStore ); const { isListViewOpened, getCurrentPostType, getEditorSettings } = @@ -74,6 +76,11 @@ const getEditorCommandLoader = () => isRichEditingEnabled: getEditorSettings().richEditingEnabled, isPublishSidebarEnabled: select( editorStore ).isPublishSidebarEnabled(), + disableContentOnlyForUnsyncedPatterns: + !! getEditorSettings() + .disableContentOnlyForUnsyncedPatterns, + disableContentOnlyForTemplateParts: + !! getEditorSettings().disableContentOnlyForTemplateParts, }; }, [] ); const { getActiveComplementaryArea } = useSelect( interfaceStore ); @@ -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 ); @@ -98,6 +110,9 @@ const getEditorCommandLoader = () => } const commands = []; + const disableContentOnlyForPatternsAndTemplateParts = + disableContentOnlyForUnsyncedPatterns && + disableContentOnlyForTemplateParts; commands.push( { name: 'core/open-shortcut-help', @@ -176,6 +191,25 @@ const getEditorCommandLoader = () => }, } ); + commands.push( { + 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( { + disableContentOnlyForUnsyncedPatterns: disableContentOnly, + disableContentOnlyForTemplateParts: disableContentOnly, + } ); + close(); + }, + } ); + if ( allowSwitchEditorMode ) { commands.push( { name: 'core/toggle-code-editor', diff --git a/test/e2e/specs/editor/various/patterns.spec.js b/test/e2e/specs/editor/various/patterns.spec.js index e4872edafd5375..64ee9e8dc0262c 100644 --- a/test/e2e/specs/editor/various/patterns.spec.js +++ b/test/e2e/specs/editor/various/patterns.spec.js @@ -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( ` +
+

Pattern content

+
+` ); + + 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,