Commit 057a68972ce for woocommerce
commit 057a68972ce106b2afa387f991fc83a861c6eebd
Author: Luigi Teschio <gigitux@gmail.com>
Date: Tue Oct 20 18:12:14 2026 +0200
Unify Product Title and Product Summary block variations (#69785)
* Update single product templates to use the current title block
* Add changelog entry for single product template update
* fix template
* consolidate variations registration
* clean up changelog
* update context
* Scope Product Collection context to collection descendants
* Add changelog entry for Product Collection context scoping
* improve guard
* improve logic
* improve code
* fix test
* Update plugins/woocommerce/tests/php/src/Blocks/BlockTypes/SingleProduct.php
Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
* remove changelog
* use constant
---------
Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/unify-product-element-variations b/plugins/woocommerce/changelog/unify-product-element-variations
new file mode 100644
index 00000000000..d39691c3f6a
--- /dev/null
+++ b/plugins/woocommerce/changelog/unify-product-element-variations
@@ -0,0 +1,4 @@
+Significance: patch
+Type: tweak
+
+Unify Product Title and Product Summary variations across product blocks and templates.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/single-product.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/single-product.ts
index f2f09a4e222..5a8dbb06867 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/single-product.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/classic-template/single-product.ts
@@ -3,7 +3,7 @@
*/
import { __, sprintf } from '@wordpress/i18n';
import { getSetting } from '@woocommerce/settings';
-import { VARIATION_NAME as PRODUCT_TITLE_VARIATION_NAME } from '@woocommerce/blocks/product-query/variations/elements/product-title';
+import { PRODUCT_TITLE_VARIATION_NAME } from '@woocommerce/product-elements/variations/constants';
import {
INNER_BLOCKS_PRODUCT_TEMPLATE as productCollectionInnerBlocksTemplate,
DEFAULT_ATTRIBUTES as productCollectionDefaultAttributes,
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/constants.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/constants.ts
index 7e8af19238c..c488d3f8091 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/constants.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/constants.ts
@@ -9,7 +9,7 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { VARIATION_NAME as PRODUCT_TITLE_VARIATION_NAME } from '../product-collection/variations/elements/product-title';
+import { PRODUCT_TITLE_VARIATION_NAME } from '../product-elements-blocks/variations/constants';
export const DEFAULT_EDITOR_SIZE = {
height: 500,
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/featured-product/deprecated.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/featured-product/deprecated.tsx
index 9293339721f..6955392100f 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/featured-product/deprecated.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/featured-items/featured-product/deprecated.tsx
@@ -3,6 +3,7 @@
*/
import { BlockInstance, createBlock } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';
+import { PRODUCT_TITLE_VARIATION_NAME } from '@woocommerce/product-elements/variations/constants';
/**
* Internal dependencies
@@ -77,8 +78,7 @@ const v1 = {
level: 2,
isLink: false,
textAlign: 'center',
- __woocommerceNamespace:
- 'woocommerce/product-collection/product-title',
+ __woocommerceNamespace: PRODUCT_TITLE_VARIATION_NAME,
} )
);
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts
index c01d54e7a85..4903de73b6b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts
@@ -18,6 +18,10 @@ import type {
PostTemplateLayout,
PostTemplateLayoutTypes,
} from './types';
+import {
+ PRODUCT_TITLE_VARIATION_NAME,
+ PRODUCT_SUMMARY_VARIATION_NAME,
+} from '../product-elements-blocks/variations/constants';
import { DEFAULT_ATTRIBUTES } from '../product-collection/constants';
import {
LayoutOptions,
@@ -80,8 +84,7 @@ const transformPostTitle: TransformBlock = ( block, innerBlocks ) => {
return createBlock(
'core/post-title',
{
- __woocommerceNamespace:
- 'woocommerce/product-collection/product-title',
+ __woocommerceNamespace: PRODUCT_TITLE_VARIATION_NAME,
...restAttributes,
},
innerBlocks
@@ -93,8 +96,7 @@ const transformPostSummary: TransformBlock = ( block, innerBlocks ) => {
return createBlock(
'core/post-excerpt',
{
- __woocommerceNamespace:
- 'woocommerce/product-collection/product-summary',
+ __woocommerceNamespace: PRODUCT_SUMMARY_VARIATION_NAME,
...restAttributes,
},
innerBlocks
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/constants.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/constants.ts
index 87f10879ebc..8a0f856bc6b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/constants.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/constants.ts
@@ -17,6 +17,7 @@ import type { InnerBlockTemplate } from '@wordpress/blocks';
* Internal dependencies
*/
import blockJson from './block.json';
+import { PRODUCT_TITLE_VARIATION_NAME } from '../product-elements-blocks/variations/constants';
import {
ProductCollectionAttributes,
ProductCollectionQuery,
@@ -25,7 +26,6 @@ import {
} from './types';
export const PRODUCT_COLLECTION_BLOCK_NAME = blockJson.name;
-const PRODUCT_TITLE_NAME = `${ PRODUCT_COLLECTION_BLOCK_NAME }/product-title`;
export const STOCK_STATUS_OPTIONS = getSetting< Record< string, string > >(
'stockStatusOptions',
@@ -164,7 +164,7 @@ export const INNER_BLOCKS_PRODUCT_TEMPLATE: InnerBlockTemplate = [
},
},
isLink: true,
- __woocommerceNamespace: PRODUCT_TITLE_NAME,
+ __woocommerceNamespace: PRODUCT_TITLE_VARIATION_NAME,
},
],
[
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/index.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/index.tsx
index c182d0a1dc3..9851a08df3d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/index.tsx
@@ -10,8 +10,6 @@ import metadata from './block.json';
import edit from './edit';
import save from './save';
import icon from './icon';
-import registerProductSummaryVariation from './variations/elements/product-summary';
-import registerProductTitleVariation from './variations/elements/product-title';
import registerCollections from './collections';
import { addProductCollectionToQueryPaginationParentOrAncestor } from './utils';
@@ -20,7 +18,5 @@ registerBlockType( metadata, {
edit,
save,
} );
-registerProductSummaryVariation();
-registerProductTitleVariation();
registerCollections();
addProductCollectionToQueryPaginationParentOrAncestor();
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/index.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/index.ts
deleted file mode 100644
index 9bb2335e783..00000000000
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/index.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * External dependencies
- */
-import type { Block } from '@wordpress/blocks';
-import { addFilter } from '@wordpress/hooks';
-
-/**
- * Internal dependencies
- */
-import { CORE_NAME as PRODUCT_TITLE_ID } from './elements/product-title';
-import { CORE_NAME as PRODUCT_SUMMARY_ID } from './elements/product-summary';
-
-const EXTENDED_CORE_ELEMENTS = [ PRODUCT_SUMMARY_ID, PRODUCT_TITLE_ID ];
-
-function registerProductCollectionElementsNamespace(
- props: Block,
- blockName: string
-) {
- if ( EXTENDED_CORE_ELEMENTS.includes( blockName ) ) {
- // Gracefully handle if settings.attributes is undefined.
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore -- We need this because `attributes` is marked as `readonly`
- props.attributes = {
- ...props.attributes,
- __woocommerceNamespace: {
- type: 'string',
- },
- };
- }
-
- return props;
-}
-
-addFilter(
- 'blocks.registerBlockType',
- 'core/custom-class-name/attribute',
- registerProductCollectionElementsNamespace
-);
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/summary/upgrade.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/summary/upgrade.tsx
index 3d6c072fe46..9ca637e98fd 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/summary/upgrade.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/summary/upgrade.tsx
@@ -15,21 +15,22 @@ import {
type ComponentType,
} from '@wordpress/element';
import { type EditorBlock } from '@woocommerce/types';
-import { VARIATION_NAME as PQ_PRODUCT_SUMMARY_VARIATION_NAME } from '@woocommerce/blocks/product-query/variations/elements/product-summary';
-import { VARIATION_NAME as PC_PRODUCT_SUMMARY_VARIATION_NAME } from '@woocommerce/blocks/product-collection/variations/elements/product-summary';
import { UpgradeDowngradeNotice } from '@woocommerce/editor-components/upgrade-downgrade-notice';
+/**
+ * Internal dependencies
+ */
+import { PRODUCT_SUMMARY_NAMESPACES } from '../variations/constants';
+
const CORE_NAME = 'core/post-excerpt';
const isProductSummaryBlockVariation = ( props: BlockInstance ) => {
- const pqVariation =
- props.attributes.__woocommerceNamespace ===
- PQ_PRODUCT_SUMMARY_VARIATION_NAME;
- const pcVariation =
- props.attributes.__woocommerceNamespace ===
- PC_PRODUCT_SUMMARY_VARIATION_NAME;
-
- return props.name === CORE_NAME && ( pqVariation || pcVariation );
+ return (
+ props.name === CORE_NAME &&
+ PRODUCT_SUMMARY_NAMESPACES.includes(
+ props.attributes.__woocommerceNamespace
+ )
+ );
};
const UpgradeNotice = ( { clientId }: { clientId: string } ) => {
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/constants.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/constants.ts
new file mode 100644
index 00000000000..54cad55025d
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/constants.ts
@@ -0,0 +1,24 @@
+export const PRODUCT_TITLE_VARIATION_NAME = 'woocommerce/product-title';
+export const PRODUCT_SUMMARY_VARIATION_NAME = 'woocommerce/product-summary';
+
+const DEPRECATED_PRODUCT_QUERY_PRODUCT_TITLE_VARIATION_NAME =
+ 'woocommerce/product-query/product-title';
+const DEPRECATED_PRODUCT_QUERY_PRODUCT_SUMMARY_VARIATION_NAME =
+ 'woocommerce/product-query/product-summary';
+
+const DEPRECATED_PRODUCT_COLLECTION_PRODUCT_TITLE_VARIATION_NAME =
+ 'woocommerce/product-collection/product-title';
+const DEPRECATED_PRODUCT_COLLECTION_PRODUCT_SUMMARY_VARIATION_NAME =
+ 'woocommerce/product-collection/product-summary';
+
+export const PRODUCT_TITLE_NAMESPACES = [
+ PRODUCT_TITLE_VARIATION_NAME,
+ DEPRECATED_PRODUCT_QUERY_PRODUCT_TITLE_VARIATION_NAME,
+ DEPRECATED_PRODUCT_COLLECTION_PRODUCT_TITLE_VARIATION_NAME,
+];
+
+export const PRODUCT_SUMMARY_NAMESPACES = [
+ PRODUCT_SUMMARY_VARIATION_NAME,
+ DEPRECATED_PRODUCT_QUERY_PRODUCT_SUMMARY_VARIATION_NAME,
+ DEPRECATED_PRODUCT_COLLECTION_PRODUCT_SUMMARY_VARIATION_NAME,
+];
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/index.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/index.ts
new file mode 100644
index 00000000000..1ddf9a5d01c
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/index.ts
@@ -0,0 +1,34 @@
+/**
+ * External dependencies
+ */
+import type { Block } from '@wordpress/blocks';
+import { addFilter } from '@wordpress/hooks';
+
+/**
+ * Internal dependencies
+ */
+import registerProductTitleVariation from './product-title';
+import registerProductSummaryVariation from './product-summary';
+
+addFilter(
+ 'blocks.registerBlockType',
+ 'woocommerce/product-elements/namespace',
+ ( settings: Block, blockName: string ) => {
+ if (
+ ! [ 'core/post-title', 'core/post-excerpt' ].includes( blockName )
+ ) {
+ return settings;
+ }
+
+ return {
+ ...settings,
+ attributes: {
+ ...settings.attributes,
+ __woocommerceNamespace: { type: 'string' },
+ },
+ };
+ }
+);
+
+registerProductTitleVariation();
+registerProductSummaryVariation();
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/product-summary.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/product-summary.tsx
similarity index 67%
rename from plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/product-summary.tsx
rename to plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/product-summary.tsx
index 6695593ee03..9b729345358 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/product-summary.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/product-summary.tsx
@@ -8,21 +8,21 @@ import { page } from '@wordpress/icons';
* Internal dependencies
*/
import { registerElementVariation } from './utils';
+import { BLOCK_DESCRIPTION, BLOCK_TITLE } from '../summary/constants';
import {
- BLOCK_DESCRIPTION,
- BLOCK_TITLE,
-} from '../../../product-elements-blocks/summary/constants';
-import blockJson from '../../block.json';
+ PRODUCT_SUMMARY_VARIATION_NAME,
+ PRODUCT_SUMMARY_NAMESPACES,
+} from './constants';
export const CORE_NAME = 'core/post-excerpt';
-export const VARIATION_NAME = `${ blockJson.name }/product-summary`;
const registerProductSummary = () => {
registerElementVariation( CORE_NAME, {
blockDescription: BLOCK_DESCRIPTION,
blockIcon: <Icon icon={ page } />,
blockTitle: BLOCK_TITLE,
- variationName: VARIATION_NAME,
+ variationName: PRODUCT_SUMMARY_VARIATION_NAME,
+ namespaces: PRODUCT_SUMMARY_NAMESPACES,
scope: [],
} );
};
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/product-title.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/product-title.tsx
similarity index 77%
rename from plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/product-title.tsx
rename to plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/product-title.tsx
index 12306b8f2dc..350ace54be3 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/product-title.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/product-title.tsx
@@ -9,10 +9,12 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import { registerElementVariation } from './utils';
-import blockJson from '../../block.json';
+import {
+ PRODUCT_TITLE_VARIATION_NAME,
+ PRODUCT_TITLE_NAMESPACES,
+} from './constants';
export const CORE_NAME = 'core/post-title';
-export const VARIATION_NAME = `${ blockJson.name }/product-title`;
const registerProductTitle = () => {
registerElementVariation( CORE_NAME, {
@@ -22,7 +24,9 @@ const registerProductTitle = () => {
),
blockIcon: <Icon icon={ heading } />,
blockTitle: __( 'Product Title', 'woocommerce' ),
- variationName: VARIATION_NAME,
+ variationName: PRODUCT_TITLE_VARIATION_NAME,
+ namespaces: PRODUCT_TITLE_NAMESPACES,
+
scope: [ 'block', 'inserter' ],
} );
};
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/utils.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/utils.tsx
similarity index 78%
rename from plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/utils.tsx
rename to plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/utils.tsx
index 6e5b5aa44d1..47788d3a4f1 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-collection/variations/elements/utils.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-elements-blocks/variations/utils.tsx
@@ -5,13 +5,15 @@ import {
type BlockVariationScope,
registerBlockVariation,
} from '@wordpress/blocks';
+import type { ReactElement } from '@wordpress/element';
interface VariationDetails {
blockDescription: string;
- blockIcon: JSX.Element;
+ blockIcon: ReactElement;
blockTitle: string;
variationName: string;
scope: BlockVariationScope[];
+ namespaces: string[];
}
export function registerElementVariation(
@@ -22,6 +24,7 @@ export function registerElementVariation(
blockTitle,
variationName,
scope,
+ namespaces,
}: VariationDetails
) {
registerBlockVariation( coreName, {
@@ -29,7 +32,7 @@ export function registerElementVariation(
name: variationName,
title: blockTitle,
isActive: ( blockAttributes ) =>
- blockAttributes.__woocommerceNamespace === variationName,
+ namespaces.includes( blockAttributes.__woocommerceNamespace ),
icon: {
src: blockIcon,
},
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/constants.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/constants.ts
index c579c76fedc..5b5e25c358d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/constants.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/constants.ts
@@ -9,7 +9,7 @@ import { isBoolean } from '@woocommerce/types';
* Internal dependencies
*/
import { QueryBlockAttributes } from './types';
-import { VARIATION_NAME as PRODUCT_TITLE_ID } from './variations/elements/product-title';
+import { PRODUCT_TITLE_VARIATION_NAME } from '../product-elements-blocks/variations/constants';
import { VARIATION_NAME as PRODUCT_TEMPLATE_ID } from './variations/elements/product-template';
export const PRODUCT_QUERY_VARIATION_NAME = 'woocommerce/product-query';
@@ -116,7 +116,7 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [
},
},
isLink: true,
- __woocommerceNamespace: PRODUCT_TITLE_ID,
+ __woocommerceNamespace: PRODUCT_TITLE_VARIATION_NAME,
},
],
[
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/index.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/index.tsx
index 03c59763e18..78e1fd4e33d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/index.tsx
@@ -7,25 +7,17 @@ import { addFilter } from '@wordpress/hooks';
/**
* Internal dependencies
*/
-import { CORE_NAME as PRODUCT_TITLE_ID } from './variations/elements/product-title';
-import { CORE_NAME as PRODUCT_SUMMARY_ID } from './variations/elements/product-summary';
import { CORE_NAME as PRODUCT_TEMPLATE_ID } from './variations/elements/product-template';
import './inspector-controls';
import './style.scss';
import './variations/product-query';
import './variations/related-products';
-const EXTENDED_CORE_ELEMENTS = [
- PRODUCT_SUMMARY_ID,
- PRODUCT_TEMPLATE_ID,
- PRODUCT_TITLE_ID,
-];
-
function registerProductQueryElementsNamespace(
props: Block,
blockName: string
) {
- if ( EXTENDED_CORE_ELEMENTS.includes( blockName ) ) {
+ if ( PRODUCT_TEMPLATE_ID === blockName ) {
// Gracefully handle if settings.attributes is undefined.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore -- We need this because `attributes` is marked as `readonly`
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/elements/product-summary.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/elements/product-summary.tsx
deleted file mode 100644
index 315007c3c2e..00000000000
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/elements/product-summary.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * External dependencies
- */
-import { Icon } from '@wordpress/components';
-import { page } from '@wordpress/icons';
-import {
- BLOCK_DESCRIPTION,
- BLOCK_TITLE,
-} from '@woocommerce/product-elements/summary/constants';
-
-/**
- * Internal dependencies
- */
-import { registerElementVariation } from './utils';
-
-export const CORE_NAME = 'core/post-excerpt';
-export const VARIATION_NAME = 'woocommerce/product-query/product-summary';
-
-registerElementVariation( CORE_NAME, {
- blockDescription: BLOCK_DESCRIPTION,
- blockIcon: <Icon icon={ page } />,
- blockTitle: BLOCK_TITLE,
- variationName: VARIATION_NAME,
- scope: [],
-} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/elements/product-title.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/elements/product-title.tsx
deleted file mode 100644
index fc688de144d..00000000000
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/elements/product-title.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * External dependencies
- */
-import { Icon } from '@wordpress/components';
-import { __ } from '@wordpress/i18n';
-import { heading } from '@wordpress/icons';
-
-/**
- * Internal dependencies
- */
-import { registerElementVariation } from './utils';
-
-export const CORE_NAME = 'core/post-title';
-export const VARIATION_NAME = 'woocommerce/product-query/product-title';
-
-registerElementVariation( CORE_NAME, {
- blockDescription: __( 'Display the title of a product.', 'woocommerce' ),
- blockIcon: <Icon icon={ heading } />,
- blockTitle: __( 'Product Title', 'woocommerce' ),
- variationName: VARIATION_NAME,
- scope: [ 'block' ],
-} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/related-products.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/related-products.tsx
index 6752739398f..65143038f90 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/related-products.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-query/variations/related-products.tsx
@@ -15,7 +15,7 @@ import { isBoolean } from '@woocommerce/types';
import { QUERY_LOOP_ID } from '../constants';
import { VARIATION_NAME as PRODUCT_TEMPLATE_ID } from './elements/product-template';
-import { VARIATION_NAME as PRODUCT_TITLE_ID } from './elements/product-title';
+import { PRODUCT_TITLE_VARIATION_NAME } from '../../product-elements-blocks/variations/constants';
const VARIATION_NAME = 'woocommerce/related-products';
@@ -87,7 +87,7 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [
level: 3,
fontSize: 'medium',
isLink: true,
- __woocommerceNamespace: PRODUCT_TITLE_ID,
+ __woocommerceNamespace: PRODUCT_TITLE_VARIATION_NAME,
},
[],
],
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/single-product/constants.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/single-product/constants.tsx
index 82f284c270c..e1f28dbc80e 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/single-product/constants.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/single-product/constants.tsx
@@ -10,7 +10,7 @@ import type { InnerBlockTemplate } from '@wordpress/blocks';
* Internal dependencies
*/
import metadata from './block.json';
-import { VARIATION_NAME as PRODUCT_TITLE_VARIATION_NAME } from '../product-query/variations/elements/product-title';
+import { PRODUCT_TITLE_VARIATION_NAME } from '../product-elements-blocks/variations/constants';
export const BLOCK_ICON = (
<Icon
diff --git a/plugins/woocommerce/client/blocks/assets/js/index.js b/plugins/woocommerce/client/blocks/assets/js/index.js
index 28ae6325465..fdf74a7f893 100644
--- a/plugins/woocommerce/client/blocks/assets/js/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/index.js
@@ -8,3 +8,4 @@ import './filters/get-block-attributes';
import './filters/hide-incompatible-template-parts';
import './filters/unregister-block-types';
import './base/components/notice-banner/style.scss';
+import './blocks/product-elements-blocks/variations';
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index b6aa1f6bfbc..30e4de9b0c5 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -3204,7 +3204,7 @@ EOT;
<!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
<!-- /wp:woocommerce/product-image -->
-<!-- wp:post-title {"textAlign":"center","isLink":true,"style":{"spacing":{"margin":{"bottom":"0.75rem","top":"0"}},"typography":{"lineHeight":"1.4"}},"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-collection/product-title"} /-->
+<!-- wp:post-title {"textAlign":"center","isLink":true,"style":{"spacing":{"margin":{"bottom":"0.75rem","top":"0"}},"typography":{"lineHeight":"1.4"}},"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-title"} /-->
<!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small"} /-->
diff --git a/plugins/woocommerce/patterns/related-products.php b/plugins/woocommerce/patterns/related-products.php
index 5bdef6a1178..fa3a8919e97 100644
--- a/plugins/woocommerce/patterns/related-products.php
+++ b/plugins/woocommerce/patterns/related-products.php
@@ -24,7 +24,7 @@
<!-- wp:woocommerce/product-image {"showSaleBadge":false,"imageSizing":"thumbnail"} -->
<!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
<!-- /wp:woocommerce/product-image -->
- <!-- wp:post-title {"textAlign":"center","level":3,"isLink":true,"style":{"spacing":{"margin":{"bottom":"0.75rem","top":"0"}}},"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-collection/product-title"} /-->
+ <!-- wp:post-title {"textAlign":"center","level":3,"isLink":true,"style":{"spacing":{"margin":{"bottom":"0.75rem","top":"0"}}},"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-title"} /-->
<!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small"} /-->
<!-- wp:woocommerce/product-button {"textAlign":"center","fontSize":"small"} /-->
<!-- /wp:woocommerce/product-template -->
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Controller.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Controller.php
index 9b7ee4f01fe..9f965dbc9b5 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Controller.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection/Controller.php
@@ -96,12 +96,22 @@ class Controller extends AbstractBlock {
* @return string Modified block content with added interactivity.
*/
public function add_product_title_click_event_directives( $block_content, $block, $instance ) {
- $namespace = $instance->attributes['__woocommerceNamespace'] ?? '';
- $is_product_title_block = 'woocommerce/product-collection/product-title' === $namespace;
- $is_link = $instance->attributes['isLink'] ?? false;
+ $namespace = $instance->attributes['__woocommerceNamespace'] ?? '';
+ $is_product_collection_inner_block = $instance->context['isProductCollectionInnerBlock'] ?? false;
+ $is_product_title_block = in_array(
+ $namespace,
+ array(
+ 'woocommerce/product-title',
+ 'woocommerce/product-query/product-title',
+ 'woocommerce/product-collection/product-title',
+ ),
+ true
+ );
+
+ $is_link = $instance->attributes['isLink'] ?? false;
- // Only proceed if the block is a Product Title (Post Title variation) block.
- if ( $is_product_title_block && $is_link ) {
+ // Only proceed if the block is a Product Title (Post Title variation) block and is part of a Product Collection inner block.
+ if ( $is_product_title_block && $is_link && $is_product_collection_inner_block ) {
$p = new \WP_HTML_Tag_Processor( $block_content );
$p->next_tag( array( 'class_name' => 'wp-block-post-title' ) );
$is_anchor = $p->next_tag( array( 'tag_name' => 'a' ) );
diff --git a/plugins/woocommerce/templates/templates/blockified/archive-product.html b/plugins/woocommerce/templates/templates/blockified/archive-product.html
index 7f3d07e19d8..90c0c1f91fe 100644
--- a/plugins/woocommerce/templates/templates/blockified/archive-product.html
+++ b/plugins/woocommerce/templates/templates/blockified/archive-product.html
@@ -22,7 +22,7 @@
<!-- wp:woocommerce/product-image {"showSaleBadge":false,"imageSizing":"thumbnail"} -->
<!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
<!-- /wp:woocommerce/product-image -->
- <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-collection/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
+ <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
<!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
<!-- wp:woocommerce/product-button {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
<!-- /wp:woocommerce/product-template -->
diff --git a/plugins/woocommerce/templates/templates/blockified/product-search-results.html b/plugins/woocommerce/templates/templates/blockified/product-search-results.html
index d77d935d5e3..1f83b855707 100644
--- a/plugins/woocommerce/templates/templates/blockified/product-search-results.html
+++ b/plugins/woocommerce/templates/templates/blockified/product-search-results.html
@@ -20,7 +20,7 @@
<!-- wp:woocommerce/product-image {"showSaleBadge":false,"imageSizing":"thumbnail"} -->
<!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
<!-- /wp:woocommerce/product-image -->
- <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-collection/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
+ <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
<!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
<!-- wp:woocommerce/product-button {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
<!-- /wp:woocommerce/product-template -->
diff --git a/plugins/woocommerce/templates/templates/blockified/single-product.html b/plugins/woocommerce/templates/templates/blockified/single-product.html
index d448e93ca26..f5f2989cb6b 100644
--- a/plugins/woocommerce/templates/templates/blockified/single-product.html
+++ b/plugins/woocommerce/templates/templates/blockified/single-product.html
@@ -15,13 +15,13 @@
<!-- wp:column -->
<div class="wp-block-column">
- <!-- wp:post-title {"level": 1, "__woocommerceNamespace":"woocommerce/product-query/product-title"} /-->
+ <!-- wp:post-title {"level": 1, "__woocommerceNamespace":"woocommerce/product-title"} /-->
<!-- wp:woocommerce/product-rating /-->
<!-- wp:woocommerce/product-price {"fontSize":"large"} /-->
- <!-- wp:post-excerpt {"__woocommerceNamespace":"woocommerce/product-query/product-summary", "excerptLength":100} /-->
+ <!-- wp:post-excerpt {"__woocommerceNamespace":"woocommerce/product-summary", "excerptLength":100} /-->
<!-- wp:woocommerce/add-to-cart-form /-->
@@ -50,4 +50,4 @@
</main>
<!-- /wp:group -->
-<!-- wp:template-part {"slug":"footer"} /-->
+<!-- wp:template-part {"slug":"footer"} /-->
\ No newline at end of file
diff --git a/plugins/woocommerce/templates/templates/blockified/taxonomy-product_attribute.html b/plugins/woocommerce/templates/templates/blockified/taxonomy-product_attribute.html
index d7907acdc4e..fd6255c6591 100644
--- a/plugins/woocommerce/templates/templates/blockified/taxonomy-product_attribute.html
+++ b/plugins/woocommerce/templates/templates/blockified/taxonomy-product_attribute.html
@@ -24,7 +24,7 @@
<!-- wp:woocommerce/product-image {"showSaleBadge":false,"imageSizing":"thumbnail"} -->
<!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
<!-- /wp:woocommerce/product-image -->
- <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-collection/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
+ <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
<!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
<!-- wp:woocommerce/product-button {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
<!-- /wp:woocommerce/product-template -->
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/ControllerTest.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/ControllerTest.php
index a2ab3fd2141..5364fb54dde 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/ControllerTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductCollection/ControllerTest.php
@@ -6,6 +6,7 @@ namespace Automattic\WooCommerce\Tests\Blocks\BlockTypes\ProductCollection;
use Automattic\WooCommerce\Blocks\Assets\Api;
use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Controller;
+use Automattic\WooCommerce\Blocks\BlockTypes\ProductCollection\Renderer;
use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Tests\Blocks\Mocks\AssetDataRegistryMock;
@@ -35,17 +36,10 @@ class ControllerTest extends WC_Unit_Test_Case {
$this->sut = new class( $asset_api, $asset_data_registry, $integration_registry ) extends Controller {
/**
- * Skip normal hook registration for unit tests.
+ * Initialize the renderer without registering the block again.
*/
- protected function initialize() {
- $this->renderer = new class() {
- /**
- * Accept parsed block data from the controller under test.
- *
- * @param array $parsed_block The parsed block.
- */
- public function set_parsed_block( array $parsed_block ): void {}
- };
+ protected function initialize(): void {
+ $this->renderer = new Renderer();
}
};
@@ -150,20 +144,34 @@ class ControllerTest extends WC_Unit_Test_Case {
}
/**
- * Create a real Post Title block instance for the public render filter.
+ * Create a Post Title block with context from rendering its parent Product Collection.
*
* @param array $attributes Block attributes.
* @return \WP_Block
*/
private function create_post_title_block( array $attributes ): \WP_Block {
- return new \WP_Block(
+ $product_collection = new \WP_Block(
array(
- 'blockName' => 'core/post-title',
- 'attrs' => $attributes,
- 'innerBlocks' => array(),
+ 'blockName' => 'woocommerce/product-collection',
+ 'attrs' => array(),
+ 'innerBlocks' => array(
+ array(
+ 'blockName' => 'core/post-title',
+ 'attrs' => $attributes,
+ 'innerBlocks' => array(),
+ 'innerHTML' => '',
+ 'innerContent' => array(),
+ ),
+ ),
'innerHTML' => '',
- 'innerContent' => array(),
+ 'innerContent' => array( null ),
)
);
+ $product_collection->render( array( 'dynamic' => false ) );
+
+ /** @var \WP_Block $instance */
+ $instance = $product_collection->inner_blocks[0];
+
+ return $instance;
}
}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/SingleProduct.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/SingleProduct.php
index 5f32b925fa2..32f1ed9459d 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/SingleProduct.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/SingleProduct.php
@@ -268,7 +268,7 @@ class SingleProduct extends \WP_UnitTestCase {
<!-- wp:column -->
<div class="wp-block-column">
-<!-- wp:post-title {"isLink":true,"__woocommerceNamespace":"woocommerce/product-query/product-title"} /-->
+<!-- wp:post-title {"isLink":true,"__woocommerceNamespace":"woocommerce/product-title"} /-->
<!-- wp:woocommerce/product-rating /-->
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php b/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php
index 343d033f3a8..95f543d92b2 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php
@@ -433,13 +433,13 @@ class SingleProductTemplateTests extends WP_UnitTestCase {
<!-- wp:column -->
<div class="wp-block-column">
- <!-- wp:post-title {"level": 1, "__woocommerceNamespace":"woocommerce/product-query/product-title"} /-->
+ <!-- wp:post-title {"level": 1, "__woocommerceNamespace":"woocommerce/product-title"} /-->
<!-- wp:woocommerce/product-rating /-->
<!-- wp:woocommerce/product-price {"fontSize":"large"} /-->
- <!-- wp:post-excerpt {"__woocommerceNamespace":"woocommerce/product-query/product-summary", "excerptLength":100} /-->
+ <!-- wp:post-excerpt {"__woocommerceNamespace":"woocommerce/product-summary", "excerptLength":100} /-->
<!-- wp:woocommerce/add-to-cart-form /-->