Commit d101eb0cbc for woocommerce
commit d101eb0cbcd84bc718e7a17207b817ddfaa4a7b8
Author: Abdullah Ayman <63800091+AbdullahAymanMSRE@users.noreply.github.com>
Date: Mon Jan 12 06:25:10 2026 +0200
Update Product Details block to use WordPress core Accordion in WP 6.9+ (#62339)
* Update Product Details block to use WordPress core Accordion in WP 6.9+
Fixes #62120
- Add version detection for WordPress 6.9+
- Update block template to use core/accordion blocks when available
- Maintain backward compatibility with WooCommerce accordion for older WP versions
- Update PHP rendering to conditionally use appropriate block names"
* Add changefile(s) from automation for the following project(s): woocommerce
* Address code review feedback
- Fix PHP linting issues and formatting
- Remove unintended whitespace changes
- Use already existing utils for checking wp version
* Address code review feedback
- Revert pnpm-lock to the original content
* Fix PHP linting issue by adding missing newline at end of file in ProductDetails class
* Fix tests for Product Details block
* Refactor Product Details block to use div elements for accordion items and update test cases accordingly
- Change accordion item structure from <details> to <div> for better accessibility and styling.
- Update related test cases to reflect the new structure and ensure correct visibility of additional information.
- Fix PHP linting issues and improve code formatting.
- Fix client e2e tests.
* Update e2e tests for Add to Cart + Options Block to handle WordPress version differences
- Ensure backward compatibility with earlier versions by adjusting the selector logic.
* Remove core/ prefix from accordion block markup to follow WordPress conventions.
* Add wpCoreVersion to e2e tests for Add to Cart + Options Block
* fix: using anchor block instead of WP version to inject hooked block
* chore: phpcs
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Tung Du <dinhtungdu@gmail.com>
diff --git a/plugins/woocommerce/changelog/62339-fix-product-details-use-core-accordion b/plugins/woocommerce/changelog/62339-fix-product-details-use-core-accordion
new file mode 100644
index 0000000000..8ff39aa6f7
--- /dev/null
+++ b/plugins/woocommerce/changelog/62339-fix-product-details-use-core-accordion
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Use WordPress core Accordion block in Product Details block for WP 6.9+, with fallback for older versions.
\ No newline at end of file
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-details/utils.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-details/utils.ts
index 470c94bfc5..0c2a5f57d8 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-details/utils.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-details/utils.ts
@@ -2,9 +2,30 @@
* External dependencies
*/
import { PartialProduct, ProductDimensions } from '@woocommerce/data';
+import { isWpVersion } from '@woocommerce/settings';
import { isEmpty } from '@woocommerce/types';
import { __ } from '@wordpress/i18n';
+/**
+ * Get accordion block names based on WordPress version
+ */
+const getAccordionBlockNames = () => {
+ if ( isWpVersion( '6.9', '>=' ) ) {
+ return {
+ group: 'core/accordion',
+ item: 'core/accordion-item',
+ header: 'core/accordion-heading',
+ panel: 'core/accordion-panel',
+ };
+ }
+ return {
+ group: 'woocommerce/accordion-group',
+ item: 'woocommerce/accordion-item',
+ header: 'woocommerce/accordion-header',
+ panel: 'woocommerce/accordion-panel',
+ };
+};
+
export const isAdditionalProductDataEmpty = (
product: PartialProduct
): boolean => {
@@ -16,7 +37,6 @@ export const isAdditionalProductDataEmpty = (
)
);
};
-
return (
isEmpty( product.weight ) &&
isDimensionsEmpty( product.dimensions ) &&
@@ -36,9 +56,11 @@ export const getTemplate = (
isAdditionalProductDataEmpty( product ) &&
isInnerBlockOfSingleProductBlock;
+ const blockNames = getAccordionBlockNames();
+
return [
[
- 'woocommerce/accordion-group',
+ blockNames.group,
{
metadata: {
isDescendantOfProductDetails: true,
@@ -46,18 +68,18 @@ export const getTemplate = (
},
[
[
- 'woocommerce/accordion-item',
+ blockNames.item,
{
openByDefault: true,
},
[
[
- 'woocommerce/accordion-header',
+ blockNames.header,
{ title: __( 'Description', 'woocommerce' ) },
[],
],
[
- 'woocommerce/accordion-panel',
+ blockNames.panel,
{},
[ [ 'woocommerce/product-description', {}, [] ] ],
],
@@ -66,11 +88,11 @@ export const getTemplate = (
...( ! additionalProductDataEmpty
? [
[
- 'woocommerce/accordion-item',
+ blockNames.item,
{},
[
[
- 'woocommerce/accordion-header',
+ blockNames.header,
{
title: __(
'Additional Information',
@@ -80,7 +102,7 @@ export const getTemplate = (
[],
],
[
- 'woocommerce/accordion-panel',
+ blockNames.panel,
{},
[
[
@@ -94,16 +116,16 @@ export const getTemplate = (
]
: [] ),
[
- 'woocommerce/accordion-item',
+ blockNames.item,
{},
[
[
- 'woocommerce/accordion-header',
+ blockNames.header,
{ title: __( 'Reviews', 'woocommerce' ) },
[],
],
[
- 'woocommerce/accordion-panel',
+ blockNames.panel,
{},
[ [ 'woocommerce/product-reviews', {} ] ],
],
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
index 539e289c90..ca07f623af 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
@@ -91,6 +91,7 @@ test.describe( 'Add to Cart + Options Block', () => {
pageObject,
productGalleryPageObject,
editor,
+ wpCoreVersion,
} ) => {
const variationDescription =
'This is the output of the variation description';
@@ -169,6 +170,16 @@ test.describe( 'Add to Cart + Options Block', () => {
.first();
const quantitySelector = page.getByLabel( 'Product quantity' );
+ const additionalInfoPanel =
+ wpCoreVersion >= 6.9
+ ? page
+ .getByRole( 'button', {
+ name: 'Additional Information',
+ } )
+ .locator( '../..' )
+ .locator( '.wp-block-accordion-panel' )
+ : page.getByLabel( 'Additional Information', { exact: true } );
+
await test.step( 'displays an error when attributes are not selected', async () => {
await addToCartButton.click();
@@ -190,9 +201,7 @@ test.describe( 'Add to Cart + Options Block', () => {
await expect( quantitySelector ).toBeVisible();
await expect( page.getByText( 'SKU: woo-hoodie' ) ).toBeVisible();
await expect(
- page
- .getByLabel( 'Additional Information', { exact: true } )
- .getByText( '1.5 lbs' )
+ additionalInfoPanel.getByText( '1.5 lbs' )
).toBeVisible();
await expect( page.getByText( variationDescription ) ).toBeHidden();
const visibleImage =
@@ -210,9 +219,7 @@ test.describe( 'Add to Cart + Options Block', () => {
page.getByText( 'SKU: woo-hoodie-blue' )
).toBeVisible();
await expect(
- page
- .getByLabel( 'Additional Information', { exact: true } )
- .getByText( '2 lbs' )
+ additionalInfoPanel.getByText( '2 lbs' )
).toBeVisible();
await expect(
page.getByText( variationDescription )
@@ -233,9 +240,7 @@ test.describe( 'Add to Cart + Options Block', () => {
await expect( page.getByText( 'SKU: woo-hoodie' ) ).toBeVisible();
await expect( addToCartButton ).toHaveClass( /\bdisabled\b/ );
await expect(
- page
- .getByLabel( 'Additional Information', { exact: true } )
- .getByText( '1.5 lbs' )
+ additionalInfoPanel.getByText( '1.5 lbs' )
).toBeVisible();
await expect( page.getByText( variationDescription ) ).toBeHidden();
await expect( async () => {
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php
index 66600ce04e..f163398144 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php
@@ -1,4 +1,6 @@
-<?php declare( strict_types = 1 );
+<?php
+
+declare( strict_types = 1 );
namespace Automattic\WooCommerce\Blocks\BlockTypes;
@@ -140,13 +142,20 @@ class ProductDetails extends AbstractBlock {
$accordion_blocks = array();
+ $accordion_anchor_block = $this->get_accordion_anchor_block( $parsed_block );
+
+ if ( ! $accordion_anchor_block ) {
+ return $parsed_block;
+ }
+
foreach ( $product_tabs as $key => $tab ) {
ob_start();
call_user_func( $tab['callback'], $key, $tab );
$tab_content = ob_get_clean();
$accordion_blocks[] = $this->create_accordion_item_block(
$tab['title'],
- '<!-- wp:html -->' . $tab_content . '<!-- /wp:html -->'
+ '<!-- wp:html -->' . $tab_content . '<!-- /wp:html -->',
+ $accordion_anchor_block
);
}
@@ -158,26 +167,48 @@ class ProductDetails extends AbstractBlock {
*
* @param string $title Title of the accordion item.
* @param string $content Content of the accordion item as block markup.
+ * @param array $anchor_block Accordion anchor block to determine which item block to create.
*
* @return array Accordion item.
*/
- private function create_accordion_item_block( $title, $content ) {
- $template = '<!-- wp:woocommerce/accordion-item -->
- <div class="wp-block-woocommerce-accordion-item"><!-- wp:woocommerce/accordion-header -->
- <h3 class="wp-block-woocommerce-accordion-header accordion-item__heading">
- <button class="accordion-item__toggle">
- <span>%1$s</span>
- <span class="accordion-item__toggle-icon has-icon-plus" style="width:1.2em;height:1.2em"><svg width="1.2em" height="1.2em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z" fill="currentColor"></path></svg></span>
- </button>
- </h3>
- <!-- /wp:woocommerce/accordion-header -->
-
- <!-- wp:woocommerce/accordion-panel -->
- <div class="wp-block-woocommerce-accordion-panel"><div class="accordion-content__wrapper">
- %2$s
- </div></div>
- <!-- /wp:woocommerce/accordion-panel --></div>
- <!-- /wp:woocommerce/accordion-item -->';
+ private function create_accordion_item_block( $title, $content, $anchor_block ) {
+ if ( isset( $anchor_block['blockName'] ) && 'core/accordion' === $anchor_block['blockName'] ) {
+ $template = '<!-- wp:accordion-item -->
+ <div class="wp-block-accordion-item">
+ <!-- wp:accordion-heading -->
+ <h3 class="wp-block-accordion-heading">
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title">%1$s</span>
+ <span class="wp-block-accordion-heading__toggle-icon" aria-hidden="true">+</span>
+ </button>
+ </h3>
+ <!-- /wp:accordion-heading -->
+
+ <!-- wp:accordion-panel -->
+ <div class="wp-block-accordion-panel">
+ %2$s
+ </div>
+ <!-- /wp:accordion-panel -->
+ </div>
+ <!-- /wp:accordion-item -->';
+ } else {
+ $template = '<!-- wp:woocommerce/accordion-item -->
+ <div class="wp-block-woocommerce-accordion-item"><!-- wp:woocommerce/accordion-header -->
+ <h3 class="wp-block-woocommerce-accordion-header accordion-item__heading">
+ <button class="accordion-item__toggle">
+ <span>%1$s</span>
+ <span class="accordion-item__toggle-icon has-icon-plus" style="width:1.2em;height:1.2em"><svg width="1.2em" height="1.2em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z" fill="currentColor"></path></svg></span>
+ </button>
+ </h3>
+ <!-- /wp:woocommerce/accordion-header -->
+
+ <!-- wp:woocommerce/accordion-panel -->
+ <div class="wp-block-woocommerce-accordion-panel"><div class="accordion-content__wrapper">
+ %2$s
+ </div></div>
+ <!-- /wp:woocommerce/accordion-panel --></div>
+ <!-- /wp:woocommerce/accordion-item -->';
+ }
return parse_blocks( sprintf( $template, $title, $content ) )[0];
}
@@ -191,7 +222,7 @@ class ProductDetails extends AbstractBlock {
* @return array Parsed block.
*/
private function inject_parsed_accordion_blocks( $parsed_block, $accordion_blocks ) {
- if ( 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
+ if ( 'core/accordion' === $parsed_block['blockName'] || 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
$parsed_block['innerBlocks'] = array_merge( $parsed_block['innerBlocks'], $accordion_blocks );
$parsed_block['innerBlocks'] = array_values( array_filter( $parsed_block['innerBlocks'] ) );
$opening_tag = reset( $parsed_block['innerContent'] );
@@ -224,7 +255,7 @@ class ProductDetails extends AbstractBlock {
return $parsed_block;
}
- if ( 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
+ if ( 'core/accordion' === $parsed_block['blockName'] || 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
foreach ( $parsed_block['innerBlocks'] as $key => $inner_block ) {
$parsed_block['innerBlocks'][ $key ] = $this->mark_accordion_item_hidden( $inner_block, $context );
}
@@ -280,7 +311,7 @@ class ProductDetails extends AbstractBlock {
* @return bool True if the block has an accordion, false otherwise.
*/
private function has_accordion( $parsed_block ) {
- if ( 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
+ if ( 'core/accordion' === $parsed_block['blockName'] || 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
return true;
}
@@ -293,6 +324,28 @@ class ProductDetails extends AbstractBlock {
return false;
}
+ /**
+ * Get the first accordion anchor block in a parsed block.
+ *
+ * @param array $parsed_block Parsed block.
+ *
+ * @return array|null Accordion anchor block or null.
+ */
+ private function get_accordion_anchor_block( $parsed_block ) {
+ if ( 'core/accordion' === $parsed_block['blockName'] || 'woocommerce/accordion-group' === $parsed_block['blockName'] ) {
+ return $parsed_block;
+ }
+
+ foreach ( $parsed_block['innerBlocks'] as $inner_block ) {
+ $anchor_block = $this->get_accordion_anchor_block( $inner_block );
+ if ( $anchor_block ) {
+ return $anchor_block;
+ }
+ }
+
+ return null;
+ }
+
/**
* Validate hooked blocks data. Remove duplicated entries with the same title
* and invalid entries with invalid content. Log errors to the WC logger.
@@ -310,10 +363,10 @@ class ProductDetails extends AbstractBlock {
foreach ( $hooked_blocks as $block ) {
$invalid = ! is_array( $block ) ||
- ! isset( $block['title'] ) ||
- ! isset( $block['content'] ) ||
- ! is_string( $block['title'] ) ||
- ! is_string( $block['content'] );
+ ! isset( $block['title'] ) ||
+ ! isset( $block['content'] ) ||
+ ! is_string( $block['title'] ) ||
+ ! is_string( $block['content'] );
if ( ! $invalid ) {
$parsed_content = parse_blocks( $block['content'] );
@@ -361,7 +414,7 @@ class ProductDetails extends AbstractBlock {
'hooked_block_types',
function ( $hooked_block_types, $relative_position, $anchor_block_type ) use ( $slug ) {
if (
- 'woocommerce/accordion-group' === $anchor_block_type &&
+ ( 'core/accordion' === $anchor_block_type || 'woocommerce/accordion-group' === $anchor_block_type ) &&
'last_child' === $relative_position &&
! in_array( $slug, $hooked_block_types, true )
) {
@@ -378,14 +431,14 @@ class ProductDetails extends AbstractBlock {
function ( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block ) use ( $block ) {
if (
is_null( $parsed_hooked_block ) ||
- 'woocommerce/accordion-group' !== $parsed_anchor_block['blockName'] ||
+ ( 'core/accordion' !== $parsed_anchor_block['blockName'] && 'woocommerce/accordion-group' !== $parsed_anchor_block['blockName'] ) ||
'last_child' !== $relative_position ||
empty( $parsed_anchor_block['attrs']['metadata']['isDescendantOfProductDetails'] )
) {
return null;
}
- return $this->create_accordion_item_block( $block['title'], $block['content'] );
+ return $this->create_accordion_item_block( $block['title'], $block['content'], $parsed_anchor_block );
},
10,
4
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/ProductDetails.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/ProductDetails.php
index dbdf976666..1a009d5eb6 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/ProductDetails.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/ProductDetails.php
@@ -97,12 +97,33 @@ class ProductDetails extends \WP_UnitTestCase {
$this->assertEquals( $serialized_blocks_without_whitespace, $expected_serialized_blocks_without_whitespace, '' );
}
+ /**
+ * @return array
+ */
+ public function render_with_hook_provider() {
+ return array(
+ 'woocommerce_accordion' => array(
+ 'template.html',
+ 'render_with_hook_expected_result.html',
+ ),
+ 'core_accordion' => array(
+ 'template_wp69.html',
+ 'render_with_hook_expected_result_wp69.html',
+ ),
+ );
+ }
+
/**
* Test Product Details render function when `woocommerce_product_tabs` hook is used.
* IMPORTANT: The current test doesn't validate the entire HTML, but only the text content inside the HTML.
* This is because some ids are generated dynamically via wp_unique_id that it is not straightforward to mock.
+ *
+ * @dataProvider render_with_hook_provider
+ *
+ * @param string $template_file Template file.
+ * @param string $expected_file Expected result file.
*/
- public function test_product_details_render_with_hook() {
+ public function test_product_details_render_with_hook( $template_file, $expected_file ) {
add_filter(
'woocommerce_product_tabs',
function ( $tabs ) {
@@ -127,11 +148,11 @@ class ProductDetails extends \WP_UnitTestCase {
}
);
- $template = file_get_contents( __DIR__ . '/template.html' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+ $template = file_get_contents( __DIR__ . '/' . $template_file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$serialized_blocks = do_blocks( $template );
- $expected_serialized_blocks = file_get_contents( __DIR__ . '/render_with_hook_expected_result.html' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+ $expected_serialized_blocks = file_get_contents( __DIR__ . '/' . $expected_file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$serialized_blocks_without_whitespace = wp_strip_all_tags( $serialized_blocks, true );
$expected_serialized_blocks_without_whitespace = wp_strip_all_tags( $expected_serialized_blocks, true );
@@ -139,12 +160,39 @@ class ProductDetails extends \WP_UnitTestCase {
$this->assertEquals( $serialized_blocks_without_whitespace, $expected_serialized_blocks_without_whitespace, '' );
}
+ /**
+ * @return array
+ */
+ public function hooked_blocks_provider() {
+ return array(
+ 'woocommerce_accordion' => array(
+ 'woocommerce/accordion-group',
+ 'woocommerce/accordion-item',
+ 'woocommerce/accordion-header',
+ 'woocommerce/accordion-panel',
+ ),
+ 'core_accordion' => array(
+ 'core/accordion',
+ 'core/accordion-item',
+ 'core/accordion-heading',
+ 'core/accordion-panel',
+ ),
+ );
+ }
+
/**
* Test the `woocommerce_product_details_hooked_blocks` hook. This hook allows developers to
* specify a title and block markup that will be automatically wrapped in the required
* Accordion Item block and appended to the Product Details' Accordion Group block.
+ *
+ * @dataProvider hooked_blocks_provider
+ *
+ * @param string $accordion_group_name Accordion group block name.
+ * @param string $accordion_item_name Accordion item block name.
+ * @param string $accordion_header_name Accordion header block name.
+ * @param string $accordion_panel_name Accordion panel block name.
*/
- public function test_hooked_blocks() {
+ public function test_hooked_blocks( $accordion_group_name, $accordion_item_name, $accordion_header_name, $accordion_panel_name ) {
$test_block = array(
'slug' => 'custom-info',
'title' => 'Custom Info',
@@ -165,7 +213,7 @@ class ProductDetails extends \WP_UnitTestCase {
// We pretend that we're in the `last_child` position of the `woocommerce/accordion-group` block.
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment -- test code.
- $hooked_block_types = apply_filters( 'hooked_block_types', array(), 'last_child', 'woocommerce/accordion-group', null );
+ $hooked_block_types = apply_filters( 'hooked_block_types', array(), 'last_child', $accordion_group_name, null );
$this->assertSame( array( $test_block['slug'] ), $hooked_block_types );
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment -- test code.
@@ -180,7 +228,7 @@ class ProductDetails extends \WP_UnitTestCase {
$test_block['slug'],
'last_child',
array(
- 'blockName' => 'woocommerce/accordion-group',
+ 'blockName' => $accordion_group_name,
'attrs' => array(
'metadata' => array(
'isDescendantOfProductDetails' => true,
@@ -191,13 +239,13 @@ class ProductDetails extends \WP_UnitTestCase {
), // $parsed_anchor_block
null
);
- $this->assertSame( 'woocommerce/accordion-item', $hooked_block_custom_info['blockName'] );
+ $this->assertSame( $accordion_item_name, $hooked_block_custom_info['blockName'] );
$this->assertCount( 2, $hooked_block_custom_info['innerBlocks'] );
- $this->assertSame( 'woocommerce/accordion-header', $hooked_block_custom_info['innerBlocks'][0]['blockName'] );
+ $this->assertSame( $accordion_header_name, $hooked_block_custom_info['innerBlocks'][0]['blockName'] );
$this->assertStringContainsString( $test_block['title'], $hooked_block_custom_info['innerBlocks'][0]['innerHTML'] );
- $this->assertSame( 'woocommerce/accordion-panel', $hooked_block_custom_info['innerBlocks'][1]['blockName'] );
+ $this->assertSame( $accordion_panel_name, $hooked_block_custom_info['innerBlocks'][1]['blockName'] );
$this->assertSame( parse_blocks( $test_block['content'] ), $hooked_block_custom_info['innerBlocks'][1]['innerBlocks'] );
}
}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/render_with_hook_expected_result_wp69.html b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/render_with_hook_expected_result_wp69.html
new file mode 100644
index 0000000000..3ee08c53be
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/render_with_hook_expected_result_wp69.html
@@ -0,0 +1,145 @@
+<div
+ data-block-name="woocommerce/product-details"
+ class="wp-block-woocommerce-product-details"
+>
+ <div
+ data-block-name="core/accordion"
+ class="wp-block-accordion is-layout-flow wp-block-accordion-is-layout-flow"
+ >
+ <div
+ data-block-name="core/accordion-item"
+ class="wp-block-accordion-item is-layout-flow wp-block-accordion-item-is-layout-flow"
+ >
+ <h3
+ data-block-name="core/accordion-heading"
+ class="wp-block-accordion-heading"
+ >
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Description Header</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <div
+ data-block-name="core/accordion-panel"
+ class="wp-block-accordion-panel is-layout-flow wp-block-accordion-panel-is-layout-flow"
+ >
+ <p>Description Body</p>
+ </div>
+ </div>
+
+ <div
+ data-block-name="core/accordion-item"
+ class="wp-block-accordion-item is-layout-flow wp-block-accordion-item-is-layout-flow"
+ >
+ <h3
+ data-block-name="core/accordion-heading"
+ class="wp-block-accordion-heading"
+ >
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Additional Information Header</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <div
+ data-block-name="core/accordion-panel"
+ class="wp-block-accordion-panel is-layout-flow wp-block-accordion-panel-is-layout-flow"
+ >
+ <p>Additional Information Body</p>
+ </div>
+ </div>
+
+ <div
+ data-block-name="core/accordion-item"
+ class="wp-block-accordion-item is-layout-flow wp-block-accordion-item-is-layout-flow"
+ >
+ <h3
+ data-block-name="core/accordion-heading"
+ class="wp-block-accordion-heading"
+ >
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Reviews Header</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <div
+ data-block-name="core/accordion-panel"
+ class="wp-block-accordion-panel is-layout-flow wp-block-accordion-panel-is-layout-flow"
+ >
+ <p>Reviews Body</p>
+ </div>
+ </div>
+
+ <div
+ data-block-name="core/accordion-item"
+ class="wp-block-accordion-item is-layout-flow wp-block-accordion-item-is-layout-flow"
+ >
+ <h3
+ data-block-name="core/accordion-heading"
+ class="wp-block-accordion-heading"
+ >
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Custom Info</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <div
+ data-block-name="core/accordion-panel"
+ class="wp-block-accordion-panel is-layout-flow wp-block-accordion-panel-is-layout-flow"
+ >
+ <p>This is the content for the custom info tab.</p>
+ </div>
+ </div>
+
+ <div
+ data-block-name="core/accordion-item"
+ class="wp-block-accordion-item is-layout-flow wp-block-accordion-item-is-layout-flow"
+ >
+ <h3
+ data-block-name="core/accordion-heading"
+ class="wp-block-accordion-heading"
+ >
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Specifications</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <div
+ data-block-name="core/accordion-panel"
+ class="wp-block-accordion-panel is-layout-flow wp-block-accordion-panel-is-layout-flow"
+ >
+ <h2>Specifications</h2>
+ <p>Here you can list product specifications.</p>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/template_wp69.html b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/template_wp69.html
new file mode 100644
index 0000000000..e4ef9b673b
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductDetails/template_wp69.html
@@ -0,0 +1,83 @@
+<!-- wp:woocommerce/product-details -->
+<div class="wp-block-woocommerce-product-details">
+ <!-- wp:accordion {"autoclose":false} -->
+ <div class="wp-block-accordion">
+ <!-- wp:accordion-item {"openByDefault": false} -->
+ <div class="wp-block-accordion-item">
+ <!-- wp:accordion-heading -->
+ <h3 class="wp-block-accordion-heading">
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Description Header</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <!-- /wp:accordion-heading -->
+ <!-- wp:accordion-panel -->
+ <div class="wp-block-accordion-panel">
+ <!-- wp:paragraph -->
+ <p>Description Body</p>
+ <!-- /wp:paragraph -->
+ </div>
+ <!-- /wp:accordion-panel -->
+ </div>
+ <!-- /wp:accordion-item -->
+ <!-- wp:accordion-item {"openByDefault": false} -->
+ <div class="wp-block-accordion-item">
+ <!-- wp:accordion-heading -->
+ <h3 class="wp-block-accordion-heading">
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Additional Information Header</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <!-- /wp:accordion-heading -->
+ <!-- wp:accordion-panel -->
+ <div class="wp-block-accordion-panel">
+ <!-- wp:paragraph -->
+ <p>Additional Information Body</p>
+ <!-- /wp:paragraph -->
+ </div>
+ <!-- /wp:accordion-panel -->
+ </div>
+ <!-- /wp:accordion-item -->
+ <!-- wp:accordion-item {"openByDefault": false} -->
+ <div class="wp-block-accordion-item">
+ <!-- wp:accordion-heading -->
+ <h3 class="wp-block-accordion-heading">
+ <button class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title"
+ >Reviews Header</span
+ >
+ <span
+ class="wp-block-accordion-heading__toggle-icon"
+ aria-hidden="true"
+ >+</span
+ >
+ </button>
+ </h3>
+ <!-- /wp:accordion-heading -->
+ <!-- wp:accordion-panel {"isSelected":true} -->
+ <div class="wp-block-accordion-panel">
+ <!-- wp:paragraph -->
+ <p>Reviews Body</p>
+ <!-- /wp:paragraph -->
+ </div>
+ <!-- /wp:accordion-panel -->
+ </div>
+ <!-- /wp:accordion-item -->
+ </div>
+ <!-- /wp:accordion -->
+</div>
+<!-- /wp:woocommerce/product-details -->