Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template part - register block variations and area selection inputs from original area definitions. #30821

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
4 changes: 4 additions & 0 deletions lib/editor-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ function gutenberg_extend_post_editor_settings( $settings ) {
$settings['imageDefaultSize'] = in_array( $image_default_size, $image_sizes, true ) ? $image_default_size : 'large';
$settings['__unstableEnableFullSiteEditingBlocks'] = gutenberg_supports_block_templates();

if ( gutenberg_is_fse_theme() ) {
$settings['defaultTemplatePartAreas'] = gutenberg_get_allowed_template_part_areas();
}

return $settings;
}
add_filter( 'block_editor_settings', 'gutenberg_extend_post_editor_settings' );
Expand Down
6 changes: 6 additions & 0 deletions lib/full-site-editing/template-parts.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ function gutenberg_get_allowed_template_part_areas() {
'General templates often perform a specific role like displaying post content, and are not tied to any particular area.',
'gutenberg'
),
'icon' => 'layout',
'area_tag' => 'div',
),
array(
'area' => WP_TEMPLATE_PART_AREA_HEADER,
Expand All @@ -184,6 +186,8 @@ function gutenberg_get_allowed_template_part_areas() {
'The Header template defines a page area that typically contains a title, logo, and main navigation.',
'gutenberg'
),
'icon' => 'header',
'area_tag' => 'header',
),
array(
'area' => WP_TEMPLATE_PART_AREA_FOOTER,
Expand All @@ -192,6 +196,8 @@ function gutenberg_get_allowed_template_part_areas() {
'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.',
'gutenberg'
),
'icon' => 'footer',
'area_tag' => 'footer',
),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,15 @@ import { useEntityProp } from '@wordpress/core-data';
import { SelectControl, TextControl } from '@wordpress/components';
import { sprintf, __ } from '@wordpress/i18n';
import { InspectorAdvancedControls } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import { getTagBasedOnArea } from './get-tag-based-on-area';

const AREA_OPTIONS = [
{ label: __( 'Header' ), value: 'header' },
{ label: __( 'Footer' ), value: 'footer' },
{
label: __( 'General' ),
value: 'uncategorized',
},
];
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';

export function TemplatePartAdvancedControls( {
tagName,
setAttributes,
isEntityAvailable,
templatePartId,
defaultWrapper,
} ) {
const [ area, setArea ] = useEntityProp(
'postType',
Expand All @@ -40,6 +29,18 @@ export function TemplatePartAdvancedControls( {
templatePartId
);

const { areaOptions } = useSelect( ( select ) => {
const definedAreas = select(
editorStore
).__experimentalGetDefaultTemplatePartAreas();
return {
areaOptions: definedAreas.map( ( { label, area: _area } ) => ( {
label,
value: _area,
} ) ),
};
}, [] );

return (
<InspectorAdvancedControls>
{ isEntityAvailable && (
Expand All @@ -56,7 +57,7 @@ export function TemplatePartAdvancedControls( {
<SelectControl
label={ __( 'Area' ) }
labelPosition="top"
options={ AREA_OPTIONS }
options={ areaOptions }
value={ area }
onChange={ setArea }
/>
Expand All @@ -69,7 +70,7 @@ export function TemplatePartAdvancedControls( {
label: sprintf(
/* translators: %s: HTML tag based on area. */
__( 'Default based on area (%s)' ),
`<${ getTagBasedOnArea( area ) }>`
`<${ defaultWrapper }>`
),
value: '',
},
Expand Down

This file was deleted.

16 changes: 12 additions & 4 deletions packages/block-library/src/template-part/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';

/**
* Internal dependencies
Expand All @@ -25,7 +26,6 @@ import TemplatePartInnerBlocks from './inner-blocks';
import TemplatePartPlaceholder from './placeholder';
import TemplatePartSelection from './selection';
import { TemplatePartAdvancedControls } from './advanced-controls';
import { getTagBasedOnArea } from './get-tag-based-on-area';

export default function TemplatePartEdit( {
attributes,
Expand All @@ -42,7 +42,7 @@ export default function TemplatePartEdit( {
// Set the postId block attribute if it did not exist,
// but wait until the inner blocks have loaded to allow
// new edits to trigger this.
const { isResolved, innerBlocks, isMissing, area } = useSelect(
const { isResolved, innerBlocks, isMissing, defaultWrapper } = useSelect(
( select ) => {
const { getEditedEntityRecord, hasFinishedResolution } = select(
coreStore
Expand All @@ -64,11 +64,18 @@ export default function TemplatePartEdit( {
)
: false;

const defaultWrapperElement = select( editorStore )
.__experimentalGetDefaultTemplatePartAreas()
.find(
( { area } ) =>
area === ( entityRecord?.area || attributes.area )
)?.area_tag;

return {
innerBlocks: getBlocks( clientId ),
isResolved: hasResolvedEntity,
isMissing: hasResolvedEntity && ! entityRecord,
area: entityRecord?.area,
defaultWrapper: defaultWrapperElement || 'div',
};
},
[ templatePartId, clientId ]
Expand All @@ -77,7 +84,7 @@ export default function TemplatePartEdit( {
const blockProps = useBlockProps();
const isPlaceholder = ! slug;
const isEntityAvailable = ! isPlaceholder && ! isMissing && isResolved;
const TagName = tagName || getTagBasedOnArea( area );
const TagName = tagName || defaultWrapper;

// We don't want to render a missing state if we have any inner blocks.
// A new template part is automatically created if we have any inner blocks but no entity.
Expand Down Expand Up @@ -117,6 +124,7 @@ export default function TemplatePartEdit( {
setAttributes={ setAttributes }
isEntityAvailable={ isEntityAvailable }
templatePartId={ templatePartId }
defaultWrapper={ defaultWrapper }
/>
{ isPlaceholder && (
<TagName { ...blockProps }>
Expand Down
3 changes: 1 addition & 2 deletions packages/block-library/src/template-part/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { layout } from '@wordpress/icons';
*/
import metadata from './block.json';
import edit from './edit';
import variations from './variations';
import './variations';

const { name } = metadata;
export { metadata, name };
Expand Down Expand Up @@ -47,5 +47,4 @@ export const settings = {
return startCase( entity.title?.rendered || entity.slug );
},
edit,
variations,
};
14 changes: 8 additions & 6 deletions packages/block-library/src/template-part/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,14 @@ function render_block_core_template_part( $attributes ) {
$content = do_shortcode( $content );

if ( empty( $attributes['tagName'] ) ) {
$area_tags = array(
WP_TEMPLATE_PART_AREA_HEADER => 'header',
WP_TEMPLATE_PART_AREA_FOOTER => 'footer',
WP_TEMPLATE_PART_AREA_UNCATEGORIZED => 'div',
);
$html_tag = null !== $area && isset( $area_tags[ $area ] ) ? $area_tags[ $area ] : $area_tags[ WP_TEMPLATE_PART_AREA_UNCATEGORIZED ];
$defined_areas = gutenberg_get_allowed_template_part_areas();
$area_tag = 'div';
foreach ( $defined_areas as $defined_area ) {
if ( $defined_area['area'] === $area && isset( $defined_area['area_tag'] ) ) {
$area_tag = $defined_area['area_tag'];
}
}
$html_tag = $area_tag;
} else {
$html_tag = esc_attr( $attributes['tagName'] );
}
Expand Down
102 changes: 53 additions & 49 deletions packages/block-library/src/template-part/variations.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,60 @@
/**
* WordPress dependencies
*/
import { footer, header } from '@wordpress/icons';
import { store as coreDataStore } from '@wordpress/core-data';
import { select } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { store as editorStore } from '@wordpress/editor';
import { store as blocksStore } from '@wordpress/blocks';
import { dispatch, select, subscribe } from '@wordpress/data';

const variations = [
{
name: 'header',
title: __( 'Header' ),
description: __(
"The header template defines a page area that typically contains a title, logo, and main navigation. Since it's a global element it can be present across all pages and posts."
),
icon: header,
attributes: { area: 'header' },
scope: [ 'inserter' ],
},
{
name: 'footer',
title: __( 'Footer' ),
description: __(
"The footer template defines a page area that typically contains site credits, social links, or any other combination of blocks. Since it's a global element it can be present across all pages and posts."
),
icon: footer,
attributes: { area: 'footer' },
scope: [ 'inserter' ],
},
];
const unsubscribe = subscribe( () => {
const definedVariations = select(
editorStore
).__experimentalGetDefaultTemplatePartAreas();

/**
* Add `isActive` function to all `Template Part` variations, if not defined.
* `isActive` function is used to find a variation match from a created
* Block by providing its attributes.
*/
variations.forEach( ( variation ) => {
if ( variation.isActive ) return;
variation.isActive = ( blockAttributes, variationAttributes ) => {
const { area, theme, slug } = blockAttributes;
// We first check the `area` block attribute which is set during insertion.
// This property is removed on the creation of a template part.
if ( area ) return area === variationAttributes.area;
// Find a matching variation from the created template part
// by checking the entity's `area` property.
if ( ! slug ) return false;
const entity = select( coreDataStore ).getEntityRecord(
'postType',
'wp_template_part',
`${ theme }//${ slug }`
);
return entity?.area === variationAttributes.area;
};
} );
if ( ! definedVariations?.length ) {
return;
}
unsubscribe();

export default variations;
const variations = definedVariations
.filter( ( { area } ) => 'uncategorized' !== area )
.map( ( { area, label, description, icon } ) => {
return {
name: area,
title: label,
description,
icon,
attributes: { area },
scope: [ 'inserter' ],
};
} );

/**
* Add `isActive` function to all `Template Part` variations, if not defined.
* `isActive` function is used to find a variation match from a created
* Block by providing its attributes.
*/
variations.forEach( ( variation ) => {
if ( variation.isActive ) return;
variation.isActive = ( blockAttributes, variationAttributes ) => {
const { area, theme, slug } = blockAttributes;
// We first check the `area` block attribute which is set during insertion.
// This property is removed on the creation of a template part.
if ( area ) return area === variationAttributes.area;
// Find a matching variation from the created template part
// by checking the entity's `area` property.
if ( ! slug ) return false;
const entity = select( coreDataStore ).getEntityRecord(
'postType',
'wp_template_part',
`${ theme }//${ slug }`
);
return entity?.area === variationAttributes.area;
};
} );

dispatch( blocksStore ).addBlockVariations(
'core/template-part',
variations
);
} );
13 changes: 9 additions & 4 deletions packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { addQueryArgs } from '@wordpress/url';
import { createRegistrySelector } from '@wordpress/data';
import deprecated from '@wordpress/deprecated';
import { Platform } from '@wordpress/element';
import { layout } from '@wordpress/icons';

/**
* Internal dependencies
Expand All @@ -40,7 +41,7 @@ import {
} from './constants';
import { getPostRawValue } from './reducer';
import { cleanForSlug } from '../utils/url';
import { getTemplatePartIconByArea } from './utils/get-template-part-icon';
import { getTemplatePartIcon } from './utils/get-template-part-icon';

/**
* Shared reference to an empty object for cases where it is important to avoid
Expand Down Expand Up @@ -1680,9 +1681,10 @@ export function __experimentalGetDefaultTemplateTypes( state ) {
*/
export const __experimentalGetDefaultTemplatePartAreas = createSelector(
( state ) => {
const areas = getEditorSettings( state )?.defaultTemplatePartAreas;
const areas =
getEditorSettings( state )?.defaultTemplatePartAreas || [];
return areas?.map( ( item ) => {
return { ...item, icon: getTemplatePartIconByArea( item.area ) };
return { ...item, icon: getTemplatePartIcon( item.icon ) };
} );
},
( state ) => [ getEditorSettings( state )?.defaultTemplatePartAreas ]
Expand Down Expand Up @@ -1723,7 +1725,10 @@ export function __experimentalGetTemplateInfo( state, template ) {

const templateTitle = isString( title ) ? title : title?.rendered;
const templateDescription = isString( excerpt ) ? excerpt : excerpt?.raw;
const templateIcon = getTemplatePartIconByArea( area );
const templateIcon =
__experimentalGetDefaultTemplatePartAreas( state ).find(
( item ) => area === item.area
)?.icon || layout;

return {
title:
Expand Down
Loading