diff --git a/plugins/woocommerce-blocks/assets/js/blocks/classic-template/README.md b/plugins/woocommerce-blocks/assets/js/blocks/classic-template/README.md index 71b1887cc69..e17e1a7525d 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/classic-template/README.md +++ b/plugins/woocommerce-blocks/assets/js/blocks/classic-template/README.md @@ -18,7 +18,7 @@ This block does not have any customizable options available, so any style or cus ### Props - `attributes` - - `template`: `single-product` | `archive-product` | `taxonomy-product_cat` | `taxonomy-product_tag` + - `template`: `single-product` | `archive-product` | `taxonomy-product_cat` | `taxonomy-product_tag` | `taxonomy-product_attribute` - `align`: `wide` | `full` ```html diff --git a/plugins/woocommerce-blocks/assets/js/blocks/classic-template/constants.ts b/plugins/woocommerce-blocks/assets/js/blocks/classic-template/constants.ts index dfeb37b3308..2e1252cf575 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/classic-template/constants.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/classic-template/constants.ts @@ -38,6 +38,13 @@ export const TEMPLATES: TemplateDetails = { ), placeholder: 'archive-product', }, + 'taxonomy-product_attribute': { + title: __( + 'WooCommerce Product Attribute Block', + 'woo-gutenberg-products-block' + ), + placeholder: 'archive-product', + }, 'product-search-results': { title: __( 'WooCommerce Product Search Results Block', diff --git a/plugins/woocommerce-blocks/assets/js/blocks/classic-template/test/utils.ts b/plugins/woocommerce-blocks/assets/js/blocks/classic-template/test/utils.ts index 8f413c21ab0..7f836ba9421 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/classic-template/test/utils.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/classic-template/test/utils.ts @@ -20,6 +20,10 @@ const TEMPLATES = { title: 'Product Taxonomy Title', placeholder: 'Product Taxonomy Placeholder', }, + 'taxonomy-product_attribute': { + title: 'Product Attribute Title', + placeholder: 'Product Attribute Placeholder', + }, }; describe( 'getTemplateDetailsBySlug', function () { diff --git a/plugins/woocommerce-blocks/src/BlockTemplatesController.php b/plugins/woocommerce-blocks/src/BlockTemplatesController.php index 5626068b3b8..a63278b7edc 100644 --- a/plugins/woocommerce-blocks/src/BlockTemplatesController.php +++ b/plugins/woocommerce-blocks/src/BlockTemplatesController.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Blocks; use Automattic\WooCommerce\Blocks\Domain\Package; +use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; /** @@ -110,7 +111,7 @@ class BlockTemplatesController { list( $template_id, $template_slug ) = $template_name_parts; - // If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag.html template let's use the themes archive-product.html template. + // If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag/attribute.html template let's use the themes archive-product.html template. if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) { $template_path = BlockTemplateUtils::get_theme_template_path( 'archive-product' ); $template_object = BlockTemplateUtils::create_new_block_template_object( $template_path, $template_type, $template_slug, true ); @@ -330,7 +331,7 @@ class BlockTemplatesController { continue; } - // If the theme has an archive-product.html template, but not a taxonomy-product_cat.html template let's use the themes archive-product.html template. + // If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag/attribute.html template let's use the themes archive-product.html template. if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) { $template_file = BlockTemplateUtils::get_theme_template_path( 'archive-product' ); $templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true ); @@ -439,8 +440,8 @@ class BlockTemplatesController { } if ( isset( $queried_object->taxonomy ) && taxonomy_is_product_attribute( $queried_object->taxonomy ) && - ! BlockTemplateUtils::theme_has_template( 'archive-product' ) && - $this->block_template_is_available( 'archive-product' ) + ! BlockTemplateUtils::theme_has_template( ProductAttributeTemplate::SLUG ) && + $this->block_template_is_available( ProductAttributeTemplate::SLUG ) ) { add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 ); } diff --git a/plugins/woocommerce-blocks/src/BlockTypes/ClassicTemplate.php b/plugins/woocommerce-blocks/src/BlockTypes/ClassicTemplate.php index edb0903cb00..616ea349d9b 100644 --- a/plugins/woocommerce-blocks/src/BlockTypes/ClassicTemplate.php +++ b/plugins/woocommerce-blocks/src/BlockTypes/ClassicTemplate.php @@ -1,6 +1,7 @@ render_single_product(); diff --git a/plugins/woocommerce-blocks/src/Templates/ProductAttributeTemplate.php b/plugins/woocommerce-blocks/src/Templates/ProductAttributeTemplate.php index 566d0de89ab..35494fd93b2 100644 --- a/plugins/woocommerce-blocks/src/Templates/ProductAttributeTemplate.php +++ b/plugins/woocommerce-blocks/src/Templates/ProductAttributeTemplate.php @@ -8,7 +8,7 @@ namespace Automattic\WooCommerce\Blocks\Templates; * @internal */ class ProductAttributeTemplate { - const SLUG = 'archive-product'; + const SLUG = 'taxonomy-product_attribute'; /** * Constructor. @@ -25,14 +25,14 @@ class ProductAttributeTemplate { } /** - * Render the Archive Product Template for product attributes. + * Renders the Product by Attribute template for product attributes taxonomy pages. * * @param array $templates Templates that match the product attributes taxonomy. */ public function update_taxonomy_template_hierarchy( $templates ) { $queried_object = get_queried_object(); if ( taxonomy_is_product_attribute( $queried_object->taxonomy ) && wc_current_theme_is_fse_theme() ) { - array_unshift( $templates, self::SLUG ); + array_splice( $templates, count( $templates ) - 1, 0, self::SLUG ); } return $templates; diff --git a/plugins/woocommerce-blocks/src/Utils/BlockTemplateUtils.php b/plugins/woocommerce-blocks/src/Utils/BlockTemplateUtils.php index 0655fd27f76..b298a7d7a06 100644 --- a/plugins/woocommerce-blocks/src/Utils/BlockTemplateUtils.php +++ b/plugins/woocommerce-blocks/src/Utils/BlockTemplateUtils.php @@ -1,6 +1,7 @@ _x( 'Products by Tag', 'Template name', 'woo-gutenberg-products-block' ), 'description' => __( 'Displays products filtered by a tag.', 'woo-gutenberg-products-block' ), ), + ProductAttributeTemplate::SLUG => array( + 'title' => _x( 'Products by Attribute', 'Template name', 'woo-gutenberg-products-block' ), + 'description' => __( 'Displays products filtered by an attribute.', 'woo-gutenberg-products-block' ), + ), ProductSearchResultsTemplate::SLUG => array( 'title' => _x( 'Product Search Results', 'Template name', 'woo-gutenberg-products-block' ), 'description' => __( 'Displays search results for your store.', 'woo-gutenberg-products-block' ), @@ -449,14 +454,14 @@ class BlockTemplateUtils { /** * Checks if we can fallback to the `archive-product` template for a given slug * - * `taxonomy-product_cat` and `taxonomy-product_tag` templates can generally use the - * `archive-product` as a fallback if there are no specific overrides. + * `taxonomy-product_cat`, `taxonomy-product_tag`, `taxonomy-attribute` templates can + * generally use the `archive-product` as a fallback if there are no specific overrides. * * @param string $template_slug Slug to check for fallbacks. * @return boolean */ public static function template_is_eligible_for_product_archive_fallback( $template_slug ) { - $eligible_for_fallbacks = array( 'taxonomy-product_cat', 'taxonomy-product_tag' ); + $eligible_for_fallbacks = array( 'taxonomy-product_cat', 'taxonomy-product_tag', ProductAttributeTemplate::SLUG ); return in_array( $template_slug, $eligible_for_fallbacks, true ) && ! self::theme_has_template( $template_slug ) diff --git a/plugins/woocommerce-blocks/templates/templates/taxonomy-product_attribute.html b/plugins/woocommerce-blocks/templates/templates/taxonomy-product_attribute.html new file mode 100644 index 00000000000..7f3f66cd92d --- /dev/null +++ b/plugins/woocommerce-blocks/templates/templates/taxonomy-product_attribute.html @@ -0,0 +1,5 @@ + + +
+ + diff --git a/plugins/woocommerce-blocks/tests/e2e/fixtures/fixture-data.js b/plugins/woocommerce-blocks/tests/e2e/fixtures/fixture-data.js index 2fb4a4145ba..97194702bb5 100644 --- a/plugins/woocommerce-blocks/tests/e2e/fixtures/fixture-data.js +++ b/plugins/woocommerce-blocks/tests/e2e/fixtures/fixture-data.js @@ -26,7 +26,10 @@ const Attributes = () => [ ], }, { - attribute: { name: 'Shade' }, + attribute: { + name: 'Shade', + has_archives: true, + }, terms: [ { name: 'Red', diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/backend/site-editing-templates.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/backend/site-editing-templates.test.js index e0e4c15e042..948b0c41c35 100644 --- a/plugins/woocommerce-blocks/tests/e2e/specs/backend/site-editing-templates.test.js +++ b/plugins/woocommerce-blocks/tests/e2e/specs/backend/site-editing-templates.test.js @@ -93,6 +93,14 @@ const BLOCK_DATA = { }, name: 'woocommerce/legacy-template', }, + 'taxonomy-product_attribute': { + attributes: { + placeholder: 'archive-product', + template: 'taxonomy-product_attribute', + title: 'WooCommerce Product Attribute Block', + }, + name: 'woocommerce/legacy-template', + }, 'product-search-results': { attributes: { title: 'WooCommerce Product Search Results Block', @@ -516,6 +524,100 @@ describe( 'Store Editing Templates', () => { } ); } ); + describe( 'Products by Attribute template', () => { + it( 'default template from WooCommerce Blocks is available on an FSE theme', async () => { + const EXPECTED_TEMPLATE = defaultTemplateProps( + 'Products by Attribute' + ); + + await goToTemplatesList(); + + const templates = await getAllTemplates(); + + try { + expect( templates ).toContainEqual( EXPECTED_TEMPLATE ); + } catch ( ok ) { + // Depending on the speed of the execution and whether Chrome is headless or not + // the id might be parsed or not + + expect( templates ).toContainEqual( { + ...EXPECTED_TEMPLATE, + addedBy: WOOCOMMERCE_PARSED_ID, + } ); + } + } ); + + runOnlyWhenGutenbergIsDisabled( () => + it( 'should contain the "WooCommerce Product Taxonomy Block" classic template', async () => { + await goToTemplateEditor( { + postId: 'woocommerce/woocommerce//taxonomy-product_attribute', + } ); + + const [ classicBlock ] = await filterCurrentBlocks( + ( block ) => + block.name === + BLOCK_DATA[ 'taxonomy-product_attribute' ].name + ); + + expect( classicBlock.attributes.template ).toBe( + BLOCK_DATA[ 'taxonomy-product_attribute' ].attributes + .template + ); + expect( await getCurrentSiteEditorContent() ).toMatchSnapshot(); + } ) + ); + + it( 'should show the action menu if the template has been customized by the user', async () => { + const EXPECTED_TEMPLATE = { + ...defaultTemplateProps( 'Products by Attribute' ), + hasActions: true, + }; + + await visitTemplateAndAddCustomParagraph( + 'taxonomy-product_attribute' + ); + + await goToTemplatesList( { waitFor: 'actions' } ); + + const templates = await getAllTemplates(); + + try { + expect( templates ).toContainEqual( EXPECTED_TEMPLATE ); + } catch ( ok ) { + // Depending on the speed of the execution and whether Chrome is headless or not + // the id might be parsed or not + + expect( templates ).toContainEqual( { + ...EXPECTED_TEMPLATE, + addedBy: WOOCOMMERCE_PARSED_ID, + } ); + } + } ); + + it( 'should preserve and correctly show the user customization on the back-end', async () => { + await goToTemplateEditor( { + postId: 'woocommerce/woocommerce//taxonomy-product_attribute', + } ); + + await expect( canvas() ).toMatchElement( + SELECTORS.blocks.paragraph, + { + text: CUSTOMIZED_STRING, + timeout: DEFAULT_TIMEOUT, + } + ); + } ); + + it( 'should show the user customization on the front-end', async () => { + await page.goto( new URL( '/shade/red', BASE_URL ) ); + + await expect( page ).toMatchElement( 'p', { + text: CUSTOMIZED_STRING, + timeout: DEFAULT_TIMEOUT, + } ); + } ); + } ); + describe( 'Product Search Results block template', () => { it( 'default template from WooCommerce Blocks is available on an FSE theme', async () => { const EXPECTED_TEMPLATE = defaultTemplateProps( diff --git a/plugins/woocommerce-blocks/tests/mocks/theme-with-woo-templates/block-templates/taxonomy-product_attribute.html b/plugins/woocommerce-blocks/tests/mocks/theme-with-woo-templates/block-templates/taxonomy-product_attribute.html new file mode 100644 index 00000000000..7c0cb10777d --- /dev/null +++ b/plugins/woocommerce-blocks/tests/mocks/theme-with-woo-templates/block-templates/taxonomy-product_attribute.html @@ -0,0 +1,8 @@ + + +

Template loaded from theme

+
+ +
+ +