Skip to content
3 changes: 3 additions & 0 deletions packages/block-editor/src/components/block-edit/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { createContext, useContext } from '@wordpress/element';

export const mayDisplayControlsKey = Symbol( 'mayDisplayControls' );
export const mayDisplayParentControlsKey = Symbol( 'mayDisplayParentControls' );
export const mayDisplayPatternEditingControlsKey = Symbol(
'mayDisplayPatternEditingControls'
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 almost certainly missing context here 😅 but my reading of #73845 points to all attribute types (so presumably all the inspector tabs they live in) being displayed in regular (or "design") editing mode? If that's the goal, maybe we don't need this check?

If it does turn out to be needed though, considering that contentOnly mode (not sure if we're still calling it that?) could in future be extended to other places than patterns, it might be good to use a less specific name for this Symbol such as mayDisplayContentEditingControlsKey.

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, I'm not sure. Showing Settings for patterns would be a lot. It'd include all of those Advanced panels.

If you're right then there's a chance it can be simplified.

Copy link
Contributor

Choose a reason for hiding this comment

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

My read was all tabs would be available when not in contentOnly; that would just mean we would always show content/list view. I don't think the settings should be visible in contentOnly.

Copy link
Contributor Author

@talldan talldan Jan 14, 2026

Choose a reason for hiding this comment

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

Yeah, they are visible when not in contentOnly.

Example for the List Block:

Kapture.2026-01-14.at.12.59.53.mp4

);
export const blockEditingModeKey = Symbol( 'blockEditingMode' );
export const blockBindingsKey = Symbol( 'blockBindings' );
export const isPreviewModeKey = Symbol( 'isPreviewMode' );
Expand Down
6 changes: 6 additions & 0 deletions packages/block-editor/src/components/block-edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
useBlockEditContext,
mayDisplayControlsKey,
mayDisplayParentControlsKey,
mayDisplayPatternEditingControlsKey,
blockEditingModeKey,
blockBindingsKey,
isPreviewModeKey,
Expand All @@ -33,6 +34,7 @@ export { useBlockEditContext };
export default function BlockEdit( {
mayDisplayControls,
mayDisplayParentControls,
mayDisplayPatternEditingControls,
blockEditingMode,
isPreviewMode,
// The remaining props are passed through the BlockEdit filters and are thus
Expand Down Expand Up @@ -69,6 +71,9 @@ export default function BlockEdit( {
// usage outside of the package (this context is exposed).
[ mayDisplayControlsKey ]: mayDisplayControls,
[ mayDisplayParentControlsKey ]: mayDisplayParentControls,
[ mayDisplayPatternEditingControlsKey ]:
mayDisplayPatternEditingControls &&
blockEditingMode !== 'disabled',
[ blockEditingModeKey ]: blockEditingMode,
[ blockBindingsKey ]: bindings,
[ isPreviewModeKey ]: isPreviewMode,
Expand All @@ -82,6 +87,7 @@ export default function BlockEdit( {
__unstableLayoutClassNames,
mayDisplayControls,
mayDisplayParentControls,
mayDisplayPatternEditingControls,
blockEditingMode,
bindings,
isPreviewMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,12 @@ function StyleInspectorSlots( {
blockName,
showAdvancedControls = true,
showPositionControls = true,
showListControls = false,
showBindingsControls = true,
} ) {
const borderPanelLabel = useBorderPanelLabel( { blockName } );
return (
<>
<InspectorControls.Slot />
{ showListControls && <InspectorControls.Slot group="list" /> }
<InspectorControls.Slot
group="color"
label={ __( 'Color' ) }
Expand Down Expand Up @@ -377,11 +375,9 @@ const BlockInspectorSingleBlock = ( {
) }
<ContentTab contentClientIds={ contentClientIds } />
<InspectorControls.Slot group="content" />
<InspectorControls.Slot group="list" />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It may have been for convenience, not sure, but the List tab was previously rendered as part of <StyleInspectorSlots>.

I've moved it out of that component to here, which feels like the right move to me.

{ ! isSectionBlock && (
<StyleInspectorSlots
blockName={ blockName }
showListControls
/>
<StyleInspectorSlots blockName={ blockName } />
) }
{ isSectionBlock &&
isBlockSynced &&
Expand Down
3 changes: 3 additions & 0 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ function BlockListBlock( {
const {
mayDisplayControls,
mayDisplayParentControls,
isSelectionWithinCurrentSection,
themeSupportsLayout,
...context
} = useContext( PrivateBlockContext );
Expand Down Expand Up @@ -135,6 +136,7 @@ function BlockListBlock( {
}
mayDisplayControls={ mayDisplayControls }
mayDisplayParentControls={ mayDisplayParentControls }
mayDisplayPatternEditingControls={ isSelectionWithinCurrentSection }
blockEditingMode={ context.blockEditingMode }
isPreviewMode={ context.isPreviewMode }
/>
Expand Down Expand Up @@ -231,6 +233,7 @@ function BlockListBlock( {
value={ {
wrapperProps: updatedWrapperProps,
isAligned,
isSelectionWithinCurrentSection,
...context,
} }
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ function BlockListBlock( {
isParentSelected,
order,
mayDisplayControls,
mayDisplayPatternEditingControls,
blockEditingMode,
} = useSelect(
( select ) => {
Expand Down Expand Up @@ -263,6 +264,7 @@ function BlockListBlock( {
getMultiSelectedBlockClientIds().every(
( id ) => getBlockName( id ) === name
) ),
mayDisplayPatternEditingControls: false, // Section/pattern editing not yet supported on native
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Claude did this, I don't know if it's the right move 🤷

Copy link
Contributor

Choose a reason for hiding this comment

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

Are these native files still in development? The last mobile-specific edit to this file was mid-2024.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think native has had any content-only/pattern editing changes thus far? Might be wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so, so I can probably remove the changes. Last I remember the native code doesn't even compile, the CI tests are skipped.

blockEditingMode: getBlockEditingMode( clientId ),
};
},
Expand Down Expand Up @@ -403,6 +405,9 @@ function BlockListBlock( {
}
wrapperProps={ wrapperProps }
mayDisplayControls={ mayDisplayControls }
mayDisplayPatternEditingControls={
mayDisplayPatternEditingControls
}
blockEditingMode={ blockEditingMode }
/>
<View onLayout={ onLayout } />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,29 @@ export default function useInspectorControlsTabs(
...( hasListFills && hasStyleFills > 1 ? advancedFills : [] ),
];

// When the block fields experiment is active, only rely on `hasContentFills`
// to determine whether the content tab to be shown. The tab purely uses slot
// fills in this situation.
const shouldShowBlockFields =
window?.__experimentalContentOnlyInspectorFields;
const hasContentTab =
hasContentFills ||
!! ( contentClientIds && contentClientIds.length > 0 );
( ! shouldShowBlockFields && contentClientIds?.length );

const hasListTab = hasListFills && ! isSectionBlock;
if ( hasContentTab ) {
tabs.push( TAB_CONTENT );
}

// Add the tabs in the order that they will default to if available.
// List View > Content > Settings > Styles.
if ( hasListTab ) {
if ( hasListFills ) {
tabs.push( TAB_LIST_VIEW );
}

if ( hasContentTab ) {
tabs.push( TAB_CONTENT );
}

Comment on lines +96 to -102
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reorders the tabs so the List Tab is after the Content tab, like the designs show.

if (
( settingsFills.length ||
// Advanded fills who up in settings tab if available or they blend into the default tab, if there's only one tab.
( advancedFills.length && ( hasContentTab || hasListTab ) ) ) &&
( advancedFills.length && ( hasContentTab || hasListFills ) ) ) &&
! isSectionBlock
) {
tabs.push( TAB_SETTINGS );
Expand Down
30 changes: 10 additions & 20 deletions packages/block-editor/src/components/inspector-controls/fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ import { useEffect, useContext } from '@wordpress/element';
import {
useBlockEditContext,
mayDisplayControlsKey,
mayDisplayPatternEditingControlsKey,
} from '../block-edit/context';
import groups from './groups';

export function PrivateInspectorControlsFill( {
export default function InspectorControlsFill( {
children,
group = 'default',
__experimentalGroup,
resetAllFilter,
forceDisplayControls,
} ) {
if ( __experimentalGroup ) {
deprecated(
Expand All @@ -43,7 +43,14 @@ export function PrivateInspectorControlsFill( {
warning( `Unknown InspectorControls group "${ group }" provided.` );
return null;
}
if ( ! forceDisplayControls && ! context[ mayDisplayControlsKey ] ) {
const shouldDisplayForPatternEditing =
context[ mayDisplayPatternEditingControlsKey ] &&
( group === 'list' || group === 'content' );
Comment on lines +46 to +48
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Declares exactly which InspectorControls groups support pattern editing, otherwise the Settings tab shows in patterns as well.


if (
! context[ mayDisplayControlsKey ] &&
! shouldDisplayForPatternEditing
) {
return null;
}

Expand All @@ -64,23 +71,6 @@ export function PrivateInspectorControlsFill( {
);
}

export default function InspectorControlsFill( {
children,
group = 'default',
__experimentalGroup,
resetAllFilter,
} ) {
return (
<PrivateInspectorControlsFill
group={ group }
__experimentalGroup={ __experimentalGroup }
resetAllFilter={ resetAllFilter }
>
{ children }
</PrivateInspectorControlsFill>
);
}

function RegisterResetAll( { resetAllFilter, children } ) {
const { registerResetAllFilter, deregisterResetAllFilter } =
useContext( ToolsPanelContext );
Expand Down
98 changes: 41 additions & 57 deletions packages/block-editor/src/hooks/block-fields/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
/**
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { privateApis as blocksPrivateApis } from '@wordpress/blocks';
import {
privateApis as blocksPrivateApis,
getBlockType,
} from '@wordpress/blocks';
import {
__experimentalHStack as HStack,
__experimentalTruncate as Truncate,
} from '@wordpress/components';
import { createHigherOrderComponent } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';
import { DataForm } from '@wordpress/dataviews';
import { useContext, useState, useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import BlockIcon from '../../components/block-icon';
import useBlockDisplayTitle from '../../components/block-title/use-block-display-title';
import useBlockDisplayInformation from '../../components/use-block-display-information';
const { fieldsKey, formKey } = unlock( blocksPrivateApis );
import FieldsDropdownMenu from './fields-dropdown-menu';
import { PrivateBlockContext } from '../../components/block-list/private-block-context';
import { PrivateInspectorControlsFill } from '../../components/inspector-controls/fill';
import InspectorControls from '../../components/inspector-controls/fill';

// controls
import RichText from './rich-text';
Expand All @@ -49,7 +52,6 @@ function createConfiguredControl( ControlComponent, config = {} ) {
* @param {Object} props
* @param {string} props.clientId The clientId of the block.
* @param {Object} props.blockType The blockType definition.
* @param {Object} props.attributes The block's attribute values.
* @param {Function} props.setAttributes Action to set the block's attributes.
* @param {boolean} props.isCollapsed Whether the DataForm is rendered as 'collapsed' with only the first field
* displayed by default. When collapsed a dropdown is displayed to allow
Expand All @@ -59,7 +61,6 @@ function createConfiguredControl( ControlComponent, config = {} ) {
function BlockFields( {
clientId,
blockType,
attributes,
setAttributes,
isCollapsed = false,
} ) {
Expand All @@ -71,6 +72,11 @@ function BlockFields( {

const blockTypeFields = blockType?.[ fieldsKey ];

const attributes = useSelect(
( select ) => select( blockEditorStore ).getBlockAttributes( clientId ),
[ clientId ]
);
Comment on lines +75 to +78
Copy link
Contributor Author

Choose a reason for hiding this comment

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

attributes isn't passed into the hook now that it doesn't use the BlockEdit filter, so have to select the values


const computedForm = useMemo( () => {
if ( ! isCollapsed ) {
return blockType?.[ formKey ];
Expand Down Expand Up @@ -187,56 +193,34 @@ function BlockFields( {
);
}

const withBlockFields = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const {
blockType,
isSelectionWithinCurrentSection,
isSectionBlock,
blockEditingMode,
isSelected,
} = useContext( PrivateBlockContext );

const shouldShowBlockFields =
window?.__experimentalContentOnlyInspectorFields;
const blockTypeFields = blockType?.[ fieldsKey ];

if ( ! shouldShowBlockFields || ! blockTypeFields?.length ) {
return <BlockEdit key="edit" { ...props } />;
}
function hasBlockFieldsSupport( blockName ) {
return !! (
window?.__experimentalContentOnlyInspectorFields &&
getBlockType( blockName )?.[ fieldsKey ]
);
}

return (
<>
<BlockEdit key="edit" { ...props } />
{
// Display the controls of all inner blocks for section/pattern editing.
isSelectionWithinCurrentSection &&
( isSectionBlock ||
blockEditingMode === 'contentOnly' ) && (
<PrivateInspectorControlsFill
group="content"
forceDisplayControls
>
<BlockFields
{ ...props }
blockType={ blockType }
isCollapsed
/>
</PrivateInspectorControlsFill>
)
}
{ ! isSelectionWithinCurrentSection && isSelected && (
<PrivateInspectorControlsFill group="content">
<BlockFields { ...props } blockType={ blockType } />
</PrivateInspectorControlsFill>
) }
</>
);
}
);
export function BlockFieldsPanel( props ) {
const { blockType, isSelectionWithinCurrentSection } =
useContext( PrivateBlockContext );

addFilter(
'editor.BlockEdit',
'core/content-only-controls/block-fields',
withBlockFields
);
return (
<InspectorControls group="content">
<BlockFields
{ ...props }
blockType={ blockType }
isCollapsed={ isSelectionWithinCurrentSection }
/>
</InspectorControls>
);
}
Comment on lines +207 to +216
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is quite a lot simpler now, we generally render the same thing regardless of whether in a pattern or not, the only difference is isCollapsed. 🎉


/**
* Export block support definition.
*/
export default {
edit: BlockFieldsPanel,
hasSupport: hasBlockFieldsSupport,
attributeKeys: [],
supportsPatternEditing: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

supportsPatternEditing is a new thing, the block edit hooks are generally optimized to only work for single selected blocks. There needs to be a way to opt in to also showing the controls for blocks that aren't currently selected, as it the case with Pattern Editing.

};
3 changes: 2 additions & 1 deletion packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import './lock';
import allowedBlocks from './allowed-blocks';
import anchor from './anchor';
import ariaLabel from './aria-label';
import './block-fields';
import blockFields from './block-fields';
import customClassName from './custom-class-name';
import './generated-class-name';
import style from './style';
Expand Down Expand Up @@ -56,6 +56,7 @@ createBlockEditFilter(
blockBindingsPanel,
childLayout,
allowedBlocks,
blockFields,
listView,
autoInspectorControls,
].filter( Boolean )
Expand Down
Loading
Loading