string $slug The slug of the item. * @param array $block The block data. * @return void */ private function register_hooked_block( $slug, $block ) { add_filter( 'hooked_block_types', function ( $hooked_block_types, $relative_position, $anchor_block_type ) use ( $slug ) { if ( ( 'core/accordion' === $anchor_block_type || 'woocommerce/accordion-group' === $anchor_block_type ) && 'last_child' === $relative_position && ! in_array( $slug, $hooked_block_types, true ) ) { $hooked_block_types[] = $slug; } return $hooked_block_types; }, 10, 3 ); add_filter( "hooked_block_{$slug}", function ( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block ) use ( $block ) { if ( is_null( $parsed_hooked_block ) || ( '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'], $parsed_anchor_block ); }, 10, 4 ); } /** * Enqueue legacy assets when this block is used as we don't enqueue them for block themes anymore. * * @see https://github.com/woocommerce/woocommerce/pull/60223 */ public function enqueue_legacy_assets() { wp_enqueue_script( 'wc-single-product' ); } /** * Previously, the Product Details block was a standalone block. It doesn't * have any inner blocks and it rendered the tabs directly like the classic * template. When upgrading, we want the existing stores using the block to * continue working as before, so we moved the logic the legacy render * method here. * * @see https://github.com/woocommerce/woocommerce/pull/59005 * * @param array $attributes Block attributes. * @param string $content Block content. * @param WP_Block $block Block instance. * * @return string Rendered block output. */ protected function render_legacy_block( $attributes, $content, $block ) { if ( ! is_singular( 'product' ) ) { return $content; } add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_legacy_assets' ], 20 ); $hide_tab_title = isset( $attributes['hideTabTitle'] ) ? $attributes['hideTabTitle'] : false; if ( $hide_tab_title ) { add_filter( 'woocommerce_product_description_heading', '__return_empty_string' ); add_filter( 'woocommerce_product_additional_information_heading', '__return_empty_string' ); add_filter( 'woocommerce_reviews_title', '__return_empty_string' ); } $tabs = $this->render_tabs(); if ( $hide_tab_title ) { remove_filter( 'woocommerce_product_description_heading', '__return_empty_string' ); remove_filter( 'woocommerce_product_additional_information_heading', '__return_empty_string' ); remove_filter( 'woocommerce_reviews_title', '__return_empty_string' ); // Remove the first `h2` of every `.wc-tab`. This is required for the Reviews tabs when there are no reviews and for plugin tabs. $tabs_html = new WP_HTML_Tag_Processor( $tabs ); while ( $tabs_html->next_tag( array( 'class_name' => 'wc-tab' ) ) ) { if ( $tabs_html->next_tag( 'h2' ) ) { $tabs_html->set_attribute( 'hidden', 'true' ); } } $tabs = $tabs_html->get_updated_html(); } $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); return sprintf( '