Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Patterns: Add edit pattern to the list view instead of Ungroup
  • Loading branch information
scruffian authored and ramonjd committed Nov 14, 2025
commit cf6f17246085ada883df203b4e60643febe89c98
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,21 @@
* WordPress dependencies
*/
import { Button, __experimentalVStack as VStack } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import useContentOnlySectionEdit from '../../hooks/use-content-only-section-edit';

export default function EditContents( { clientId } ) {
const { editContentOnlySection, stopEditingContentOnlySection } = unlock(
useDispatch( blockEditorStore )
);
const { isWithinSection, isWithinEditedSection, editedContentOnlySection } =
useSelect(
( select ) => {
const {
isSectionBlock,
getParentSectionBlock,
getEditedContentOnlySection,
isWithinEditedContentOnlySection,
} = unlock( select( blockEditorStore ) );

return {
isWithinSection:
isSectionBlock( clientId ) ||
!! getParentSectionBlock( clientId ),
isWithinEditedSection:
isWithinEditedContentOnlySection( clientId ),
editedContentOnlySection: getEditedContentOnlySection(),
};
},
[ clientId ]
);
const {
isWithinSection,
isWithinEditedSection,
editedContentOnlySection,
editContentOnlySection,
stopEditingContentOnlySection,
} = useContentOnlySectionEdit( clientId );

if ( ! isWithinSection && ! isWithinEditedSection ) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* WordPress dependencies
*/
import { MenuItem } from '@wordpress/components';
import { _x } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import useContentOnlySectionEdit from '../../hooks/use-content-only-section-edit';

export function EditPatternMenuItem( { clientId, onClose } ) {
const {
isSectionBlock,
isEditingContentOnlySection,
editContentOnlySection,
} = useContentOnlySectionEdit( clientId );

// Only show when the experiment is enabled, the block is a section block,
// and we're not already editing it
if (
! window?.__experimentalContentOnlyPatternInsertion ||
! isSectionBlock ||
isEditingContentOnlySection
) {
return null;
}

return (
<MenuItem
onClick={ () => {
editContentOnlySection( clientId );
onClose();
} }
>
{ _x( 'Edit pattern', 'Editing a pattern block in the Editor' ) }
Copy link
Member

Choose a reason for hiding this comment

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

I like the updated copy.

@andrewserong raised an interesting point - are we referring to everything as patterns? E.g., template parts and templateLock: 'contentOnly'?

I'm fine with that by the way. 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking that for new users who may not know what a pattern is, it might be more clear to say "edit section", plus that includes patterns, template parts etc..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to edit section...

</MenuItem>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import BlockModeToggle from '../block-settings-menu/block-mode-toggle';
import { ModifyContentOnlySectionMenuItem } from '../content-lock';
import { BlockRenameControl, useBlockRename } from '../block-rename';
import { BlockVisibilityMenuItem } from '../block-visibility';
import { EditPatternMenuItem } from './edit-pattern-menu-item';
import { unlock } from '../../lock-unlock';

const { Fill, Slot } = createSlotFill( 'BlockSettingsMenuControls' );

Expand All @@ -31,6 +33,7 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
selectedClientIds,
isContentOnly,
canToggleSelectedBlocksVisibility,
isSectionBlock,
} = useSelect(
( select ) => {
const {
Expand All @@ -39,6 +42,9 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
getSelectedBlockClientIds,
getBlockEditingMode,
} = select( blockEditorStore );
const { isSectionBlock: _isSectionBlock } = unlock(
select( blockEditorStore )
);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: You can access private and non-private selectors for same select.

const ids =
clientIds !== null ? clientIds : getSelectedBlockClientIds();
return {
Expand All @@ -51,6 +57,8 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
).every( ( block ) =>
hasBlockSupport( block.name, 'blockVisibility', true )
),
isSectionBlock:
ids.length === 1 ? _isSectionBlock( ids[ 0 ] ) : false,
};
},
[ clientIds ]
Expand All @@ -70,8 +78,17 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
const convertToGroupButtonProps =
useConvertToGroupButtonProps( selectedClientIds );
const { isGroupable, isUngroupable } = convertToGroupButtonProps;

// Don't show ungroup for section blocks when the experiment is enabled
// since we show "Unlock design" instead
const shouldShowUngroup =
isUngroupable &&
! (
isSectionBlock && window?.__experimentalContentOnlyPatternInsertion
);

Copy link
Contributor

Choose a reason for hiding this comment

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

Once we merge #73183 we won't need this. It's simpler to ensure that section blocks are never ungroupable, because there aren't any circumstances under which we'd want to ungroup them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this all got removed in the rebase.

const showConvertToGroupButton =
( isGroupable || isUngroupable ) && ! isContentOnly;
( isGroupable || shouldShowUngroup ) && ! isContentOnly;

return (
<Slot
Expand All @@ -95,6 +112,13 @@ const BlockSettingsMenuControlsSlot = ( { fillProps, clientIds = null } ) => {
{ showConvertToGroupButton && (
<ConvertToGroupButton
{ ...convertToGroupButtonProps }
isUngroupable={ shouldShowUngroup }
onClose={ fillProps?.onClose }
/>
) }
{ selectedClientIds.length === 1 && (
<EditPatternMenuItem
clientId={ selectedClientIds[ 0 ] }
onClose={ fillProps?.onClose }
/>
) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';

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

/**
* Hook that provides section block editing state and actions.
*
* @param {string} clientId Block client ID.
* @return {Object} Object containing section block state and actions.
*/
export default function useContentOnlySectionEdit( clientId ) {
const {
isSectionBlock,
isWithinSection,
isWithinEditedSection,
isEditingContentOnlySection,
editedContentOnlySection,
} = useSelect(
( select ) => {
const {
isSectionBlock: _isSectionBlock,
getParentSectionBlock,
getEditedContentOnlySection,
isWithinEditedContentOnlySection,
} = unlock( select( blockEditorStore ) );

const editedSection = getEditedContentOnlySection();

return {
isSectionBlock: _isSectionBlock( clientId ),
isWithinSection:
_isSectionBlock( clientId ) ||
!! getParentSectionBlock( clientId ),
isWithinEditedSection:
isWithinEditedContentOnlySection( clientId ),
isEditingContentOnlySection: editedSection === clientId,
editedContentOnlySection: editedSection,
};
},
[ clientId ]
);

const blockEditorActions = useDispatch( blockEditorStore );
const { editContentOnlySection, stopEditingContentOnlySection } =
unlock( blockEditorActions );

return {
isSectionBlock,
isWithinSection,
isWithinEditedSection,
isEditingContentOnlySection,
editedContentOnlySection,
editContentOnlySection,
stopEditingContentOnlySection,
};
}