From d46e480105db9b3b74495faefc393d595e8a73ba Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 17 Jan 2023 18:57:39 -0300 Subject: [PATCH 01/68] Add minimum structure for Single Product Details block --- .../blocks/single-product-details/block.json | 31 +++++++++++++++++ .../blocks/single-product-details/block.tsx | 11 +++++++ .../js/blocks/single-product-details/edit.tsx | 33 +++++++++++++++++++ .../blocks/single-product-details/editor.scss | 3 ++ .../blocks/single-product-details/index.tsx | 20 +++++++++++ .../blocks/single-product-details/style.scss | 21 ++++++++++++ .../js/blocks/single-product-details/types.ts | 3 ++ bin/webpack-entries.js | 3 ++ package-lock.json | 2 +- src/BlockTypes/SingleProductDetails.php | 32 ++++++++++++++++++ src/BlockTypesController.php | 1 + 11 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 assets/js/blocks/single-product-details/block.json create mode 100644 assets/js/blocks/single-product-details/block.tsx create mode 100644 assets/js/blocks/single-product-details/edit.tsx create mode 100644 assets/js/blocks/single-product-details/editor.scss create mode 100644 assets/js/blocks/single-product-details/index.tsx create mode 100644 assets/js/blocks/single-product-details/style.scss create mode 100644 assets/js/blocks/single-product-details/types.ts create mode 100644 src/BlockTypes/SingleProductDetails.php diff --git a/assets/js/blocks/single-product-details/block.json b/assets/js/blocks/single-product-details/block.json new file mode 100644 index 00000000000..31bbc78192f --- /dev/null +++ b/assets/js/blocks/single-product-details/block.json @@ -0,0 +1,31 @@ +{ + "name": "woocommerce/single-product-details", + "version": "1.0.0", + "title": "Single Product Details", + "description": "A block that allows your customers to see details and reviews about the product.", + "category": "woocommerce", + "keywords": [ "WooCommerce" ], + "supports": { + "align": true, + "color": { + "text": true + }, + "typography": { + "fontSize": true, + "__experimentalFontFamily": true + } + }, + "attributes": { + "displayStyle": { + "type": "string", + "default": "icon_and_text" + }, + "iconStyle": { + "type": "string", + "default": "default" + } + }, + "textdomain": "woo-gutenberg-products-block", + "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json" +} diff --git a/assets/js/blocks/single-product-details/block.tsx b/assets/js/blocks/single-product-details/block.tsx new file mode 100644 index 00000000000..89fdf9d7d35 --- /dev/null +++ b/assets/js/blocks/single-product-details/block.tsx @@ -0,0 +1,11 @@ +/** + * External dependencies + */ + +/** + * Internal dependencies + */ + +export const SingleProductDetails = () =>
Single Product Details
; + +export default SingleProductDetails; diff --git a/assets/js/blocks/single-product-details/edit.tsx b/assets/js/blocks/single-product-details/edit.tsx new file mode 100644 index 00000000000..88bc365f4b5 --- /dev/null +++ b/assets/js/blocks/single-product-details/edit.tsx @@ -0,0 +1,33 @@ +/** + * External dependencies + */ +import classNames from 'classnames'; +import { useBlockProps } from '@wordpress/block-editor'; +import { Disabled } from '@wordpress/components'; +import type { BlockEditProps } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Block from './block'; +import { Attributes } from './types'; +import './editor.scss'; + +const Edit = ( { attributes }: BlockEditProps< Attributes > ) => { + const { className } = attributes; + const blockProps = useBlockProps( { + className: classNames( 'wc-block-single-product-details', className ), + } ); + + return ( + <> +
+ + + +
+ + ); +}; + +export default Edit; diff --git a/assets/js/blocks/single-product-details/editor.scss b/assets/js/blocks/single-product-details/editor.scss new file mode 100644 index 00000000000..772dbeae8cd --- /dev/null +++ b/assets/js/blocks/single-product-details/editor.scss @@ -0,0 +1,3 @@ +.wc-block-single-product-details__icon-style-toggle { + width: 100%; +} diff --git a/assets/js/blocks/single-product-details/index.tsx b/assets/js/blocks/single-product-details/index.tsx new file mode 100644 index 00000000000..09cb24071f9 --- /dev/null +++ b/assets/js/blocks/single-product-details/index.tsx @@ -0,0 +1,20 @@ +/** + * External dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; + +registerBlockType( metadata, { + attributes: { + ...metadata.attributes, + }, + edit, + save() { + return null; + }, +} ); diff --git a/assets/js/blocks/single-product-details/style.scss b/assets/js/blocks/single-product-details/style.scss new file mode 100644 index 00000000000..842f4c98663 --- /dev/null +++ b/assets/js/blocks/single-product-details/style.scss @@ -0,0 +1,21 @@ +.wp-block-woocommerce-single-product-details { + a { + text-decoration: none !important; + align-items: center; + display: flex; + color: currentColor !important; + + &:hover { + text-decoration: underline !important; + } + + .icon + .label { + margin-left: $gap-smaller; + } + + .icon { + height: em(16px); + width: em(16px); + } + } +} diff --git a/assets/js/blocks/single-product-details/types.ts b/assets/js/blocks/single-product-details/types.ts new file mode 100644 index 00000000000..aa9b37b316a --- /dev/null +++ b/assets/js/blocks/single-product-details/types.ts @@ -0,0 +1,3 @@ +export interface Attributes { + className?: string; +} diff --git a/bin/webpack-entries.js b/bin/webpack-entries.js index b5366b6b580..8380322fdd2 100644 --- a/bin/webpack-entries.js +++ b/bin/webpack-entries.js @@ -64,6 +64,9 @@ const blocks = { 'single-product': { isExperimental: true, }, + 'single-product-details': { + isExperimental: true, + }, 'stock-filter': {}, }; diff --git a/package-lock.json b/package-lock.json index 0c209a24481..fdabce23cc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@woocommerce/block-library", - "version": "9.4.0-dev", + "version": "9.5.0-dev", "hasInstallScript": true, "license": "GPL-3.0+", "dependencies": { diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php new file mode 100644 index 00000000000..ca3f541a4f0 --- /dev/null +++ b/src/BlockTypes/SingleProductDetails.php @@ -0,0 +1,32 @@ +"; + } +} diff --git a/src/BlockTypesController.php b/src/BlockTypesController.php index 5c80f4ff54d..d557a5696a2 100644 --- a/src/BlockTypesController.php +++ b/src/BlockTypesController.php @@ -204,6 +204,7 @@ protected function get_block_types() { 'RatingFilter', 'ReviewsByCategory', 'ReviewsByProduct', + 'SingleProductDetails', 'StockFilter', ]; From 38fcacb094b6eb2d38e6f9b9d1895abe8c438d9f Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 18 Jan 2023 19:19:07 +0100 Subject: [PATCH 02/68] Add Product Image Gallery #8233 Add Product Image Gallery --- assets/js/atomic/blocks/index.js | 1 + .../product-image-gallery/block.json | 12 +++++ .../product-image-gallery/edit.tsx | 3 ++ .../product-image-gallery/index.ts | 15 ++++++ src/BlockTypes/ProductImageGallery.php | 49 +++++++++++++++++++ src/BlockTypesController.php | 1 + 6 files changed, 81 insertions(+) create mode 100644 assets/js/atomic/blocks/product-elements/product-image-gallery/block.json create mode 100644 assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx create mode 100644 assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts create mode 100644 src/BlockTypes/ProductImageGallery.php diff --git a/assets/js/atomic/blocks/index.js b/assets/js/atomic/blocks/index.js index 5a1ff791cbd..6745263834d 100644 --- a/assets/js/atomic/blocks/index.js +++ b/assets/js/atomic/blocks/index.js @@ -13,3 +13,4 @@ import './product-elements/category-list'; import './product-elements/tag-list'; import './product-elements/stock-indicator'; import './product-elements/add-to-cart'; +import './product-elements/product-image-gallery'; diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json new file mode 100644 index 00000000000..25d5506e570 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json @@ -0,0 +1,12 @@ +{ + "name": "woocommerce/product-image-gallery", + "version": "1.0.0", + "title": "Product image gallery", + "description": "", + "category": "woocommerce", + "keywords": [ "WooCommerce" ], + "usesContext": [ "postId", "postType", "queryId" ], + "textdomain": "woo-gutenberg-products-block", + "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json" +} diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx new file mode 100644 index 00000000000..f105412546b --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx @@ -0,0 +1,3 @@ +export const ProductImageGalleryEdit = ( props ) => { + return placeholder; +}; diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts new file mode 100644 index 00000000000..58d29ad826e --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts @@ -0,0 +1,15 @@ +/** + * External dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; +import metadata from './block.json'; + +/** + * Internal dependencies + */ +import { ProductImageGalleryEdit } from './edit'; + +registerBlockType( metadata, { + icon: 'tickets', + edit: ProductImageGalleryEdit, +} ); diff --git a/src/BlockTypes/ProductImageGallery.php b/src/BlockTypes/ProductImageGallery.php new file mode 100644 index 00000000000..32ab6b83c58 --- /dev/null +++ b/src/BlockTypes/ProductImageGallery.php @@ -0,0 +1,49 @@ +context['postId']; + global $product; + $product = wc_get_product( $post_id ); + + if ( class_exists( 'WC_Frontend_Scripts' ) ) { + $frontend_scripts = new \WC_Frontend_Scripts(); + $frontend_scripts::load_scripts(); + } + + ob_start(); + woocommerce_show_product_images(); + $product_image_gallery_html = ob_get_clean(); + + return $product_image_gallery_html; + } +} diff --git a/src/BlockTypesController.php b/src/BlockTypesController.php index dadfc4ee512..5ef64cd806d 100644 --- a/src/BlockTypesController.php +++ b/src/BlockTypesController.php @@ -186,6 +186,7 @@ protected function get_block_types() { 'ProductCategory', 'ProductCategoryList', 'ProductImage', + 'ProductImageGallery', 'ProductNew', 'ProductOnSale', 'ProductPrice', From e743ebe745a7efdb2b7137f7f9d31ad78e43fc63 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Fri, 20 Jan 2023 18:02:07 -0300 Subject: [PATCH 03/68] Add tests for Single Product Details block --- .../single-product-details.fixture.json | 1 + .../backend/single-produt-details.test.js | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json create mode 100644 tests/e2e/specs/backend/single-produt-details.test.js diff --git a/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json b/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json new file mode 100644 index 00000000000..e8acbacb003 --- /dev/null +++ b/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json @@ -0,0 +1 @@ +{"title":"Single Product Details Block","pageContent":""} \ No newline at end of file diff --git a/tests/e2e/specs/backend/single-produt-details.test.js b/tests/e2e/specs/backend/single-produt-details.test.js new file mode 100644 index 00000000000..5a6bc6f3cef --- /dev/null +++ b/tests/e2e/specs/backend/single-produt-details.test.js @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { getAllBlocks, switchUserToAdmin } from '@wordpress/e2e-test-utils'; +import { visitBlockPage } from '@woocommerce/blocks-test-utils'; + +/** + * Internal dependencies + */ +import { insertBlockDontWaitForInsertClose } from '../../utils.js'; + +const block = { + name: 'Single Product Details', + slug: 'woocommerce/single-product-details', + class: '.wc-block-single-product-details', +}; + +describe( `${ block.name } Block`, () => { + beforeAll( async () => { + await switchUserToAdmin(); + await visitBlockPage( `${ block.name } Block` ); + } ); + + it( 'can be inserted more than once', async () => { + await insertBlockDontWaitForInsertClose( block.name ); + expect( await getAllBlocks() ).toHaveLength( 2 ); + } ); + + it( 'renders without crashing', async () => { + await expect( page ).toRenderBlock( block ); + } ); +} ); From 3a050d324fbc3c46a8503fc7a8a2322c31d0780c Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Tue, 24 Jan 2023 15:30:59 +0100 Subject: [PATCH 04/68] Add the initial basis for the Add to Cart button --- .../js/blocks/add-to-cart-button/block.json | 43 ++++++++++++++ assets/js/blocks/add-to-cart-button/edit.tsx | 23 ++++++++ assets/js/blocks/add-to-cart-button/index.tsx | 29 ++++++++++ .../js/blocks/add-to-cart-button/style.scss | 3 + bin/webpack-entries.js | 1 + src/BlockTypes/AddToCartButton.php | 56 +++++++++++++++++++ src/BlockTypesController.php | 1 + 7 files changed, 156 insertions(+) create mode 100644 assets/js/blocks/add-to-cart-button/block.json create mode 100644 assets/js/blocks/add-to-cart-button/edit.tsx create mode 100644 assets/js/blocks/add-to-cart-button/index.tsx create mode 100644 assets/js/blocks/add-to-cart-button/style.scss create mode 100644 src/BlockTypes/AddToCartButton.php diff --git a/assets/js/blocks/add-to-cart-button/block.json b/assets/js/blocks/add-to-cart-button/block.json new file mode 100644 index 00000000000..872808f0d02 --- /dev/null +++ b/assets/js/blocks/add-to-cart-button/block.json @@ -0,0 +1,43 @@ +{ + "name": "woocommerce/add-to-cart-button", + "version": "1.0.0", + "title": "Add-to-cart button", + "description": "Display a button so the customer can add a product to their cart. Options will also be displayed depending on product type. e.g. quantity, variation.", + "category": "woocommerce", + "keywords": [ "WooCommerce" ], + "textdomain": "woo-gutenberg-products-block", + "attributes": { + "contentJustification": { + "type": "string" + }, + "fontSize": { + "type": "string", + "default": "small" + } + }, + "supports": { + "align": [ "wide", "full" ], + "color": { + "background": false, + "link": true, + "__experimentalDefaultControls": { + "text": true, + "link": true + } + }, + "html": false, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalTextTransform": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json" +} diff --git a/assets/js/blocks/add-to-cart-button/edit.tsx b/assets/js/blocks/add-to-cart-button/edit.tsx new file mode 100644 index 00000000000..2e06a7bc485 --- /dev/null +++ b/assets/js/blocks/add-to-cart-button/edit.tsx @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import { useBlockProps } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +export interface Attributes { + className?: string; +} + +const Edit = () => { + const blockProps = useBlockProps( { + className: 'woocommerce wc-block-add-to-cart-button', + } ); + + return ( +
+ { __( 'Add to Cart', 'woo-gutenberg-products-block' ) } +
+ ); +}; + +export default Edit; diff --git a/assets/js/blocks/add-to-cart-button/index.tsx b/assets/js/blocks/add-to-cart-button/index.tsx new file mode 100644 index 00000000000..3803cf2020a --- /dev/null +++ b/assets/js/blocks/add-to-cart-button/index.tsx @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; +import { Icon, queryPagination } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; + +registerBlockType( metadata, { + icon: { + src: ( + + ), + }, + attributes: { + ...metadata.attributes, + }, + edit, + save() { + return null; + }, +} ); diff --git a/assets/js/blocks/add-to-cart-button/style.scss b/assets/js/blocks/add-to-cart-button/style.scss new file mode 100644 index 00000000000..d07cdbd357f --- /dev/null +++ b/assets/js/blocks/add-to-cart-button/style.scss @@ -0,0 +1,3 @@ +.woocommerce.wc-block-add-to-cart-button { + font-size: inherit; +} diff --git a/bin/webpack-entries.js b/bin/webpack-entries.js index 5bdb771020a..6e09bb3ca71 100644 --- a/bin/webpack-entries.js +++ b/bin/webpack-entries.js @@ -13,6 +13,7 @@ const glob = require( 'glob' ); // property. const blocks = { 'active-filters': {}, + 'add-to-cart-button': {}, 'all-products': { customDir: 'products/all-products', }, diff --git a/src/BlockTypes/AddToCartButton.php b/src/BlockTypes/AddToCartButton.php new file mode 100644 index 00000000000..6343c17ffeb --- /dev/null +++ b/src/BlockTypes/AddToCartButton.php @@ -0,0 +1,56 @@ +%4$s', + esc_attr( $classes_and_styles['classes'] ), + esc_attr( $classname ), + esc_attr( $classes_and_styles['styles'] ), + $breadcrumb + ); + } + + /** + * Get the frontend script handle for this block type. + * + * @param string $key Data to get, or default to everything. + */ + protected function get_block_type_script( $key = null ) { + return null; + } +} diff --git a/src/BlockTypesController.php b/src/BlockTypesController.php index dadfc4ee512..4cbb47a2b07 100644 --- a/src/BlockTypesController.php +++ b/src/BlockTypesController.php @@ -165,6 +165,7 @@ protected function get_block_types() { $block_types = [ 'ActiveFilters', + 'AddToCartButton', 'AllProducts', 'AllReviews', 'AttributeFilter', From f096dc9099a949b9c8d2998ef374c4afd10bf567 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Wed, 25 Jan 2023 16:01:00 +0100 Subject: [PATCH 05/68] Trigger the single product add to cart action for each product type. --- src/BlockTypes/AddToCartButton.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/BlockTypes/AddToCartButton.php b/src/BlockTypes/AddToCartButton.php index 6343c17ffeb..bce7014e137 100644 --- a/src/BlockTypes/AddToCartButton.php +++ b/src/BlockTypes/AddToCartButton.php @@ -26,10 +26,19 @@ class AddToCartButton extends AbstractBlock { */ protected function render( $attributes, $content, $block ) { ob_start(); - // TBD: add to cart button. - $breadcrumb = ob_get_clean(); - if ( ! $breadcrumb ) { + while ( have_posts() ) { + the_post(); + global $product; + /** + * Trigger the single product add to cart action for each product type. + */ + do_action( 'woocommerce_' . $product->get_type() . '_add_to_cart' ); + } + + $product = ob_get_clean(); + + if ( ! $product ) { return; } @@ -41,7 +50,7 @@ protected function render( $attributes, $content, $block ) { esc_attr( $classes_and_styles['classes'] ), esc_attr( $classname ), esc_attr( $classes_and_styles['styles'] ), - $breadcrumb + $product ); } From f87de2189b7502f665f12591c8eceda6c17a63b2 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Wed, 25 Jan 2023 18:34:08 -0300 Subject: [PATCH 06/68] wip: create block structure and add initial styles --- .../blocks/single-product-details/block.json | 11 +-- .../blocks/single-product-details/block.tsx | 88 ++++++++++++++++++- .../js/blocks/single-product-details/edit.tsx | 4 +- .../blocks/single-product-details/editor.scss | 47 +++++++++- src/BlockTypes/SingleProductDetails.php | 4 +- 5 files changed, 139 insertions(+), 15 deletions(-) diff --git a/assets/js/blocks/single-product-details/block.json b/assets/js/blocks/single-product-details/block.json index 31bbc78192f..85bc5bf8647 100644 --- a/assets/js/blocks/single-product-details/block.json +++ b/assets/js/blocks/single-product-details/block.json @@ -15,16 +15,7 @@ "__experimentalFontFamily": true } }, - "attributes": { - "displayStyle": { - "type": "string", - "default": "icon_and_text" - }, - "iconStyle": { - "type": "string", - "default": "default" - } - }, + "attributes": {}, "textdomain": "woo-gutenberg-products-block", "apiVersion": 2, "$schema": "https://schemas.wp.org/trunk/block.json" diff --git a/assets/js/blocks/single-product-details/block.tsx b/assets/js/blocks/single-product-details/block.tsx index 89fdf9d7d35..563b51d6ce4 100644 --- a/assets/js/blocks/single-product-details/block.tsx +++ b/assets/js/blocks/single-product-details/block.tsx @@ -1,11 +1,97 @@ /** * External dependencies */ +import classnames from 'classnames'; /** * Internal dependencies */ +const ReviewsTab = () => { + return
tab
; +}; -export const SingleProductDetails = () =>
Single Product Details
; +const AdditionalInformationTab = () => { + return
tab
; +}; + +const DescriptionTab = () => { + return ( +
+ { /* */ } +
+ ); +}; + +const ProductTabContent = ( { content } ) => { + return ( + + ); +}; + +const ProductTabTitle = ( { title, active } ) => { + return ( +
  • _tab', { + active, + } ) } + id="tab-title-" + role="tab" + aria-controls="tab-" + > + { title } +
  • + ); +}; + +const ProductTabsList = ( { productTabs } ) => { + return ( +
    +
      + { productTabs.map( ( productTab ) => ( + + ) ) } +
    + { productTabs.map( ( productTab ) => ( + + ) ) } + + { /* */ } +
    + ); +}; + +export const SingleProductDetails = ( props ) => { + console.log( { props } ); + return ( + + ); +}; export default SingleProductDetails; diff --git a/assets/js/blocks/single-product-details/edit.tsx b/assets/js/blocks/single-product-details/edit.tsx index 88bc365f4b5..78697ce9c07 100644 --- a/assets/js/blocks/single-product-details/edit.tsx +++ b/assets/js/blocks/single-product-details/edit.tsx @@ -13,7 +13,9 @@ import Block from './block'; import { Attributes } from './types'; import './editor.scss'; -const Edit = ( { attributes }: BlockEditProps< Attributes > ) => { +const Edit = ( { attributes, ...rest }: BlockEditProps< Attributes > ) => { + console.log( { attributes } ); + console.log( { rest } ); const { className } = attributes; const blockProps = useBlockProps( { className: classNames( 'wc-block-single-product-details', className ), diff --git a/assets/js/blocks/single-product-details/editor.scss b/assets/js/blocks/single-product-details/editor.scss index 772dbeae8cd..7955f1b1a89 100644 --- a/assets/js/blocks/single-product-details/editor.scss +++ b/assets/js/blocks/single-product-details/editor.scss @@ -1,3 +1,46 @@ -.wc-block-single-product-details__icon-style-toggle { - width: 100%; +.wc-block-single-product-details { + ul.tabs { + list-style: none; + padding: 0 0 0 1em; + margin: 0 0 1.618em; + overflow: hidden; + position: relative; + border-bottom: 1px solid $gray-200; + + li { + border: 1px solid $gray-200; + background-color: $white; + display: inline-block; + position: relative; + z-index: 0; + border-radius: 4px 4px 0 0; + margin: 0; + padding: 0.5em 1em; + opacity: 0.5; + + a { + display: inline-block; + font-weight: 700; + color: $black; + text-decoration: none; + + &:hover { + text-decoration: none; + color: lighten($black, 10%); + } + } + + &.active { + background: $gray-100; + z-index: 2; + border-bottom-color: $gray-200; + opacity: 1; + + a { + color: inherit; + text-shadow: inherit; + } + } + } + } } diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php index ca3f541a4f0..fedd675d7b6 100644 --- a/src/BlockTypes/SingleProductDetails.php +++ b/src/BlockTypes/SingleProductDetails.php @@ -27,6 +27,8 @@ class SingleProductDetails extends AbstractBlock { protected function render( $attributes, $content, $block ) { $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); - return "
    ' + ); } } From 561f6f80ae543a888ac2c4859a74382693a7ad0a Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Thu, 26 Jan 2023 18:50:15 -0300 Subject: [PATCH 07/68] Add block details to the SingleProductDetails.php file --- .../blocks/single-product-details/block.tsx | 14 +++- .../blocks/single-product-details/editor.scss | 43 ---------- .../blocks/single-product-details/style.scss | 53 ++++++++---- src/BlockTemplatesController.php | 2 +- src/BlockTypes/SingleProductDetails.php | 81 ++++++++++++++++++- 5 files changed, 133 insertions(+), 60 deletions(-) diff --git a/assets/js/blocks/single-product-details/block.tsx b/assets/js/blocks/single-product-details/block.tsx index 563b51d6ce4..1e06f0ed868 100644 --- a/assets/js/blocks/single-product-details/block.tsx +++ b/assets/js/blocks/single-product-details/block.tsx @@ -88,7 +88,19 @@ export const SingleProductDetails = ( props ) => { ); diff --git a/assets/js/blocks/single-product-details/editor.scss b/assets/js/blocks/single-product-details/editor.scss index 7955f1b1a89..a6751d249f1 100644 --- a/assets/js/blocks/single-product-details/editor.scss +++ b/assets/js/blocks/single-product-details/editor.scss @@ -1,46 +1,3 @@ .wc-block-single-product-details { - ul.tabs { - list-style: none; - padding: 0 0 0 1em; - margin: 0 0 1.618em; - overflow: hidden; - position: relative; - border-bottom: 1px solid $gray-200; - li { - border: 1px solid $gray-200; - background-color: $white; - display: inline-block; - position: relative; - z-index: 0; - border-radius: 4px 4px 0 0; - margin: 0; - padding: 0.5em 1em; - opacity: 0.5; - - a { - display: inline-block; - font-weight: 700; - color: $black; - text-decoration: none; - - &:hover { - text-decoration: none; - color: lighten($black, 10%); - } - } - - &.active { - background: $gray-100; - z-index: 2; - border-bottom-color: $gray-200; - opacity: 1; - - a { - color: inherit; - text-shadow: inherit; - } - } - } - } } diff --git a/assets/js/blocks/single-product-details/style.scss b/assets/js/blocks/single-product-details/style.scss index 842f4c98663..8eb3a59b08c 100644 --- a/assets/js/blocks/single-product-details/style.scss +++ b/assets/js/blocks/single-product-details/style.scss @@ -1,21 +1,46 @@ .wp-block-woocommerce-single-product-details { - a { - text-decoration: none !important; - align-items: center; - display: flex; - color: currentColor !important; + ul.tabs { + list-style: none; + padding: 0 0 0 1em; + margin: 0 0 1.618em; + overflow: hidden; + position: relative; + border-bottom: 1px solid $gray-200; - &:hover { - text-decoration: underline !important; - } + li { + border: 1px solid $gray-200; + background-color: $white; + display: inline-block; + position: relative; + z-index: 0; + border-radius: 4px 4px 0 0; + margin: 0; + padding: 0.5em 1em; + opacity: 0.5; - .icon + .label { - margin-left: $gap-smaller; - } + a { + display: inline-block; + font-weight: 700; + color: $black; + text-decoration: none; + + &:hover { + text-decoration: none; + color: lighten($black, 10%); + } + } + + &.active { + background: $gray-100; + z-index: 2; + border-bottom-color: $gray-200; + opacity: 1; - .icon { - height: em(16px); - width: em(16px); + a { + color: inherit; + text-shadow: inherit; + } + } } } } diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index 06a339b779f..19b3782b506 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -67,7 +67,7 @@ protected function init() { add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 ); add_filter( 'current_theme_supports-block-templates', array( $this, 'remove_block_template_support_for_shop_page' ) ); add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 ); - + add_filter( 'woocommerce_product_tabs', 'woocommerce_default_product_tabs' ); if ( $this->package->is_experimental_build() ) { add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 ); } diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php index fedd675d7b6..373bf4b7b6b 100644 --- a/src/BlockTypes/SingleProductDetails.php +++ b/src/BlockTypes/SingleProductDetails.php @@ -25,10 +25,89 @@ class SingleProductDetails extends AbstractBlock { * @return string Rendered block output. */ protected function render( $attributes, $content, $block ) { + ob_start(); + + while ( have_posts() ) { + global $product; + the_post(); + the_content(); + } + + $description = ob_get_clean(); + + ob_start(); + + while ( have_posts() ) { + global $product; + the_post(); + wc_display_product_attributes($product); + } + + $product_attributes = ob_get_clean(); + + while ( have_posts() ) { + global $product; + the_post(); + wp_list_comments( array( 'post_id' => the_ID() ) ); + } + + $comments = ob_get_clean(); + + if ( ! $product ) { + return 'hello'; + } + $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); return sprintf( - '
    ' + '
    +
    + + +
    +

    Description

    +
    + %3$s +
    +
    + +
    +

    Additional Information

    +
    + %4$s +
    +
    + +
    +

    Reviews

    +
    + %5$s +
    +
    + +
    +
    ', + esc_attr( $classes_and_styles['classes'] ), + esc_attr( $classes_and_styles['styles'] ), + $description, + $product_attributes, + $comments ); } } From 1331e1c75ff03b39c3100778806a3e1566e7926b Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Fri, 27 Jan 2023 10:51:17 +0100 Subject: [PATCH 08/68] Rename the block from add-to-cart-button to add-to-cart-form --- assets/js/blocks/add-to-cart-button/style.scss | 3 --- .../{add-to-cart-button => add-to-cart-form}/block.json | 4 ++-- .../{add-to-cart-button => add-to-cart-form}/edit.tsx | 2 +- .../{add-to-cart-button => add-to-cart-form}/index.tsx | 0 assets/js/blocks/add-to-cart-form/style.scss | 3 +++ bin/webpack-entries.js | 2 +- src/BlockTypes/{AddToCartButton.php => AddToCartForm.php} | 6 +++--- src/BlockTypesController.php | 3 ++- 8 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 assets/js/blocks/add-to-cart-button/style.scss rename assets/js/blocks/{add-to-cart-button => add-to-cart-form}/block.json (92%) rename assets/js/blocks/{add-to-cart-button => add-to-cart-form}/edit.tsx (87%) rename assets/js/blocks/{add-to-cart-button => add-to-cart-form}/index.tsx (100%) create mode 100644 assets/js/blocks/add-to-cart-form/style.scss rename src/BlockTypes/{AddToCartButton.php => AddToCartForm.php} (87%) diff --git a/assets/js/blocks/add-to-cart-button/style.scss b/assets/js/blocks/add-to-cart-button/style.scss deleted file mode 100644 index d07cdbd357f..00000000000 --- a/assets/js/blocks/add-to-cart-button/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.woocommerce.wc-block-add-to-cart-button { - font-size: inherit; -} diff --git a/assets/js/blocks/add-to-cart-button/block.json b/assets/js/blocks/add-to-cart-form/block.json similarity index 92% rename from assets/js/blocks/add-to-cart-button/block.json rename to assets/js/blocks/add-to-cart-form/block.json index 872808f0d02..1271ed64ed3 100644 --- a/assets/js/blocks/add-to-cart-button/block.json +++ b/assets/js/blocks/add-to-cart-form/block.json @@ -1,7 +1,7 @@ { - "name": "woocommerce/add-to-cart-button", + "name": "woocommerce/add-to-cart-form", "version": "1.0.0", - "title": "Add-to-cart button", + "title": "Add to Cart form", "description": "Display a button so the customer can add a product to their cart. Options will also be displayed depending on product type. e.g. quantity, variation.", "category": "woocommerce", "keywords": [ "WooCommerce" ], diff --git a/assets/js/blocks/add-to-cart-button/edit.tsx b/assets/js/blocks/add-to-cart-form/edit.tsx similarity index 87% rename from assets/js/blocks/add-to-cart-button/edit.tsx rename to assets/js/blocks/add-to-cart-form/edit.tsx index 2e06a7bc485..a61041fac39 100644 --- a/assets/js/blocks/add-to-cart-button/edit.tsx +++ b/assets/js/blocks/add-to-cart-form/edit.tsx @@ -10,7 +10,7 @@ export interface Attributes { const Edit = () => { const blockProps = useBlockProps( { - className: 'woocommerce wc-block-add-to-cart-button', + className: 'woocommerce wc-block-add-to-cart-form', } ); return ( diff --git a/assets/js/blocks/add-to-cart-button/index.tsx b/assets/js/blocks/add-to-cart-form/index.tsx similarity index 100% rename from assets/js/blocks/add-to-cart-button/index.tsx rename to assets/js/blocks/add-to-cart-form/index.tsx diff --git a/assets/js/blocks/add-to-cart-form/style.scss b/assets/js/blocks/add-to-cart-form/style.scss new file mode 100644 index 00000000000..e698412b7c4 --- /dev/null +++ b/assets/js/blocks/add-to-cart-form/style.scss @@ -0,0 +1,3 @@ +.woocommerce.wc-block-add-to-cart-form { + font-size: inherit; +} diff --git a/bin/webpack-entries.js b/bin/webpack-entries.js index 6e09bb3ca71..bef67f5027c 100644 --- a/bin/webpack-entries.js +++ b/bin/webpack-entries.js @@ -13,7 +13,7 @@ const glob = require( 'glob' ); // property. const blocks = { 'active-filters': {}, - 'add-to-cart-button': {}, + 'add-to-cart-form': {}, 'all-products': { customDir: 'products/all-products', }, diff --git a/src/BlockTypes/AddToCartButton.php b/src/BlockTypes/AddToCartForm.php similarity index 87% rename from src/BlockTypes/AddToCartButton.php rename to src/BlockTypes/AddToCartForm.php index bce7014e137..4b8f0d0e8ec 100644 --- a/src/BlockTypes/AddToCartButton.php +++ b/src/BlockTypes/AddToCartForm.php @@ -7,13 +7,13 @@ /** * CatalogSorting class. */ -class AddToCartButton extends AbstractBlock { +class AddToCartForm extends AbstractBlock { /** * Block name. * * @var string */ - protected $block_name = 'add-to-cart-button'; + protected $block_name = 'add-to-cart-form'; /** * Render the block. @@ -46,7 +46,7 @@ protected function render( $attributes, $content, $block ) { $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); return sprintf( - '
    %4$s
    ', + '
    %4$s
    ', esc_attr( $classes_and_styles['classes'] ), esc_attr( $classname ), esc_attr( $classes_and_styles['styles'] ), diff --git a/src/BlockTypesController.php b/src/BlockTypesController.php index 4cbb47a2b07..c3dff64b2fd 100644 --- a/src/BlockTypesController.php +++ b/src/BlockTypesController.php @@ -165,7 +165,7 @@ protected function get_block_types() { $block_types = [ 'ActiveFilters', - 'AddToCartButton', + 'AddToCartForm', 'AllProducts', 'AllReviews', 'AttributeFilter', @@ -250,6 +250,7 @@ protected function get_block_types() { $block_types = array_diff( $block_types, [ + 'AddToCartForm', 'CatalogSorting', 'ClassicTemplate', 'ProductResultsCount', From a65d69da4e7f2851ec5218b25382bea6dcaa5498 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Fri, 27 Jan 2023 11:05:53 +0100 Subject: [PATCH 09/68] Update to use the cart icon. --- assets/js/blocks/add-to-cart-form/edit.tsx | 1 - assets/js/blocks/add-to-cart-form/index.tsx | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/edit.tsx b/assets/js/blocks/add-to-cart-form/edit.tsx index a61041fac39..114bf67eee3 100644 --- a/assets/js/blocks/add-to-cart-form/edit.tsx +++ b/assets/js/blocks/add-to-cart-form/edit.tsx @@ -3,7 +3,6 @@ */ import { useBlockProps } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; - export interface Attributes { className?: string; } diff --git a/assets/js/blocks/add-to-cart-form/index.tsx b/assets/js/blocks/add-to-cart-form/index.tsx index 3803cf2020a..cc3033437a1 100644 --- a/assets/js/blocks/add-to-cart-form/index.tsx +++ b/assets/js/blocks/add-to-cart-form/index.tsx @@ -2,7 +2,8 @@ * External dependencies */ import { registerBlockType } from '@wordpress/blocks'; -import { Icon, queryPagination } from '@wordpress/icons'; +import { Icon } from '@wordpress/icons'; +import { cart } from '@woocommerce/icons'; /** * Internal dependencies @@ -14,7 +15,7 @@ registerBlockType( metadata, { icon: { src: ( ), From 16ee8bf0411ac710a2cf46da987642d2cb111d64 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Fri, 27 Jan 2023 14:54:49 +0100 Subject: [PATCH 10/68] Implement the skeleton for the editor preview. --- assets/js/blocks/add-to-cart-form/edit.tsx | 16 ++++++++++++- assets/js/blocks/add-to-cart-form/editor.scss | 23 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 assets/js/blocks/add-to-cart-form/editor.scss diff --git a/assets/js/blocks/add-to-cart-form/edit.tsx b/assets/js/blocks/add-to-cart-form/edit.tsx index 114bf67eee3..5b5bc531386 100644 --- a/assets/js/blocks/add-to-cart-form/edit.tsx +++ b/assets/js/blocks/add-to-cart-form/edit.tsx @@ -3,6 +3,11 @@ */ import { useBlockProps } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; +import { Disabled } from '@wordpress/components'; +/** + * Internal dependencies + */ +import './editor.scss'; export interface Attributes { className?: string; } @@ -14,7 +19,16 @@ const Edit = () => { return (
    - { __( 'Add to Cart', 'woo-gutenberg-products-block' ) } + +
    Qty
    + +
    ); }; diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss new file mode 100644 index 00000000000..f6a59864c56 --- /dev/null +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -0,0 +1,23 @@ +.wc-add-to-cart-form { + display: grid; + grid-template-columns: 50% 50%; +} + +.quantity-placeholder { + background: #e5e5e5; + margin-bottom: 10px; + width: 60px; + height: 50px; + text-align: center; + display: grid; + place-items: center; + color: #c7c0c0; +} + +.single_add_to_cart_button { + margin-left: 10px; + text-decoration: none; + font-size: medium; + width: inherit; + height: inherit; +} From d07b9b5559a2ef1b7a8c6f143ee08906b52b0ae7 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Fri, 27 Jan 2023 12:59:11 -0300 Subject: [PATCH 11/68] Render tabs title with empty content --- src/BlockTypes/SingleProductDetails.php | 124 +++++++++++------------- 1 file changed, 54 insertions(+), 70 deletions(-) diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php index 373bf4b7b6b..5644b00d5ca 100644 --- a/src/BlockTypes/SingleProductDetails.php +++ b/src/BlockTypes/SingleProductDetails.php @@ -25,89 +25,73 @@ class SingleProductDetails extends AbstractBlock { * @return string Rendered block output. */ protected function render( $attributes, $content, $block ) { - ob_start(); + $tabs = $this->render_tabs(); - while ( have_posts() ) { - global $product; - the_post(); - the_content(); - } + $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); - $description = ob_get_clean(); + return sprintf( + '
    +
    + %3$s +
    +
    ', + esc_attr( $classes_and_styles['classes'] ), + esc_attr( $classes_and_styles['styles'] ), + $tabs, + ); + } + protected function render_tabs(){ ob_start(); while ( have_posts() ) { - global $product; - the_post(); - wc_display_product_attributes($product); - } - - $product_attributes = ob_get_clean(); - - while ( have_posts() ) { - global $product; + global $product, $post; the_post(); - wp_list_comments( array( 'post_id' => the_ID() ) ); - } - - $comments = ob_get_clean(); - - if ( ! $product ) { - return 'hello'; - } - - $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); - - return sprintf( - '
    -
    - + ', + esc_attr( $key ), + esc_attr( $product_tab['title'] ), + ); -
    -

    Description

    -
    - %3$s -
    -
    + $tabs_content .= sprintf(' +
    +

    %2$s

    +
    -
    -

    Additional Information

    -
    - %4$s +
    -
    + ', + esc_attr( $key ), + esc_attr( $product_tab['title'] ), + ); + } + } + $tabs = sprintf(' +
      + %1$s +
    + + %2$s + + ', + $tabs_title, + $tabs_content + ); + echo $tabs; + } -
    -

    Reviews

    -
    - %5$s -
    -
    + $tabs = ob_get_clean(); -
    -
    ', - esc_attr( $classes_and_styles['classes'] ), - esc_attr( $classes_and_styles['styles'] ), - $description, - $product_attributes, - $comments - ); + return $tabs; } } From 9c90f710614bedec697cdccd429c0ef303820c90 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Fri, 27 Jan 2023 16:44:55 -0300 Subject: [PATCH 12/68] Use woocommerce_output_product_data_tabs function to retrieve tabs data --- .../blocks/single-product-details/block.tsx | 136 ++++++++---------- .../blocks/single-product-details/editor.scss | 4 +- src/BlockTypes/SingleProductDetails.php | 47 +----- 3 files changed, 65 insertions(+), 122 deletions(-) diff --git a/assets/js/blocks/single-product-details/block.tsx b/assets/js/blocks/single-product-details/block.tsx index 1e06f0ed868..bd3ef45cd85 100644 --- a/assets/js/blocks/single-product-details/block.tsx +++ b/assets/js/blocks/single-product-details/block.tsx @@ -2,108 +2,92 @@ * External dependencies */ import classnames from 'classnames'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -const ReviewsTab = () => { - return
    tab
    ; -}; - -const AdditionalInformationTab = () => { - return
    tab
    ; -}; -const DescriptionTab = () => { - return ( -
    - { /* */ } -
    - ); -}; +interface SingleProductTab { + id: string; + title: string; + active: boolean; + content: string | undefined; +} -const ProductTabContent = ( { content } ) => { +const ProductTabTitle = ( { + id, + title, + active, +}: Pick< SingleProductTab, 'id' | 'title' | 'active' > ) => { return ( ); }; -const ProductTabTitle = ( { title, active } ) => { +const ProductTabContent = ( { + id, + content, +}: Pick< SingleProductTab, 'id' | 'content' > ) => { return ( -
  • _tab', { - active, - } ) } - id="tab-title-" +
    - { title } -
  • + { content } +
    ); }; -const ProductTabsList = ( { productTabs } ) => { +export const SingleProductDetails = () => { + const productTabs = [ + { + id: 'description', + title: 'Description', + active: true, + content: __( + 'This block lists description, attributes and reviews for a single product.', + 'woo-gutenberg-products-block' + ), + }, + { + id: 'additional_information', + title: 'Additional Information', + active: false, + }, + { id: 'reviews', title: 'Reviews', active: false }, + ]; + const tabsTitle = productTabs.map( ( { id, title, active } ) => ( + + ) ); + const tabsContent = productTabs.map( ( { id, content } ) => ( + + ) ); + return (
      - { productTabs.map( ( productTab ) => ( - - ) ) } + { tabsTitle }
    - { productTabs.map( ( productTab ) => ( - - ) ) } - - { /* */ } + { tabsContent }
    ); }; -export const SingleProductDetails = ( props ) => { - console.log( { props } ); - return ( - - ); -}; - export default SingleProductDetails; diff --git a/assets/js/blocks/single-product-details/editor.scss b/assets/js/blocks/single-product-details/editor.scss index a6751d249f1..da60cc6b925 100644 --- a/assets/js/blocks/single-product-details/editor.scss +++ b/assets/js/blocks/single-product-details/editor.scss @@ -1,3 +1,5 @@ .wc-block-single-product-details { - + ul.tabs.li { + list-style-type: none; + } } diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php index 5644b00d5ca..f45f0e68b7f 100644 --- a/src/BlockTypes/SingleProductDetails.php +++ b/src/BlockTypes/SingleProductDetails.php @@ -31,9 +31,7 @@ protected function render( $attributes, $content, $block ) { return sprintf( '
    -
    - %3$s -
    + %3$s
    ', esc_attr( $classes_and_styles['classes'] ), esc_attr( $classes_and_styles['styles'] ), @@ -45,49 +43,8 @@ protected function render_tabs(){ ob_start(); while ( have_posts() ) { - global $product, $post; the_post(); - $product_tabs = apply_filters( 'woocommerce_product_tabs', array() ); - $tabs_title = ''; - $tabs_content = ''; - if ( ! empty( $product_tabs ) ) { - foreach ( $product_tabs as $key => $product_tab ) { - $tabs_title .= sprintf(' - - ', - esc_attr( $key ), - esc_attr( $product_tab['title'] ), - ); - - $tabs_content .= sprintf(' -
    -

    %2$s

    -
    - -
    -
    - ', - esc_attr( $key ), - esc_attr( $product_tab['title'] ), - ); - } - } - $tabs = sprintf(' -
      - %1$s -
    - - %2$s - - ', - $tabs_title, - $tabs_content - ); - echo $tabs; + woocommerce_output_product_data_tabs(); } $tabs = ob_get_clean(); From 961b62f2d5a4ef598837d7a6255d00558057b05f Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 00:29:25 +0100 Subject: [PATCH 13/68] Update styles and add Notice for the display in the Editor. --- assets/js/blocks/add-to-cart-form/block.json | 32 ------------------ assets/js/blocks/add-to-cart-form/edit.tsx | 34 ++++++++++++++++---- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/block.json b/assets/js/blocks/add-to-cart-form/block.json index 1271ed64ed3..7d2b45b32a2 100644 --- a/assets/js/blocks/add-to-cart-form/block.json +++ b/assets/js/blocks/add-to-cart-form/block.json @@ -6,38 +6,6 @@ "category": "woocommerce", "keywords": [ "WooCommerce" ], "textdomain": "woo-gutenberg-products-block", - "attributes": { - "contentJustification": { - "type": "string" - }, - "fontSize": { - "type": "string", - "default": "small" - } - }, - "supports": { - "align": [ "wide", "full" ], - "color": { - "background": false, - "link": true, - "__experimentalDefaultControls": { - "text": true, - "link": true - } - }, - "html": false, - "typography": { - "fontSize": true, - "lineHeight": true, - "__experimentalFontFamily": true, - "__experimentalFontStyle": true, - "__experimentalFontWeight": true, - "__experimentalTextTransform": true, - "__experimentalDefaultControls": { - "fontSize": true - } - } - }, "apiVersion": 2, "$schema": "https://schemas.wp.org/trunk/block.json" } diff --git a/assets/js/blocks/add-to-cart-form/edit.tsx b/assets/js/blocks/add-to-cart-form/edit.tsx index 5b5bc531386..b96914e8e9b 100644 --- a/assets/js/blocks/add-to-cart-form/edit.tsx +++ b/assets/js/blocks/add-to-cart-form/edit.tsx @@ -3,7 +3,12 @@ */ import { useBlockProps } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -import { Disabled } from '@wordpress/components'; +import { + Button, + Disabled, + Notice, + __experimentalInputControl as InputControl, +} from '@wordpress/components'; /** * Internal dependencies */ @@ -20,14 +25,29 @@ const Edit = () => { return (
    -
    Qty
    - +
    ); From 01a6400a69e6ca7bc5e1e4a48af303112ebc3a78 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 00:38:38 +0100 Subject: [PATCH 14/68] Update CSS. --- assets/js/blocks/add-to-cart-form/editor.scss | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index f6a59864c56..f1af71a44b0 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -1,23 +1,35 @@ -.wc-add-to-cart-form { - display: grid; - grid-template-columns: 50% 50%; +.wc-block-add-to-cart-form { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; } -.quantity-placeholder { - background: #e5e5e5; - margin-bottom: 10px; - width: 60px; - height: 50px; - text-align: center; - display: grid; - place-items: center; - color: #c7c0c0; +.wc-block-add-to-cart-form__notice { + margin: 10px 0; } -.single_add_to_cart_button { - margin-left: 10px; - text-decoration: none; - font-size: medium; - width: inherit; - height: inherit; +.wc-block-add-to-cart-form__quantity { + width: 70px; + padding-right: 10px; + float: left; + font-size: var(--wp--preset--font-size--small); + + input[type="number"]::-webkit-inner-spin-button { + opacity: 1; + } + + .components-input-control__input { + padding: 12px !important; + height: 50px !important; + } +} + +.wc-block-add-to-cart-form__button.is-primary { + float: left; + color: var(--wp--preset--color--black); + background-color: var(--wp--preset--color--primary); + font-size: var(--wp--preset--font-size--small); + padding: 25px; + border-radius: 0; } From caa6e6a83b981c8322ba963b41269c218a18d0cf Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 12:34:18 +0100 Subject: [PATCH 15/68] Add base tests for the new Add to Cart Form component. --- .../specs/backend/add-to-cart-form.test.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/e2e/specs/backend/add-to-cart-form.test.js diff --git a/tests/e2e/specs/backend/add-to-cart-form.test.js b/tests/e2e/specs/backend/add-to-cart-form.test.js new file mode 100644 index 00000000000..978114b9348 --- /dev/null +++ b/tests/e2e/specs/backend/add-to-cart-form.test.js @@ -0,0 +1,61 @@ +/** + * External dependencies + */ +import { + canvas, + createNewPost, + insertBlock, + switchUserToAdmin, +} from '@wordpress/e2e-test-utils'; +import { searchForBlock } from '@wordpress/e2e-test-utils/build/inserter'; + +/** + * Internal dependencies + */ +import { + filterCurrentBlocks, + goToSiteEditor, + useTheme, + waitForCanvas, +} from '../../utils.js'; + +const block = { + name: 'Add to Cart form', + slug: 'woocommerce/add-to-cart-form', + class: '.wc-block-add-to-cart-form', +}; + +describe( `${ block.name } Block`, () => { + it( 'in can not be inserted in a post', async () => { + await switchUserToAdmin(); + await createNewPost( { + postType: 'post', + title: block.name, + } ); + await searchForBlock( block.name ); + expect( page ).toMatch( 'No results found.' ); + } ); + + describe( 'in FSE editor', () => { + useTheme( 'emptytheme' ); + + beforeEach( async () => { + await goToSiteEditor(); + await waitForCanvas(); + } ); + + it( 'can be inserted in FSE area', async () => { + await insertBlock( block.name ); + await expect( canvas() ).toMatchElement( block.class ); + } ); + + it( 'can be inserted more than once', async () => { + await insertBlock( block.name ); + await insertBlock( block.name ); + const foo = await filterCurrentBlocks( + ( b ) => b.name === block.slug + ); + expect( foo ).toHaveLength( 2 ); + } ); + } ); +} ); From 13dc04645bef60cbb693ff67e0154233298722e4 Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 30 Jan 2023 14:47:00 +0100 Subject: [PATCH 16/68] Add Product Image Gallery block --- .../product-image-gallery/block.json | 17 ++++++- .../product-image-gallery/edit.tsx | 49 ++++++++++++++----- .../product-image-gallery/index.ts | 19 +++++-- .../product-image-gallery/style.scss | 11 +++++ assets/js/atomic/utils/index.js | 1 + src/BlockTypes/ProductImageGallery.php | 26 +++++++++- 6 files changed, 104 insertions(+), 19 deletions(-) diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json index 25d5506e570..76aa69ac84e 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json @@ -1,9 +1,22 @@ { "name": "woocommerce/product-image-gallery", "version": "1.0.0", - "title": "Product image gallery", - "description": "", + "title": "Product Image Gallery", + "icon": "image", + "description": "Display the main product images.", "category": "woocommerce", + "supports": { + "align": true, + "reusable": false, + "spacing": { + "margin": true + }, + "__experimentalBorder": { + "radius": true, + "__experimentalSkipSerialization": true + }, + "__experimentalSelector": ".wp-block-woocommerce-product-image-gallery img" + }, "keywords": [ "WooCommerce" ], "usesContext": [ "postId", "postType", "queryId" ], "textdomain": "woo-gutenberg-products-block", diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx index 4d54b44b559..0643dab1315 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx @@ -1,21 +1,48 @@ -/** - * Internal dependencies - */ - /** * External dependencies */ +import { useBorderProps } from '@woocommerce/base-hooks'; import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings'; import { isEmptyObject } from '@woocommerce/types'; import { useBlockProps } from '@wordpress/block-editor'; import { BlockAttributes } from '@wordpress/blocks'; -const Placeholder = () => { +/** + * Internal dependencies + */ +import './editor.scss'; + +const Placeholder = ( { attributes }: BlockAttributes ) => { + const marginProps = useBorderProps( attributes ); + return ( - Placeholder +
    + Placeholder +
    + { [ ...Array( 4 ).keys() ].map( ( index ) => { + return ( + Placeholder + ); + } ) } +
    +
    ); }; @@ -30,13 +57,13 @@ interface Props { context: Context; } -const Edit = ( { context }: Props ) => { +const Edit = ( { context, attributes }: Props ) => { const blockProps = useBlockProps(); if ( isEmptyObject( context ) ) { return (
    - +
    ); } diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts index 58d4027c88e..99ac2c7f4a4 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts @@ -1,7 +1,8 @@ /** * External dependencies */ -import { registerBlockType } from '@wordpress/blocks'; +import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; +import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils'; /** * Internal dependencies @@ -9,9 +10,17 @@ import { registerBlockType } from '@wordpress/blocks'; import edit from './edit'; import save from './save'; import metadata from './block.json'; +import './style.scss'; -registerBlockType( metadata, { - icon: 'tickets', - edit, - save, +registerBlockSingleProductTemplate( { + registerBlockFn: () => { + registerBlockType( metadata, { + edit, + save, + } ); + }, + unregisterBlockFn: () => { + unregisterBlockType( metadata.name ); + }, + blockName: metadata.name, } ); diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss index e69de29bb2d..a4abddab1bf 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss @@ -0,0 +1,11 @@ +.woocommerce-product-gallery { + border-radius: inherit; + + .woocommerce-product-gallery__wrapper { + border-radius: inherit; + + .woocommerce-product-gallery__image { + border-radius: inherit; + } + } + } diff --git a/assets/js/atomic/utils/index.js b/assets/js/atomic/utils/index.js index f10a14b6858..06280bb433f 100644 --- a/assets/js/atomic/utils/index.js +++ b/assets/js/atomic/utils/index.js @@ -2,3 +2,4 @@ export * from './get-block-map'; export * from './create-blocks-from-template'; export * from './render-parent-block'; export * from './render-standalone-blocks'; +export * from './register-block-single-product-template'; diff --git a/src/BlockTypes/ProductImageGallery.php b/src/BlockTypes/ProductImageGallery.php index 32ab6b83c58..3ce3fdc312f 100644 --- a/src/BlockTypes/ProductImageGallery.php +++ b/src/BlockTypes/ProductImageGallery.php @@ -1,6 +1,9 @@ 'img', + ); + } + /** * Include and render the block. * @@ -40,10 +54,20 @@ protected function render( $attributes, $content, $block ) { $frontend_scripts::load_scripts(); } + $classname = $attributes['className'] ?? ''; + $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); + ob_start(); woocommerce_show_product_images(); $product_image_gallery_html = ob_get_clean(); - return $product_image_gallery_html; + return sprintf( + '', + esc_attr( $classes_and_styles['classes'] ), + esc_attr( $classname ), + esc_attr( $classes_and_styles['styles'] ), + $product_image_gallery_html + ); + } } From 21a1fea12282af175b12226447bcf81075d81060 Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 30 Jan 2023 16:31:45 +0100 Subject: [PATCH 17/68] remove support global styles --- .../product-elements/product-image-gallery/block.json | 10 +--------- .../product-elements/product-image-gallery/edit.tsx | 5 ++++- .../product-elements/product-image-gallery/editor.scss | 4 ---- src/BlockTypes/ProductImageGallery.php | 10 ---------- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json index 76aa69ac84e..9dd08179132 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json @@ -7,15 +7,7 @@ "category": "woocommerce", "supports": { "align": true, - "reusable": false, - "spacing": { - "margin": true - }, - "__experimentalBorder": { - "radius": true, - "__experimentalSkipSerialization": true - }, - "__experimentalSelector": ".wp-block-woocommerce-product-image-gallery img" + "reusable": false }, "keywords": [ "WooCommerce" ], "usesContext": [ "postId", "postType", "queryId" ], diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx index 0643dab1315..7740bccab2a 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx @@ -6,6 +6,7 @@ import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings'; import { isEmptyObject } from '@woocommerce/types'; import { useBlockProps } from '@wordpress/block-editor'; import { BlockAttributes } from '@wordpress/blocks'; +import { Disabled } from '@wordpress/components'; /** * Internal dependencies @@ -63,7 +64,9 @@ const Edit = ( { context, attributes }: Props ) => { if ( isEmptyObject( context ) ) { return (
    - + + +
    ); } diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss index 5c176af5243..ad1d09c6d26 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss @@ -1,16 +1,12 @@ .wc-block-editor-product-gallery { - border-style: none; - img { width: 500px; height: 500px; - border-radius: inherit; } .wc-block-editor-product-gallery__gallery { img { width: 100px; height: 100px; - border-radius: inherit; } } } diff --git a/src/BlockTypes/ProductImageGallery.php b/src/BlockTypes/ProductImageGallery.php index 3ce3fdc312f..23f0b0865f7 100644 --- a/src/BlockTypes/ProductImageGallery.php +++ b/src/BlockTypes/ProductImageGallery.php @@ -24,16 +24,6 @@ protected function get_block_type_uses_context() { return [ 'query', 'queryId', 'postId' ]; } - /** - * Get block attributes. - * - * @return array - */ - protected function get_block_type_supports() { - return array( - '__experimentalSelector' => 'img', - ); - } /** * Include and render the block. From d9900e006606d9d406442374f84eb4e00073673f Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 30 Jan 2023 16:31:45 +0100 Subject: [PATCH 18/68] remove support global styles --- .../product-elements/product-image-gallery/block.json | 10 +--------- .../product-elements/product-image-gallery/edit.tsx | 5 ++++- .../product-image-gallery/editor.scss | 4 ---- .../product-elements/product-image-gallery/index.ts | 1 - .../product-elements/product-image-gallery/style.scss | 11 ----------- src/BlockTypes/ProductImageGallery.php | 10 ---------- 6 files changed, 5 insertions(+), 36 deletions(-) delete mode 100644 assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json index 76aa69ac84e..9dd08179132 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json @@ -7,15 +7,7 @@ "category": "woocommerce", "supports": { "align": true, - "reusable": false, - "spacing": { - "margin": true - }, - "__experimentalBorder": { - "radius": true, - "__experimentalSkipSerialization": true - }, - "__experimentalSelector": ".wp-block-woocommerce-product-image-gallery img" + "reusable": false }, "keywords": [ "WooCommerce" ], "usesContext": [ "postId", "postType", "queryId" ], diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx index 0643dab1315..7740bccab2a 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx @@ -6,6 +6,7 @@ import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings'; import { isEmptyObject } from '@woocommerce/types'; import { useBlockProps } from '@wordpress/block-editor'; import { BlockAttributes } from '@wordpress/blocks'; +import { Disabled } from '@wordpress/components'; /** * Internal dependencies @@ -63,7 +64,9 @@ const Edit = ( { context, attributes }: Props ) => { if ( isEmptyObject( context ) ) { return (
    - + + +
    ); } diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss index 5c176af5243..ad1d09c6d26 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss @@ -1,16 +1,12 @@ .wc-block-editor-product-gallery { - border-style: none; - img { width: 500px; height: 500px; - border-radius: inherit; } .wc-block-editor-product-gallery__gallery { img { width: 100px; height: 100px; - border-radius: inherit; } } } diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts index 99ac2c7f4a4..7ed4f0142a9 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts @@ -10,7 +10,6 @@ import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils'; import edit from './edit'; import save from './save'; import metadata from './block.json'; -import './style.scss'; registerBlockSingleProductTemplate( { registerBlockFn: () => { diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss deleted file mode 100644 index a4abddab1bf..00000000000 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/style.scss +++ /dev/null @@ -1,11 +0,0 @@ -.woocommerce-product-gallery { - border-radius: inherit; - - .woocommerce-product-gallery__wrapper { - border-radius: inherit; - - .woocommerce-product-gallery__image { - border-radius: inherit; - } - } - } diff --git a/src/BlockTypes/ProductImageGallery.php b/src/BlockTypes/ProductImageGallery.php index 3ce3fdc312f..23f0b0865f7 100644 --- a/src/BlockTypes/ProductImageGallery.php +++ b/src/BlockTypes/ProductImageGallery.php @@ -24,16 +24,6 @@ protected function get_block_type_uses_context() { return [ 'query', 'queryId', 'postId' ]; } - /** - * Get block attributes. - * - * @return array - */ - protected function get_block_type_supports() { - return array( - '__experimentalSelector' => 'img', - ); - } /** * Include and render the block. From 3c06189047e0c87565262ee24b638efad9d4896b Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 17:21:15 +0100 Subject: [PATCH 19/68] Update the button CSS. --- assets/js/blocks/add-to-cart-form/editor.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index f1af71a44b0..51941d199c6 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -25,11 +25,8 @@ } } -.wc-block-add-to-cart-form__button.is-primary { +.wc-block-add-to-cart-form__button { float: left; - color: var(--wp--preset--color--black); - background-color: var(--wp--preset--color--primary); - font-size: var(--wp--preset--font-size--small); padding: 25px; border-radius: 0; } From eb0ce4a053d03992a4aff1600b2c519d1989c6f8 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Mon, 30 Jan 2023 14:38:31 -0300 Subject: [PATCH 20/68] Remove customizations for the Single Product Details block --- assets/js/blocks/single-product-details/block.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/assets/js/blocks/single-product-details/block.json b/assets/js/blocks/single-product-details/block.json index 85bc5bf8647..97db7cb121a 100644 --- a/assets/js/blocks/single-product-details/block.json +++ b/assets/js/blocks/single-product-details/block.json @@ -5,16 +5,7 @@ "description": "A block that allows your customers to see details and reviews about the product.", "category": "woocommerce", "keywords": [ "WooCommerce" ], - "supports": { - "align": true, - "color": { - "text": true - }, - "typography": { - "fontSize": true, - "__experimentalFontFamily": true - } - }, + "supports": {}, "attributes": {}, "textdomain": "woo-gutenberg-products-block", "apiVersion": 2, From 30f3385036c8902fc17e9e12d6ffbe9195175dd8 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 18:46:17 +0100 Subject: [PATCH 21/68] Update styles for the cart form. --- assets/js/blocks/add-to-cart-form/editor.scss | 3 --- assets/js/blocks/add-to-cart-form/style.scss | 10 ++++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index 51941d199c6..aee48ebf630 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -1,8 +1,6 @@ .wc-block-add-to-cart-form { display: flex; flex-direction: row; - justify-content: space-between; - align-items: center; } .wc-block-add-to-cart-form__notice { @@ -13,7 +11,6 @@ width: 70px; padding-right: 10px; float: left; - font-size: var(--wp--preset--font-size--small); input[type="number"]::-webkit-inner-spin-button { opacity: 1; diff --git a/assets/js/blocks/add-to-cart-form/style.scss b/assets/js/blocks/add-to-cart-form/style.scss index e698412b7c4..1b8385d1a67 100644 --- a/assets/js/blocks/add-to-cart-form/style.scss +++ b/assets/js/blocks/add-to-cart-form/style.scss @@ -1,3 +1,13 @@ .woocommerce.wc-block-add-to-cart-form { font-size: inherit; } + +.wc-block-add-to-cart-form td.woocommerce-grouped-product-list-item__quantity { + padding-right: 0.5rem; + padding-bottom: 1rem; +} + +.wc-block-add-to-cart-form table.woocommerce-grouped-product-list.group_table td { + padding-right: 0.5rem; + padding-bottom: 1rem; +} From 8a58e3c3f69eac041bc55b7b7b2e882528a59f00 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 18:50:08 +0100 Subject: [PATCH 22/68] update td style. --- assets/js/blocks/add-to-cart-form/style.scss | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/style.scss b/assets/js/blocks/add-to-cart-form/style.scss index 1b8385d1a67..059c06cca6e 100644 --- a/assets/js/blocks/add-to-cart-form/style.scss +++ b/assets/js/blocks/add-to-cart-form/style.scss @@ -2,12 +2,7 @@ font-size: inherit; } -.wc-block-add-to-cart-form td.woocommerce-grouped-product-list-item__quantity { - padding-right: 0.5rem; - padding-bottom: 1rem; -} - -.wc-block-add-to-cart-form table.woocommerce-grouped-product-list.group_table td { +.woocommerce.wc-block-add-to-cart-form td { padding-right: 0.5rem; padding-bottom: 1rem; } From d57a5daccc1131d865e68b8e280c6f370dc8bc53 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 19:59:56 +0100 Subject: [PATCH 23/68] Update divs and CSS. --- assets/js/blocks/add-to-cart-form/style.scss | 5 ++--- src/BlockTypes/AddToCartForm.php | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/style.scss b/assets/js/blocks/add-to-cart-form/style.scss index 059c06cca6e..ba006d7de88 100644 --- a/assets/js/blocks/add-to-cart-form/style.scss +++ b/assets/js/blocks/add-to-cart-form/style.scss @@ -2,7 +2,6 @@ font-size: inherit; } -.woocommerce.wc-block-add-to-cart-form td { - padding-right: 0.5rem; - padding-bottom: 1rem; +.woocommerce .wc-block-add-to-cart-form .product .summary { + float: left; } diff --git a/src/BlockTypes/AddToCartForm.php b/src/BlockTypes/AddToCartForm.php index 4b8f0d0e8ec..bf45693b1bf 100644 --- a/src/BlockTypes/AddToCartForm.php +++ b/src/BlockTypes/AddToCartForm.php @@ -46,7 +46,7 @@ protected function render( $attributes, $content, $block ) { $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); return sprintf( - '
    %4$s
    ', + '
    %4$s
    ', esc_attr( $classes_and_styles['classes'] ), esc_attr( $classname ), esc_attr( $classes_and_styles['styles'] ), From 137776ac9b608ef0dc465a8fd0c8e13015ea38ba Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Mon, 30 Jan 2023 22:49:55 +0100 Subject: [PATCH 24/68] Use conventional input instead of the experimental InputControl --- assets/js/blocks/add-to-cart-form/edit.tsx | 9 ++------- assets/js/blocks/add-to-cart-form/editor.scss | 20 ++++++++----------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/edit.tsx b/assets/js/blocks/add-to-cart-form/edit.tsx index b96914e8e9b..40882487528 100644 --- a/assets/js/blocks/add-to-cart-form/edit.tsx +++ b/assets/js/blocks/add-to-cart-form/edit.tsx @@ -3,12 +3,7 @@ */ import { useBlockProps } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -import { - Button, - Disabled, - Notice, - __experimentalInputControl as InputControl, -} from '@wordpress/components'; +import { Button, Disabled, Notice } from '@wordpress/components'; /** * Internal dependencies */ @@ -37,7 +32,7 @@ const Edit = () => { ) }

    - Date: Tue, 31 Jan 2023 12:04:49 +0100 Subject: [PATCH 25/68] address CSS feedback --- .../product-elements/product-image-gallery/edit.tsx | 10 +--------- .../product-elements/product-image-gallery/editor.scss | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx index 588492b11ed..da45b7bb8c4 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx @@ -17,22 +17,14 @@ const Placeholder = () => {
    Placeholder -
    +
    { [ ...Array( 4 ).keys() ].map( ( index ) => { return ( Placeholder ); diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss index ad1d09c6d26..962cafb0cd4 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss @@ -3,7 +3,7 @@ width: 500px; height: 500px; } - .wc-block-editor-product-gallery__gallery { + .wc-block-editor-product-gallery__other-images { img { width: 100px; height: 100px; From dc48ddf0b385829545c64c810d0beaa0a2d50f31 Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 31 Jan 2023 12:15:07 +0100 Subject: [PATCH 26/68] add support for the custom classname --- .../product-image-gallery/save.tsx | 16 ++++++++++++++-- src/BlockTypes/ProductImageGallery.php | 7 ++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx index 03b39b248c5..26f6ea20404 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx @@ -3,8 +3,20 @@ */ import { useBlockProps } from '@wordpress/block-editor'; -export const Save = (): JSX.Element => { - return
    ; +type Props = { + attributes: Record< string, unknown > & { + className?: string; + }; +}; + +export const Save = ( { attributes }: Props ): JSX.Element => { + return ( +
    + ); }; export default Save; diff --git a/src/BlockTypes/ProductImageGallery.php b/src/BlockTypes/ProductImageGallery.php index 23f0b0865f7..43e84ebbe32 100644 --- a/src/BlockTypes/ProductImageGallery.php +++ b/src/BlockTypes/ProductImageGallery.php @@ -44,18 +44,15 @@ protected function render( $attributes, $content, $block ) { $frontend_scripts::load_scripts(); } - $classname = $attributes['className'] ?? ''; - $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); + $classname = $attributes['className'] ?? ''; ob_start(); woocommerce_show_product_images(); $product_image_gallery_html = ob_get_clean(); return sprintf( - '', - esc_attr( $classes_and_styles['classes'] ), + '', esc_attr( $classname ), - esc_attr( $classes_and_styles['styles'] ), $product_image_gallery_html ); From e526eff91a4bd6e955e62793f5b18c259305e5ce Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 31 Jan 2023 17:05:15 +0100 Subject: [PATCH 27/68] remove save function --- .../product-image-gallery/index.ts | 2 -- .../product-image-gallery/save.tsx | 22 ------------------- 2 files changed, 24 deletions(-) delete mode 100644 assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts index 7ed4f0142a9..2f37200749b 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts @@ -8,14 +8,12 @@ import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils'; * Internal dependencies */ import edit from './edit'; -import save from './save'; import metadata from './block.json'; registerBlockSingleProductTemplate( { registerBlockFn: () => { registerBlockType( metadata, { edit, - save, } ); }, unregisterBlockFn: () => { diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx deleted file mode 100644 index 26f6ea20404..00000000000 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/save.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/** - * External dependencies - */ -import { useBlockProps } from '@wordpress/block-editor'; - -type Props = { - attributes: Record< string, unknown > & { - className?: string; - }; -}; - -export const Save = ( { attributes }: Props ): JSX.Element => { - return ( -
    - ); -}; - -export default Save; From 2a4baadcb334e506f10c5c084e1a3c6475159aaa Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 16:13:05 -0300 Subject: [PATCH 28/68] Remove unnecessary console.log from the Edit.tsx file --- assets/js/blocks/single-product-details/edit.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/assets/js/blocks/single-product-details/edit.tsx b/assets/js/blocks/single-product-details/edit.tsx index 78697ce9c07..88bc365f4b5 100644 --- a/assets/js/blocks/single-product-details/edit.tsx +++ b/assets/js/blocks/single-product-details/edit.tsx @@ -13,9 +13,7 @@ import Block from './block'; import { Attributes } from './types'; import './editor.scss'; -const Edit = ( { attributes, ...rest }: BlockEditProps< Attributes > ) => { - console.log( { attributes } ); - console.log( { rest } ); +const Edit = ( { attributes }: BlockEditProps< Attributes > ) => { const { className } = attributes; const blockProps = useBlockProps( { className: classNames( 'wc-block-single-product-details', className ), From 8e05b718c2bd892740e2efd8b39d5440d7d787f8 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 16:45:44 -0300 Subject: [PATCH 29/68] Remove block classname from block wrapper --- assets/js/blocks/single-product-details/block.tsx | 4 +++- assets/js/blocks/single-product-details/edit.tsx | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/single-product-details/block.tsx b/assets/js/blocks/single-product-details/block.tsx index bd3ef45cd85..37db65931f0 100644 --- a/assets/js/blocks/single-product-details/block.tsx +++ b/assets/js/blocks/single-product-details/block.tsx @@ -3,6 +3,7 @@ */ import classnames from 'classnames'; import { __ } from '@wordpress/i18n'; +import { useBlockProps } from '@wordpress/block-editor'; /** * Internal dependencies @@ -51,6 +52,7 @@ const ProductTabContent = ( { }; export const SingleProductDetails = () => { + const blockProps = useBlockProps(); const productTabs = [ { id: 'description', @@ -81,7 +83,7 @@ export const SingleProductDetails = () => { ) ); return ( -
    +
      { tabsTitle }
    diff --git a/assets/js/blocks/single-product-details/edit.tsx b/assets/js/blocks/single-product-details/edit.tsx index 88bc365f4b5..471f9f7e30b 100644 --- a/assets/js/blocks/single-product-details/edit.tsx +++ b/assets/js/blocks/single-product-details/edit.tsx @@ -1,7 +1,6 @@ /** * External dependencies */ -import classNames from 'classnames'; import { useBlockProps } from '@wordpress/block-editor'; import { Disabled } from '@wordpress/components'; import type { BlockEditProps } from '@wordpress/blocks'; @@ -16,7 +15,7 @@ import './editor.scss'; const Edit = ( { attributes }: BlockEditProps< Attributes > ) => { const { className } = attributes; const blockProps = useBlockProps( { - className: classNames( 'wc-block-single-product-details', className ), + className, } ); return ( From f70ab0cbfedabce29e0b225b636b2fd0892610eb Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 16:59:04 -0300 Subject: [PATCH 30/68] Remove unnecessary WooCommerce tabs filter from the BlockTemplatesController --- src/BlockTemplatesController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index 19b3782b506..6b3b42f8f8f 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -67,7 +67,6 @@ protected function init() { add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 ); add_filter( 'current_theme_supports-block-templates', array( $this, 'remove_block_template_support_for_shop_page' ) ); add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 ); - add_filter( 'woocommerce_product_tabs', 'woocommerce_default_product_tabs' ); if ( $this->package->is_experimental_build() ) { add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 ); } From ad70695611dd9718c7e436d65a4cd151ad9f765b Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 17:51:12 -0300 Subject: [PATCH 31/68] Remove attributes property from the block registration --- assets/js/blocks/single-product-details/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/assets/js/blocks/single-product-details/index.tsx b/assets/js/blocks/single-product-details/index.tsx index 09cb24071f9..d6ef97fddd2 100644 --- a/assets/js/blocks/single-product-details/index.tsx +++ b/assets/js/blocks/single-product-details/index.tsx @@ -10,9 +10,6 @@ import metadata from './block.json'; import edit from './edit'; registerBlockType( metadata, { - attributes: { - ...metadata.attributes, - }, edit, save() { return null; From fd83c09a5997ecaa8822550540fa9a19a5474bc6 Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 17:52:36 -0300 Subject: [PATCH 32/68] Remove isExperimental flag for the Single Product Details block --- bin/webpack-entries.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/webpack-entries.js b/bin/webpack-entries.js index 6be7e9b9242..90d8e86e367 100644 --- a/bin/webpack-entries.js +++ b/bin/webpack-entries.js @@ -66,9 +66,7 @@ const blocks = { 'single-product': { isExperimental: true, }, - 'single-product-details': { - isExperimental: true, - }, + 'single-product-details': {}, 'stock-filter': {}, }; From 36611333e33ee453673b810ad57c689ffe57e32a Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 17:59:03 -0300 Subject: [PATCH 33/68] Remove get_classes_and_styles_by_attributes method from SingleProductDetails block --- src/BlockTypes/SingleProductDetails.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php index f45f0e68b7f..17fb82cadad 100644 --- a/src/BlockTypes/SingleProductDetails.php +++ b/src/BlockTypes/SingleProductDetails.php @@ -27,14 +27,13 @@ class SingleProductDetails extends AbstractBlock { protected function render( $attributes, $content, $block ) { $tabs = $this->render_tabs(); - $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); + $classname = $attributes['className'] ?? ''; return sprintf( - '
    - %3$s + '
    + %2$s
    ', - esc_attr( $classes_and_styles['classes'] ), - esc_attr( $classes_and_styles['styles'] ), + esc_attr( $classname ), $tabs, ); } From 8a9196e3075d708699c0c97f5d8a5ac243c2de9e Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Tue, 31 Jan 2023 18:08:03 -0300 Subject: [PATCH 34/68] Prevent Single Product Details block from apppearing in Pages or Posts --- src/BlockTypesController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BlockTypesController.php b/src/BlockTypesController.php index 882000b8be7..dc04774cb93 100644 --- a/src/BlockTypesController.php +++ b/src/BlockTypesController.php @@ -255,6 +255,7 @@ protected function get_block_types() { 'CatalogSorting', 'ClassicTemplate', 'ProductResultsCount', + 'SingleProductDetails', 'StoreNotices', ] ); From 66595f10add88fe3f6b6cb3947ab987dde41fd30 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 1 Feb 2023 13:00:30 +0100 Subject: [PATCH 35/68] add second parameter to the subscribe function --- .../js/atomic/utils/register-block-single-product-template.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/atomic/utils/register-block-single-product-template.ts b/assets/js/atomic/utils/register-block-single-product-template.ts index ba0e891a485..80838714879 100644 --- a/assets/js/atomic/utils/register-block-single-product-template.ts +++ b/assets/js/atomic/utils/register-block-single-product-template.ts @@ -42,5 +42,6 @@ export const registerBlockSingleProductTemplate = ( { if ( block !== undefined ) { unregisterBlockFn(); } - } ); + // @ts-expect-error: The type definition for `subscribe` is incorrect. This PR fixes it: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/64177 + }, 'core/edit-site' ); }; From 5a0eb233db59a76ac8bcc562d9b23c2faa062e21 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Wed, 1 Feb 2023 13:30:49 +0100 Subject: [PATCH 36/68] Implement the new design and copy provided for the editor. --- assets/js/blocks/add-to-cart-form/edit.tsx | 4 ++-- assets/js/blocks/add-to-cart-form/editor.scss | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/edit.tsx b/assets/js/blocks/add-to-cart-form/edit.tsx index 40882487528..8dd20a4c1b1 100644 --- a/assets/js/blocks/add-to-cart-form/edit.tsx +++ b/assets/js/blocks/add-to-cart-form/edit.tsx @@ -27,7 +27,7 @@ const Edit = () => { >

    { __( - 'The content displayed on this block can vary depending on the product type. The editor preview corresponds to a Simple Product.', + 'Customers will see product add-to-cart options displayed here, dependent on the product type.', 'woo-gutenberg-products-block' ) }

    @@ -41,7 +41,7 @@ const Edit = () => { variant={ 'primary' } className={ 'wc-block-add-to-cart-form__button' } > - { __( 'Add to Cart', 'woo-gutenberg-products-block' ) } + { __( 'Add to cart', 'woo-gutenberg-products-block' ) }
    diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index df52eb6afbb..a383fa9ea70 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -1,17 +1,18 @@ .wc-block-add-to-cart-form { display: flex; flex-direction: row; + margin-left: 10px; } .wc-block-add-to-cart-form__notice { margin: 10px 0; } input.wc-block-add-to-cart-form__quantity { - width: 40px; + width: 32px; float: left; - padding: 13px 12px; + padding: 10px 7px; margin-right: 10px; - height: 20px; + height: 15px; } input[type="number"]::-webkit-inner-spin-button { @@ -20,6 +21,6 @@ input[type="number"]::-webkit-inner-spin-button { .wc-block-add-to-cart-form__button { float: left; - padding: 25px; + padding: 20px; border-radius: 0; } From dbc8f4b8d4120e296e17421dd2648d8a79895a63 Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Wed, 1 Feb 2023 13:42:12 +0100 Subject: [PATCH 37/68] Make the notice compatible with dark themes. --- assets/js/blocks/add-to-cart-form/editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index a383fa9ea70..9fd1a5def48 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -1,11 +1,11 @@ .wc-block-add-to-cart-form { display: flex; flex-direction: row; - margin-left: 10px; } .wc-block-add-to-cart-form__notice { margin: 10px 0; + color: $black; } input.wc-block-add-to-cart-form__quantity { width: 32px; From 9a5b4689d7c48802ce47717415e8eec2e1ae5bcc Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Wed, 1 Feb 2023 14:10:07 +0100 Subject: [PATCH 38/68] Some additional CSS tweaks --- assets/js/blocks/add-to-cart-form/editor.scss | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index 9fd1a5def48..4ec59537129 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -6,11 +6,12 @@ .wc-block-add-to-cart-form__notice { margin: 10px 0; color: $black; + max-width: 60%; } input.wc-block-add-to-cart-form__quantity { - width: 32px; + width: 35px; float: left; - padding: 10px 7px; + padding: 10px 8px; margin-right: 10px; height: 15px; } @@ -21,6 +22,6 @@ input[type="number"]::-webkit-inner-spin-button { .wc-block-add-to-cart-form__button { float: left; - padding: 20px; + padding: 20px 30px; border-radius: 0; } From e196c6aa655b511000e48aafa729a95895faef4f Mon Sep 17 00:00:00 2001 From: Patricia Hillebrandt Date: Wed, 1 Feb 2023 14:21:39 +0100 Subject: [PATCH 39/68] adjust the padding for the input --- assets/js/blocks/add-to-cart-form/editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/blocks/add-to-cart-form/editor.scss b/assets/js/blocks/add-to-cart-form/editor.scss index 4ec59537129..a69535e2eb7 100644 --- a/assets/js/blocks/add-to-cart-form/editor.scss +++ b/assets/js/blocks/add-to-cart-form/editor.scss @@ -11,7 +11,7 @@ input.wc-block-add-to-cart-form__quantity { width: 35px; float: left; - padding: 10px 8px; + padding: 10px 6px 10px 12px; margin-right: 10px; height: 15px; } From deaf86e5680538249ea79cafe38e0a9289f5cbc9 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 1 Feb 2023 18:44:22 +0100 Subject: [PATCH 40/68] wrap the Single Product Template in a div with the product class --- src/BlockTemplatesController.php | 8 +++ src/Templates/BlockTemplatesCompatibility.php | 64 +++++++++++++++++++ .../BlockTemplatesCompatibilityTests.php | 48 ++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 tests/php/Templates/BlockTemplatesCompatibilityTests.php diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index 06a339b779f..143e1d37d79 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Blocks; use Automattic\WooCommerce\Blocks\Domain\Package; +use Automattic\WooCommerce\Blocks\Templates\BlockTemplatesCompatibility; use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; @@ -322,6 +323,13 @@ function( $template ) { if ( ! $template->description ) { $template->description = BlockTemplateUtils::get_block_template_description( $template->slug ); } + + if ( 'single-product' === $template->slug ) { + $new_content = BlockTemplatesCompatibility::wrap_single_product_template( $template->content ); + $template->content = $new_content; + return $template; + } + return $template; }, $query_result diff --git a/src/Templates/BlockTemplatesCompatibility.php b/src/Templates/BlockTemplatesCompatibility.php index 8177b8bc7ed..77ad2e73791 100644 --- a/src/Templates/BlockTemplatesCompatibility.php +++ b/src/Templates/BlockTemplatesCompatibility.php @@ -361,4 +361,68 @@ protected function inject_attribute( &$block ) { } } + /** + * For compatibility reason, we need to wrap the Single Product template in a div with specific class. + * For more details, see https://github.com/woocommerce/woocommerce-blocks/issues/8314. + * + * @param string $template_content Template Content. + * @return string Wrapped template content inside a div. + */ + public static function wrap_single_product_template( $template_content ) { + $parsed_blocks = parse_blocks( $template_content ); + $last_template_parts_before_template_index = self::find_first_block( $parsed_blocks ); + + $wrap_block_group = self::create_wrap_block_group( + $parsed_blocks[ $last_template_parts_before_template_index ] + ); + + $parsed_blocks[ $last_template_parts_before_template_index ] = $wrap_block_group[0]; + + return serialize_blocks( $parsed_blocks ); + } + + /** + * Find the first block that is not a template part. + * + * @param array $parsed_blocks Array of parsed block objects. + */ + private static function find_first_block( $parsed_blocks ) { + $last_template_parts_before_template = null; + foreach ( $parsed_blocks as $key => $value ) { + if ( 'core/template-part' === $value['blockName'] || empty( $value['blockName'] ) ) { + continue; + } + + $last_template_parts_before_template = $key; + break; + } + return $last_template_parts_before_template; + } + + /** + * Wrap all the blocks inside the template in a group block. + * + * @param array $inner_blocks Array of parsed block objects. + * @return array Group block with the inner blocks. + */ + private static function create_wrap_block_group( $inner_blocks ) { + $serialized_blocks = serialize_block( $inner_blocks ); + + $new_block = parse_blocks( + sprintf( + ' +
    + %1$s +
    + ', + $serialized_blocks + ) + ); + + $new_block['innerBlocks'] = $inner_blocks; + + return $new_block; + + } + } diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php new file mode 100644 index 00000000000..e43c1c9d954 --- /dev/null +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -0,0 +1,48 @@ + + +
    + +
    + + '; + + $expected_single_product_template = ' + + +
    + +
    + +
    + +
    + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } +} From edcac215129eb4b998474c4b83a64a5c02f77e2a Mon Sep 17 00:00:00 2001 From: Alexandre Lara Date: Wed, 1 Feb 2023 16:36:32 -0300 Subject: [PATCH 41/68] Fix PHP Coding Standards warnings --- src/BlockTypes/SingleProductDetails.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/BlockTypes/SingleProductDetails.php b/src/BlockTypes/SingleProductDetails.php index 17fb82cadad..bda86189f87 100644 --- a/src/BlockTypes/SingleProductDetails.php +++ b/src/BlockTypes/SingleProductDetails.php @@ -34,11 +34,16 @@ protected function render( $attributes, $content, $block ) { %2$s
    ', esc_attr( $classname ), - $tabs, + $tabs ); } - protected function render_tabs(){ + /** + * Gets the tabs with their content to be rendered by the block. + * + * @return string The tabs html to be rendered by the block + */ + protected function render_tabs() { ob_start(); while ( have_posts() ) { From 95a05a49b70e601236f095deb26cebe7be5e6675 Mon Sep 17 00:00:00 2001 From: Luigi Date: Thu, 2 Feb 2023 16:32:02 +0100 Subject: [PATCH 42/68] improve logic and increase coverage of unit test --- src/Templates/BlockTemplatesCompatibility.php | 48 ++++++- .../BlockTemplatesCompatibilityTests.php | 124 +++++++++++++++++- 2 files changed, 164 insertions(+), 8 deletions(-) diff --git a/src/Templates/BlockTemplatesCompatibility.php b/src/Templates/BlockTemplatesCompatibility.php index 77ad2e73791..96d6492820e 100644 --- a/src/Templates/BlockTemplatesCompatibility.php +++ b/src/Templates/BlockTemplatesCompatibility.php @@ -371,9 +371,21 @@ protected function inject_attribute( &$block ) { public static function wrap_single_product_template( $template_content ) { $parsed_blocks = parse_blocks( $template_content ); $last_template_parts_before_template_index = self::find_first_block( $parsed_blocks ); + $parsed_block_without_template_parts = array_filter( + $parsed_blocks, + function( $block ) { + return 'core/template-part' !== $block['blockName'] && ! empty( $block['blockName'] ); + } + ); + + $single_product_template_blocks = array( 'woocommerce/breadcrumbs', 'woocommerce/product-gallery-image', 'woocommerce/product-add-to-cart', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); + + if ( ! self::has_single_product_template_blocks( $parsed_block_without_template_parts, $single_product_template_blocks ) ) { + return $template_content; + } $wrap_block_group = self::create_wrap_block_group( - $parsed_blocks[ $last_template_parts_before_template_index ] + $parsed_block_without_template_parts ); $parsed_blocks[ $last_template_parts_before_template_index ] = $wrap_block_group[0]; @@ -402,11 +414,11 @@ private static function find_first_block( $parsed_blocks ) { /** * Wrap all the blocks inside the template in a group block. * - * @param array $inner_blocks Array of parsed block objects. - * @return array Group block with the inner blocks. + * @param array $blocks Array of parsed block objects. + * @return array Group block with the blocks inside. */ - private static function create_wrap_block_group( $inner_blocks ) { - $serialized_blocks = serialize_block( $inner_blocks ); + private static function create_wrap_block_group( $blocks ) { + $serialized_blocks = serialize_blocks( $blocks ); $new_block = parse_blocks( sprintf( @@ -419,10 +431,34 @@ private static function create_wrap_block_group( $inner_blocks ) { ) ); - $new_block['innerBlocks'] = $inner_blocks; + $new_block['innerBlocks'] = $blocks; return $new_block; } + /** + * Check if the Single Product template has a single product template block: + * [woocommerce/breadcrumbs, woocommerce/product-gallery-image, woocommerce/product-add-to-cart, woocommerce/product-details, woocommerce/add-to-cart-form] + * + * @param array $parsed_blocks Array of parsed block objects. + * @param array $single_product_template_blocks Array of single product template blocks. + * @return bool True if the template has a single product template block, false otherwise. + */ + private static function has_single_product_template_blocks( $parsed_blocks, $single_product_template_blocks ) { + $found = false; + + foreach ( $parsed_blocks as $block ) { + if ( isset( $block['blockName'] ) && in_array( $block['blockName'], $single_product_template_blocks, true ) ) { + $found = true; + break; + } + $found = self::has_single_product_template_blocks( $block['innerBlocks'], $single_product_template_blocks ); + if ( $found ) { + break; + } + } + return $found; + } + } diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php index e43c1c9d954..0def62e7391 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -12,7 +12,8 @@ class BlockTemplatesCompatibilityTests extends WP_UnitTestCase { /** - * Test that the default Single Product Template is wrapped in a div with the correct class. + * Test that the default Single Product Template is not wrapped in a div. + * */ public function test_wrap_single_product_template_with_default_single_product_template() { @@ -25,13 +26,44 @@ public function test_wrap_single_product_template_with_default_single_product_te '; + $expected_single_product_template = ' + + +
    + +
    + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } + + /** + * Test that the default Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. + */ + public function test_wrap_single_product_template_if_contains_single_product_blocks() { + + $default_single_product_template = ' + + +
    + +
    + + '; + $expected_single_product_template = '
    - +
    @@ -45,4 +77,92 @@ public function test_wrap_single_product_template_with_default_single_product_te $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); } + + /** + * Test that the default Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. + */ + public function test_wrap_single_product_template_if_contains_nested_single_product_blocks() { + + $default_single_product_template = ' + + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + +

    + +
    +
    + + + '; + + $expected_single_product_template = ' + + +
    +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + +

    + +
    +
    +
    + + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } } From 1568ec2425257c886a2120b33b0ca2ceea758c3f Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Feb 2023 12:36:18 +0100 Subject: [PATCH 43/68] improve logic and increase coverage of unit test --- src/Templates/BlockTemplatesCompatibility.php | 70 ++-- .../BlockTemplatesCompatibilityTests.php | 317 +++++++++++++----- 2 files changed, 276 insertions(+), 111 deletions(-) diff --git a/src/Templates/BlockTemplatesCompatibility.php b/src/Templates/BlockTemplatesCompatibility.php index 96d6492820e..44f2a0b2509 100644 --- a/src/Templates/BlockTemplatesCompatibility.php +++ b/src/Templates/BlockTemplatesCompatibility.php @@ -369,9 +369,10 @@ protected function inject_attribute( &$block ) { * @return string Wrapped template content inside a div. */ public static function wrap_single_product_template( $template_content ) { - $parsed_blocks = parse_blocks( $template_content ); - $last_template_parts_before_template_index = self::find_first_block( $parsed_blocks ); - $parsed_block_without_template_parts = array_filter( + $parsed_blocks = parse_blocks( $template_content ); + $splitted_blocks = self::split_blocks_by_type( $parsed_blocks ); + + $parsed_block_without_template_parts = array_filter( $parsed_blocks, function( $block ) { return 'core/template-part' !== $block['blockName'] && ! empty( $block['blockName'] ); @@ -385,30 +386,14 @@ function( $block ) { } $wrap_block_group = self::create_wrap_block_group( - $parsed_block_without_template_parts + $splitted_blocks['blocks'] ); - $parsed_blocks[ $last_template_parts_before_template_index ] = $wrap_block_group[0]; - - return serialize_blocks( $parsed_blocks ); - } + $headers = serialize_blocks( $splitted_blocks['pre-blocks-template-parts'] ); + $blocks = serialize_block( $wrap_block_group[0] ); + $footer = serialize_blocks( $splitted_blocks['post-blocks-template-parts'] ); - /** - * Find the first block that is not a template part. - * - * @param array $parsed_blocks Array of parsed block objects. - */ - private static function find_first_block( $parsed_blocks ) { - $last_template_parts_before_template = null; - foreach ( $parsed_blocks as $key => $value ) { - if ( 'core/template-part' === $value['blockName'] || empty( $value['blockName'] ) ) { - continue; - } - - $last_template_parts_before_template = $key; - break; - } - return $last_template_parts_before_template; + return $headers . $blocks . $footer; } /** @@ -461,4 +446,41 @@ private static function has_single_product_template_blocks( $parsed_blocks, $sin return $found; } + + /** + * Create a dictionary of blocks split by type: + * template parts before the blocks. + * blocks (they can include a template part). + * template parts after the blocks. + * + * @param array $parsed_blocks Array of parsed block objects. + * @return array Array of blocks split by type. + */ + private static function split_blocks_by_type( $parsed_blocks ) { + return array_reduce( + $parsed_blocks, + function( $carry, $block ) { + if ( 'core/template-part' === $block['blockName'] ) { + if ( empty( $carry['blocks'] ) ) { + array_push( $carry['pre-blocks-template-parts'], $block ); + return $carry; + } else { + array_push( $carry['post-blocks-template-parts'], $block ); + return $carry; + } + } + if ( empty( $block['blockName'] ) ) { + return $carry; + } + array_push( $carry['blocks'], $block ); + return $carry; + }, + array( + 'pre-blocks-template-parts' => array(), + 'blocks' => array(), + 'post-blocks-template-parts' => array(), + ) + ); + } + } diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php index 0def62e7391..4dc373df27c 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -15,25 +15,25 @@ class BlockTemplatesCompatibilityTests extends WP_UnitTestCase { * Test that the default Single Product Template is not wrapped in a div. * */ - public function test_wrap_single_product_template_with_default_single_product_template() { + public function test_no_wrap_single_product_template_with_default_single_product_template() { $default_single_product_template = ' - - -
    - -
    - - '; + + +
    + +
    + + '; $expected_single_product_template = ' - - -
    - -
    - - '; + + +
    + +
    + + '; $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); @@ -44,31 +44,31 @@ public function test_wrap_single_product_template_with_default_single_product_te } /** - * Test that the default Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. */ public function test_wrap_single_product_template_if_contains_single_product_blocks() { $default_single_product_template = ' - - -
    - -
    - - '; + + +
    + +
    + + '; $expected_single_product_template = ' - - -
    - -
    - -
    - -
    - - '; + + +
    + +
    + +
    + +
    + + '; $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); @@ -79,84 +79,227 @@ public function test_wrap_single_product_template_if_contains_single_product_blo } /** - * Test that the default Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. */ public function test_wrap_single_product_template_if_contains_nested_single_product_blocks() { $default_single_product_template = ' - + + +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +

    + + +
    + +
    + + '; - -
    -
    - + $expected_single_product_template = ' + + +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +

    + + +
    + +
    + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } + + /** + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. + */ + public function test_wrap_single_product_template_without_a_main_wrapper() { - -
    - + $default_single_product_template = ' + + + - + +
    + - + - - + - - + + - + + - - + - - -

    - -
    -
    - + + - '; + + +

    + +
    + + + '; $expected_single_product_template = ' - - -
    -
    -
    - + + + +
    - -
    - + +
    + - + - + - - + + - - + + + + + + + + + + +

    + +
    +
    + + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } + + /** + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. + */ + public function test_wrap_single_product_template_with_multiple_header() { + + $default_single_product_template = ' + + + + + + + '; + + $expected_single_product_template = ' + + + + + +
    + +
    + + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } + + + /** + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. + */ + public function test_wrap_single_product_template_with_multiple_footer() { + + $default_single_product_template = ' + + + + + + + '; + + $expected_single_product_template = ' + - - - + +
    - - -

    - -
    -
    -
    - +
    + - '; + + '; $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); From e82ab2708f070fd202621cc359d2c02d5cfce2c8 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Feb 2023 12:51:25 +0100 Subject: [PATCH 44/68] fix test --- .../BlockTemplatesCompatibilityTests.php | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php index 4dc373df27c..4bfb58e5643 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -118,33 +118,37 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod $expected_single_product_template = ' - -
    - -
    - -
    - - -
    - - - - - - - - - - - - - -

    - - -
    - + +
    + +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +

    + + +
    + +
    +
    '; From 36d5ebf37c814edba90dedbf0854458fcc96cc77 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Feb 2023 12:56:08 +0100 Subject: [PATCH 45/68] format HTML --- .../BlockTemplatesCompatibilityTests.php | 116 +++++++----------- 1 file changed, 45 insertions(+), 71 deletions(-) diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php index 4bfb58e5643..f385cb2b4f5 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -168,70 +168,56 @@ public function test_wrap_single_product_template_without_a_main_wrapper() { $default_single_product_template = ' - - -
    - - - - - - - - - - - - - - - - - - - -

    - -
    +
    + + + + + + + + + + + + + +

    + + +
    - '; $expected_single_product_template = ' - -
    - - -
    - - - - - - - - - - - - - - - - - - - -

    - -
    -
    +
    + + +
    + + + + + + + + + + + + + +

    + + +
    + +
    - '; $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); @@ -250,23 +236,17 @@ public function test_wrap_single_product_template_with_multiple_header() { $default_single_product_template = ' - - - '; $expected_single_product_template = ' - - -
    - +
    +
    - '; $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); @@ -285,23 +265,17 @@ public function test_wrap_single_product_template_with_multiple_footer() { $default_single_product_template = ' - - - '; $expected_single_product_template = ' - - -
    - +
    +
    - '; From 4e76b079bf4666647291467b49bbef810849afe1 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Feb 2023 20:12:17 +0100 Subject: [PATCH 46/68] fix edge case --- src/Templates/BlockTemplatesCompatibility.php | 82 ++++++++++--------- .../BlockTemplatesCompatibilityTests.php | 41 ++++++++++ 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/src/Templates/BlockTemplatesCompatibility.php b/src/Templates/BlockTemplatesCompatibility.php index 44f2a0b2509..a249b9aaf31 100644 --- a/src/Templates/BlockTemplatesCompatibility.php +++ b/src/Templates/BlockTemplatesCompatibility.php @@ -369,31 +369,42 @@ protected function inject_attribute( &$block ) { * @return string Wrapped template content inside a div. */ public static function wrap_single_product_template( $template_content ) { - $parsed_blocks = parse_blocks( $template_content ); - $splitted_blocks = self::split_blocks_by_type( $parsed_blocks ); - - $parsed_block_without_template_parts = array_filter( - $parsed_blocks, - function( $block ) { - return 'core/template-part' !== $block['blockName'] && ! empty( $block['blockName'] ); - } - ); + $parsed_blocks = parse_blocks( $template_content ); + $grouped_blocks = self::group_blocks( $parsed_blocks ); $single_product_template_blocks = array( 'woocommerce/breadcrumbs', 'woocommerce/product-gallery-image', 'woocommerce/product-add-to-cart', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); - if ( ! self::has_single_product_template_blocks( $parsed_block_without_template_parts, $single_product_template_blocks ) ) { - return $template_content; - } + $wrapped_blocks = array_map( + function( $blocks ) use ( $single_product_template_blocks ) { + $has_single_product_template_blocks = false; - $wrap_block_group = self::create_wrap_block_group( - $splitted_blocks['blocks'] + if ( 'core/template-part' === $blocks[0]['blockName'] ) { + return $blocks; + } + + $has_single_product_template_blocks = self::has_single_product_template_blocks( $blocks, $single_product_template_blocks ); + + if ( $has_single_product_template_blocks ) { + $wrapped_block = self::create_wrap_block_group( $blocks ); + return array( $wrapped_block[0] ); + } + return $blocks; + }, + $grouped_blocks ); - $headers = serialize_blocks( $splitted_blocks['pre-blocks-template-parts'] ); - $blocks = serialize_block( $wrap_block_group[0] ); - $footer = serialize_blocks( $splitted_blocks['post-blocks-template-parts'] ); + $template = array_reduce( + $wrapped_blocks, + function( $carry, $item ) { + if ( is_array( $item ) ) { + return $carry . serialize_blocks( $item ); + } + return $carry . serialize_block( $item ); + }, + '' + ); - return $headers . $blocks . $footer; + return $template; } /** @@ -448,38 +459,35 @@ private static function has_single_product_template_blocks( $parsed_blocks, $sin /** - * Create a dictionary of blocks split by type: - * template parts before the blocks. - * blocks (they can include a template part). - * template parts after the blocks. + * Group blocks in this way: + * B1 + TP1 + B2 + B3 + B4 + TP2 + B5 + * (B = Block, TP = Template Part) + * becomes: + * [[B1], [TP1], [B2, B3, B4], [TP2], [B5]] * * @param array $parsed_blocks Array of parsed block objects. - * @return array Array of blocks split by type. + * @return array Array of blocks grouped by template part. */ - private static function split_blocks_by_type( $parsed_blocks ) { + private static function group_blocks( $parsed_blocks ) { return array_reduce( $parsed_blocks, function( $carry, $block ) { if ( 'core/template-part' === $block['blockName'] ) { - if ( empty( $carry['blocks'] ) ) { - array_push( $carry['pre-blocks-template-parts'], $block ); - return $carry; - } else { - array_push( $carry['post-blocks-template-parts'], $block ); - return $carry; - } + array_push( $carry, array( $block ) ); + return $carry; } if ( empty( $block['blockName'] ) ) { return $carry; } - array_push( $carry['blocks'], $block ); + $last_element_index = count( $carry ) - 1; + if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && 'core/template-part' !== $carry[ $last_element_index ][0]['blockName'] ) { + array_push( $carry[ $last_element_index ], $block ); + return $carry; + } + array_push( $carry, array( $block ) ); return $carry; }, - array( - 'pre-blocks-template-parts' => array(), - 'blocks' => array(), - 'post-blocks-template-parts' => array(), - ) + array() ); } diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php index f385cb2b4f5..2e2656806e5 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -286,4 +286,45 @@ public function test_wrap_single_product_template_with_multiple_footer() { $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); } + + /** + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. + */ + public function test_wrap_single_product_template_with_multiple_blocks_related_to_the_single_product_template() { + + $default_single_product_template = ' + +

    test

    + + + + + + '; + + $expected_single_product_template = ' + +

    test

    + + + +
    + +
    + + + +
    + +
    + + '; + + $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + + $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + } } From 8aee84125143d2ecf3eaf3af1b74ca449b034cb2 Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 13 Feb 2023 11:13:53 +0100 Subject: [PATCH 47/68] update @types/wordpress__data package --- .../utils/register-block-single-product-template.ts | 1 - package-lock.json | 11 +++++++---- package.json | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/assets/js/atomic/utils/register-block-single-product-template.ts b/assets/js/atomic/utils/register-block-single-product-template.ts index 80838714879..675e335b06d 100644 --- a/assets/js/atomic/utils/register-block-single-product-template.ts +++ b/assets/js/atomic/utils/register-block-single-product-template.ts @@ -42,6 +42,5 @@ export const registerBlockSingleProductTemplate = ( { if ( block !== undefined ) { unregisterBlockFn(); } - // @ts-expect-error: The type definition for `subscribe` is incorrect. This PR fixes it: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/64177 }, 'core/edit-site' ); }; diff --git a/package-lock.json b/package-lock.json index 96d1f2aea73..06fdcaaf453 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,7 @@ "@types/wordpress__blocks": "11.0.7", "@types/wordpress__components": "^23.0.0", "@types/wordpress__core-data": "^2.4.5", - "@types/wordpress__data": "^6.0.1", + "@types/wordpress__data": "^6.0.2", "@types/wordpress__data-controls": "2.2.0", "@types/wordpress__editor": "^11.0.0", "@types/wordpress__notices": "^3.5.0", @@ -11680,9 +11680,10 @@ "license": "MIT" }, "node_modules/@types/wordpress__data": { - "version": "6.0.1", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/wordpress__data/-/wordpress__data-6.0.2.tgz", + "integrity": "sha512-Pu67knXXoTWgCpxTKwePNZz/iKkYe8AQbkkSD/Ba1mw8t4zgEM+jJs5IV5N5ij/awwjs4Subj8mkvS3jMTDwyw==", "dev": true, - "license": "MIT", "dependencies": { "@types/react": "*", "redux": "^4.1.0" @@ -58730,7 +58731,9 @@ "dev": true }, "@types/wordpress__data": { - "version": "6.0.1", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/wordpress__data/-/wordpress__data-6.0.2.tgz", + "integrity": "sha512-Pu67knXXoTWgCpxTKwePNZz/iKkYe8AQbkkSD/Ba1mw8t4zgEM+jJs5IV5N5ij/awwjs4Subj8mkvS3jMTDwyw==", "dev": true, "requires": { "@types/react": "*", diff --git a/package.json b/package.json index 6b24618f34c..9474f765df1 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "@types/wordpress__blocks": "11.0.7", "@types/wordpress__components": "^23.0.0", "@types/wordpress__core-data": "^2.4.5", - "@types/wordpress__data": "^6.0.1", + "@types/wordpress__data": "^6.0.2", "@types/wordpress__data-controls": "2.2.0", "@types/wordpress__editor": "^11.0.0", "@types/wordpress__notices": "^3.5.0", From 32f97dd1f85368c80e105ebd118f7a938f1e6857 Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 13 Feb 2023 11:51:36 +0100 Subject: [PATCH 48/68] update placeholder, icon and description --- .../product-elements/product-image-gallery/block.json | 4 ++-- .../product-elements/product-image-gallery/edit.tsx | 4 ++-- .../product-elements/product-image-gallery/editor.scss | 1 + .../product-elements/product-image-gallery/index.ts | 2 ++ images/block-placeholders/product-image-gallery.svg | 9 +++++++++ 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 images/block-placeholders/product-image-gallery.svg diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json index 9dd08179132..0a1247cbc34 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json @@ -2,8 +2,8 @@ "name": "woocommerce/product-image-gallery", "version": "1.0.0", "title": "Product Image Gallery", - "icon": "image", - "description": "Display the main product images.", + "icon": "gallery", + "description": "Display a product's images.", "category": "woocommerce", "supports": { "align": true, diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx index da45b7bb8c4..df6fb8ed95e 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx @@ -16,7 +16,7 @@ const Placeholder = () => { return (
    Placeholder
    @@ -24,7 +24,7 @@ const Placeholder = () => { return ( Placeholder ); diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss index 962cafb0cd4..40696afdbba 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss @@ -7,6 +7,7 @@ img { width: 100px; height: 100px; + margin: 5px; } } } diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts index 2f37200749b..7c00f9ccf6e 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts @@ -1,6 +1,7 @@ /** * External dependencies */ +import { gallery as icon } from '@wordpress/icons'; import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils'; @@ -13,6 +14,7 @@ import metadata from './block.json'; registerBlockSingleProductTemplate( { registerBlockFn: () => { registerBlockType( metadata, { + icon, edit, } ); }, diff --git a/images/block-placeholders/product-image-gallery.svg b/images/block-placeholders/product-image-gallery.svg new file mode 100644 index 00000000000..a9a75f82cf4 --- /dev/null +++ b/images/block-placeholders/product-image-gallery.svg @@ -0,0 +1,9 @@ + + + + Layer 1 + + + + + \ No newline at end of file From e3be328a98a76aff295f7287cd70d8c826bc46aa Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 13 Feb 2023 12:05:31 +0100 Subject: [PATCH 49/68] update tsconfig --- .../blocks/product-elements/product-image-gallery/index.ts | 1 + tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts index 7c00f9ccf6e..ca81e18eee9 100644 --- a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts +++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts @@ -13,6 +13,7 @@ import metadata from './block.json'; registerBlockSingleProductTemplate( { registerBlockFn: () => { + // @ts-expect-error: `registerBlockType` is a function that is typed in WordPress core. registerBlockType( metadata, { icon, edit, diff --git a/tsconfig.json b/tsconfig.json index 5dccc2bbc15..2a1a5b6c03b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "include": [ "./assets/js/**/*", "./packages/checkout/**/*", + "./assets/js/blocks/**/block.json", "./assets/js/atomic/blocks/**/block.json", "./assets/js/blocks/mini-cart/mini-cart-contents/inner-blocks/**/block.json", "./storybook/**/*", From 0ba35e177114ed68a8559b81c091b37fbca08961 Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 13 Feb 2023 14:11:48 +0100 Subject: [PATCH 50/68] update block name --- assets/js/blocks/single-product-details/block.json | 2 +- .../__fixtures__/single-product-details.fixture.json | 2 +- tests/e2e/specs/backend/single-produt-details.test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/single-product-details/block.json b/assets/js/blocks/single-product-details/block.json index 97db7cb121a..ac3f47665af 100644 --- a/assets/js/blocks/single-product-details/block.json +++ b/assets/js/blocks/single-product-details/block.json @@ -1,7 +1,7 @@ { "name": "woocommerce/single-product-details", "version": "1.0.0", - "title": "Single Product Details", + "title": "Product Details", "description": "A block that allows your customers to see details and reviews about the product.", "category": "woocommerce", "keywords": [ "WooCommerce" ], diff --git a/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json b/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json index e8acbacb003..1df5149a095 100644 --- a/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json +++ b/tests/e2e/config/custom-matchers/__fixtures__/single-product-details.fixture.json @@ -1 +1 @@ -{"title":"Single Product Details Block","pageContent":""} \ No newline at end of file +{"title":"Product Details Block","pageContent":""} diff --git a/tests/e2e/specs/backend/single-produt-details.test.js b/tests/e2e/specs/backend/single-produt-details.test.js index 5a6bc6f3cef..81eabe5fa40 100644 --- a/tests/e2e/specs/backend/single-produt-details.test.js +++ b/tests/e2e/specs/backend/single-produt-details.test.js @@ -10,7 +10,7 @@ import { visitBlockPage } from '@woocommerce/blocks-test-utils'; import { insertBlockDontWaitForInsertClose } from '../../utils.js'; const block = { - name: 'Single Product Details', + name: 'Product Details', slug: 'woocommerce/single-product-details', class: '.wc-block-single-product-details', }; From 93bcb28c76404a2b04ecce39cd203e1e3d83f0ea Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 13 Feb 2023 14:18:40 +0100 Subject: [PATCH 51/68] fix SCSS linter error --- assets/js/blocks/single-product-details/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/blocks/single-product-details/style.scss b/assets/js/blocks/single-product-details/style.scss index 8eb3a59b08c..e32e9c1da22 100644 --- a/assets/js/blocks/single-product-details/style.scss +++ b/assets/js/blocks/single-product-details/style.scss @@ -26,7 +26,7 @@ &:hover { text-decoration: none; - color: lighten($black, 10%); + color: color.adjust($black, $lightness: 10%); } } From 6413df803b65f4de229a617926cc4a64fb4dc768 Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 14 Feb 2023 12:52:38 +0100 Subject: [PATCH 52/68] address feedback --- src/Templates/BlockTemplatesCompatibility.php | 7 ++--- .../BlockTemplatesCompatibilityTests.php | 28 +++++++++---------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Templates/BlockTemplatesCompatibility.php b/src/Templates/BlockTemplatesCompatibility.php index a249b9aaf31..b5e82dafee7 100644 --- a/src/Templates/BlockTemplatesCompatibility.php +++ b/src/Templates/BlockTemplatesCompatibility.php @@ -372,12 +372,11 @@ public static function wrap_single_product_template( $template_content ) { $parsed_blocks = parse_blocks( $template_content ); $grouped_blocks = self::group_blocks( $parsed_blocks ); - $single_product_template_blocks = array( 'woocommerce/breadcrumbs', 'woocommerce/product-gallery-image', 'woocommerce/product-add-to-cart', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); + // WIP: The list of blocks is WIP. + $single_product_template_blocks = array( 'woocommerce/product-gallery-image', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); $wrapped_blocks = array_map( function( $blocks ) use ( $single_product_template_blocks ) { - $has_single_product_template_blocks = false; - if ( 'core/template-part' === $blocks[0]['blockName'] ) { return $blocks; } @@ -435,7 +434,7 @@ private static function create_wrap_block_group( $blocks ) { /** * Check if the Single Product template has a single product template block: - * [woocommerce/breadcrumbs, woocommerce/product-gallery-image, woocommerce/product-add-to-cart, woocommerce/product-details, woocommerce/add-to-cart-form] + * woocommerce/product-gallery-image, woocommerce/product-details, woocommerce/add-to-cart-form] * * @param array $parsed_blocks Array of parsed block objects. * @param array $single_product_template_blocks Array of single product template blocks. diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php index 2e2656806e5..d540c2f1d2c 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/BlockTemplatesCompatibilityTests.php @@ -52,7 +52,7 @@ public function test_wrap_single_product_template_if_contains_single_product_blo
    - +
    '; @@ -63,7 +63,7 @@ public function test_wrap_single_product_template_if_contains_single_product_blo
    - +
    @@ -89,7 +89,7 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod
    - +
    @@ -124,7 +124,7 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod
    - +
    @@ -168,7 +168,7 @@ public function test_wrap_single_product_template_without_a_main_wrapper() { $default_single_product_template = ' - +
    @@ -195,7 +195,7 @@ public function test_wrap_single_product_template_without_a_main_wrapper() {
    - +
    @@ -236,7 +236,7 @@ public function test_wrap_single_product_template_with_multiple_header() { $default_single_product_template = ' - + '; $expected_single_product_template = ' @@ -244,7 +244,7 @@ public function test_wrap_single_product_template_with_multiple_header() {
    - +
    '; @@ -265,7 +265,7 @@ public function test_wrap_single_product_template_with_multiple_footer() { $default_single_product_template = ' - + '; @@ -273,7 +273,7 @@ public function test_wrap_single_product_template_with_multiple_footer() {
    - +
    @@ -297,9 +297,9 @@ public function test_wrap_single_product_template_with_multiple_blocks_related_t

    test

    - + - + '; $expected_single_product_template = ' @@ -309,13 +309,13 @@ public function test_wrap_single_product_template_with_multiple_blocks_related_t
    - +
    - +
    '; From 5a8c13f1dff91710339a36dda1563e0708e187c3 Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 14 Feb 2023 13:41:37 +0100 Subject: [PATCH 53/68] create SingleProductTemplateCompatibility class --- src/Domain/Bootstrap.php | 9 + .../AbstractTemplateCompatibility.php | 157 +++++++++++++ src/Templates/BlockTemplatesCompatibility.php | 215 +----------------- .../SingleProductTemplateCompatibility.php | 141 ++++++++++++ ...ngleProductTemplateCompatibilityTests.php} | 20 +- 5 files changed, 318 insertions(+), 224 deletions(-) create mode 100644 src/Templates/AbstractTemplateCompatibility.php create mode 100644 src/Templates/SingleProductTemplateCompatibility.php rename tests/php/Templates/{BlockTemplatesCompatibilityTests.php => SingleProductTemplateCompatibilityTests.php} (94%) diff --git a/src/Domain/Bootstrap.php b/src/Domain/Bootstrap.php index c42d39028a2..62ffd1611af 100644 --- a/src/Domain/Bootstrap.php +++ b/src/Domain/Bootstrap.php @@ -29,6 +29,7 @@ use Automattic\WooCommerce\StoreApi\SchemaController; use Automattic\WooCommerce\StoreApi\StoreApi; use Automattic\WooCommerce\Blocks\Shipping\ShippingController; +use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility; /** * Takes care of bootstrapping the plugin. @@ -129,6 +130,7 @@ function() { $this->container->get( ProductAttributeTemplate::class ); $this->container->get( ClassicTemplatesCompatibility::class ); $this->container->get( BlockTemplatesCompatibility::class ); + $this->container->get( SingleProductTemplateCompatibility::class ); $this->container->get( BlockPatterns::class ); $this->container->get( PaymentsApi::class ); $this->container->get( ShippingController::class )->init(); @@ -280,6 +282,13 @@ function () { return new BlockTemplatesCompatibility(); } ); + + $this->container->register( + SingleProductTemplateCompatibility::class, + function () { + return new SingleProductTemplateCompatibility(); + } + ); $this->container->register( DraftOrders::class, function( Container $container ) { diff --git a/src/Templates/AbstractTemplateCompatibility.php b/src/Templates/AbstractTemplateCompatibility.php new file mode 100644 index 00000000000..3f10d6fa1c2 --- /dev/null +++ b/src/Templates/AbstractTemplateCompatibility.php @@ -0,0 +1,157 @@ +set_hook_data(); + $this->init(); + } + + /** + * Initialization method. + */ + protected function init() { + if ( ! wc_current_theme_is_fse_theme() ) { + return; + } + + add_filter( 'render_block_data', array( $this, 'update_render_block_data' ), 10, 3 ); + add_filter( 'render_block', array( $this, 'inject_hooks' ), 10, 2 ); + } + + /** + * Update the render block data to inject our custom attribute needed to + * determine which blocks belong to an inherited Products block. + * + * @param array $parsed_block The block being rendered. + * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content. + * @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block. + * + * @return array + */ + abstract public function update_render_block_data( $parsed_block, $source_block, $parent_block ); + + /** + * Inject hooks to rendered content of corresponding blocks. + * + * @param mixed $block_content The rendered block content. + * @param mixed $block The parsed block data. + * @return string + */ + abstract public function inject_hooks( $block_content, $block ); + + /** + * The hook data to inject to the rendered content of blocks. This also + * contains hooked functions that will be removed by remove_default_hooks. + * + * The array format: + * [ + * => [ + * block_name => , + * position => before|after, + * hooked => [ + * => , + * ... + * ], + * ], + * ] + * Where: + * - hook-name is the name of the hook that will be replaced. + * - block-name is the name of the block that will replace the hook. + * - position is the position of the block relative to the hook. + * - hooked is an array of functions hooked to the hook that will be + * replaced. The key is the function name and the value is the + * priority. + */ + abstract protected function set_hook_data(); + + + /** + * Remove the default callback added by WooCommerce. We replaced these + * callbacks by blocks so we have to remove them to prevent duplicated + * content. + */ + protected function remove_default_hooks() { + foreach ( $this->hook_data as $hook => $data ) { + if ( ! isset( $data['hooked'] ) ) { + continue; + } + foreach ( $data['hooked'] as $callback => $priority ) { + remove_action( $hook, $callback, $priority ); + } + } + + /** + * When extensions implement their equivalent blocks of the template + * hook functions, they can use this filter to register their old hooked + * data here, so in the blockified template, the old hooked functions + * can be removed in favor of the new blocks while keeping the old + * hooked functions working in classic templates. + * + * Accepts an array of hooked data. The array should be in the following + * format: + * [ + * [ + * hook => , + * function => , + * priority => , + * ], + * ... + * ] + * Where: + * - hook-name is the name of the hook that have the functions hooked to. + * - function-name is the hooked function name. + * - priority is the priority of the hooked function. + * + * @param array $data Additional hooked data. Default to empty + */ + $additional_hook_data = apply_filters( 'woocommerce_blocks_hook_compatibility_additional_data', array() ); + + if ( empty( $additional_hook_data ) || ! is_array( $additional_hook_data ) ) { + return; + } + + foreach ( $additional_hook_data as $data ) { + if ( ! isset( $data['hook'], $data['function'], $data['priority'] ) ) { + continue; + } + remove_action( $data['hook'], $data['function'], $data['priority'] ); + } + } + + /** + * Get the buffer content of the hooks to append/prepend to render content. + * + * @param array $hooks The hooks to be rendered. + * @param string $position The position of the hooks. + * + * @return string + */ + protected function get_hooks_buffer( $hooks, $position ) { + ob_start(); + foreach ( $hooks as $hook => $data ) { + if ( $data['position'] === $position ) { + do_action( $hook ); + } + } + return ob_get_clean(); + } +} diff --git a/src/Templates/BlockTemplatesCompatibility.php b/src/Templates/BlockTemplatesCompatibility.php index b5e82dafee7..0a75d15d4d8 100644 --- a/src/Templates/BlockTemplatesCompatibility.php +++ b/src/Templates/BlockTemplatesCompatibility.php @@ -8,7 +8,7 @@ * * @internal */ -class BlockTemplatesCompatibility { +class BlockTemplatesCompatibility extends AbstractTemplateCompatibility { /** * The custom ID of the loop item block as the replacement of the core/null block. @@ -31,18 +31,6 @@ public function __construct() { $this->init(); } - /** - * Initialization method. - */ - protected function init() { - if ( ! wc_current_theme_is_fse_theme() ) { - return; - } - - add_filter( 'render_block_data', array( $this, 'update_render_block_data' ), 10, 3 ); - add_filter( 'render_block', array( $this, 'inject_hooks' ), 10, 2 ); - } - /** * Update the render block data to inject our custom attribute needed to * determine which blocks belong to an inherited Products block. @@ -254,77 +242,6 @@ protected function is_archive_template() { return is_shop() || is_product_taxonomy(); } - /** - * Remove the default callback added by WooCommerce. We replaced these - * callbacks by blocks so we have to remove them to prevent duplicated - * content. - */ - protected function remove_default_hooks() { - foreach ( $this->hook_data as $hook => $data ) { - if ( ! isset( $data['hooked'] ) ) { - continue; - } - foreach ( $data['hooked'] as $callback => $priority ) { - remove_action( $hook, $callback, $priority ); - } - } - - /** - * When extensions implement their equivalent blocks of the template - * hook functions, they can use this filter to register their old hooked - * data here, so in the blockified template, the old hooked functions - * can be removed in favor of the new blocks while keeping the old - * hooked functions working in classic templates. - * - * Accepts an array of hooked data. The array should be in the following - * format: - * [ - * [ - * hook => , - * function => , - * priority => , - * ], - * ... - * ] - * Where: - * - hook-name is the name of the hook that have the functions hooked to. - * - function-name is the hooked function name. - * - priority is the priority of the hooked function. - * - * @param array $data Additional hooked data. Default to empty - */ - $additional_hook_data = apply_filters( 'woocommerce_blocks_hook_compatibility_additional_data', array() ); - - if ( empty( $additional_hook_data ) || ! is_array( $additional_hook_data ) ) { - return; - } - - foreach ( $additional_hook_data as $data ) { - if ( ! isset( $data['hook'], $data['function'], $data['priority'] ) ) { - continue; - } - remove_action( $data['hook'], $data['function'], $data['priority'] ); - } - } - - /** - * Get the buffer content of the hooks to append/prepend to render content. - * - * @param array $hooks The hooks to be rendered. - * @param string $position The position of the hooks. - * - * @return string - */ - protected function get_hooks_buffer( $hooks, $position ) { - ob_start(); - foreach ( $hooks as $hook => $data ) { - if ( $data['position'] === $position ) { - do_action( $hook ); - } - } - return ob_get_clean(); - } - /** * Loop through inner blocks recursively to find the Products blocks that * inherits query from template. @@ -360,134 +277,4 @@ protected function inject_attribute( &$block ) { array_walk( $block['innerBlocks'], array( $this, 'inject_attribute' ) ); } } - - /** - * For compatibility reason, we need to wrap the Single Product template in a div with specific class. - * For more details, see https://github.com/woocommerce/woocommerce-blocks/issues/8314. - * - * @param string $template_content Template Content. - * @return string Wrapped template content inside a div. - */ - public static function wrap_single_product_template( $template_content ) { - $parsed_blocks = parse_blocks( $template_content ); - $grouped_blocks = self::group_blocks( $parsed_blocks ); - - // WIP: The list of blocks is WIP. - $single_product_template_blocks = array( 'woocommerce/product-gallery-image', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); - - $wrapped_blocks = array_map( - function( $blocks ) use ( $single_product_template_blocks ) { - if ( 'core/template-part' === $blocks[0]['blockName'] ) { - return $blocks; - } - - $has_single_product_template_blocks = self::has_single_product_template_blocks( $blocks, $single_product_template_blocks ); - - if ( $has_single_product_template_blocks ) { - $wrapped_block = self::create_wrap_block_group( $blocks ); - return array( $wrapped_block[0] ); - } - return $blocks; - }, - $grouped_blocks - ); - - $template = array_reduce( - $wrapped_blocks, - function( $carry, $item ) { - if ( is_array( $item ) ) { - return $carry . serialize_blocks( $item ); - } - return $carry . serialize_block( $item ); - }, - '' - ); - - return $template; - } - - /** - * Wrap all the blocks inside the template in a group block. - * - * @param array $blocks Array of parsed block objects. - * @return array Group block with the blocks inside. - */ - private static function create_wrap_block_group( $blocks ) { - $serialized_blocks = serialize_blocks( $blocks ); - - $new_block = parse_blocks( - sprintf( - ' -
    - %1$s -
    - ', - $serialized_blocks - ) - ); - - $new_block['innerBlocks'] = $blocks; - - return $new_block; - - } - - /** - * Check if the Single Product template has a single product template block: - * woocommerce/product-gallery-image, woocommerce/product-details, woocommerce/add-to-cart-form] - * - * @param array $parsed_blocks Array of parsed block objects. - * @param array $single_product_template_blocks Array of single product template blocks. - * @return bool True if the template has a single product template block, false otherwise. - */ - private static function has_single_product_template_blocks( $parsed_blocks, $single_product_template_blocks ) { - $found = false; - - foreach ( $parsed_blocks as $block ) { - if ( isset( $block['blockName'] ) && in_array( $block['blockName'], $single_product_template_blocks, true ) ) { - $found = true; - break; - } - $found = self::has_single_product_template_blocks( $block['innerBlocks'], $single_product_template_blocks ); - if ( $found ) { - break; - } - } - return $found; - } - - - /** - * Group blocks in this way: - * B1 + TP1 + B2 + B3 + B4 + TP2 + B5 - * (B = Block, TP = Template Part) - * becomes: - * [[B1], [TP1], [B2, B3, B4], [TP2], [B5]] - * - * @param array $parsed_blocks Array of parsed block objects. - * @return array Array of blocks grouped by template part. - */ - private static function group_blocks( $parsed_blocks ) { - return array_reduce( - $parsed_blocks, - function( $carry, $block ) { - if ( 'core/template-part' === $block['blockName'] ) { - array_push( $carry, array( $block ) ); - return $carry; - } - if ( empty( $block['blockName'] ) ) { - return $carry; - } - $last_element_index = count( $carry ) - 1; - if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && 'core/template-part' !== $carry[ $last_element_index ][0]['blockName'] ) { - array_push( $carry[ $last_element_index ], $block ); - return $carry; - } - array_push( $carry, array( $block ) ); - return $carry; - }, - array() - ); - } - } diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php new file mode 100644 index 00000000000..008b3cc8b54 --- /dev/null +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -0,0 +1,141 @@ + +
    + %1$s +
    + ', + $serialized_blocks + ) + ); + + $new_block['innerBlocks'] = $blocks; + + return $new_block; + + } + + /** + * Check if the Single Product template has a single product template block: + * woocommerce/product-gallery-image, woocommerce/product-details, woocommerce/add-to-cart-form] + * + * @param array $parsed_blocks Array of parsed block objects. + * @param array $single_product_template_blocks Array of single product template blocks. + * @return bool True if the template has a single product template block, false otherwise. + */ + private static function has_single_product_template_blocks( $parsed_blocks, $single_product_template_blocks ) { + $found = false; + + foreach ( $parsed_blocks as $block ) { + if ( isset( $block['blockName'] ) && in_array( $block['blockName'], $single_product_template_blocks, true ) ) { + $found = true; + break; + } + $found = self::has_single_product_template_blocks( $block['innerBlocks'], $single_product_template_blocks ); + if ( $found ) { + break; + } + } + return $found; + } + + + /** + * Group blocks in this way: + * B1 + TP1 + B2 + B3 + B4 + TP2 + B5 + * (B = Block, TP = Template Part) + * becomes: + * [[B1], [TP1], [B2, B3, B4], [TP2], [B5]] + * + * @param array $parsed_blocks Array of parsed block objects. + * @return array Array of blocks grouped by template part. + */ + private static function group_blocks( $parsed_blocks ) { + return array_reduce( + $parsed_blocks, + function( $carry, $block ) { + if ( 'core/template-part' === $block['blockName'] ) { + array_push( $carry, array( $block ) ); + return $carry; + } + if ( empty( $block['blockName'] ) ) { + return $carry; + } + $last_element_index = count( $carry ) - 1; + if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && 'core/template-part' !== $carry[ $last_element_index ][0]['blockName'] ) { + array_push( $carry[ $last_element_index ], $block ); + return $carry; + } + array_push( $carry, array( $block ) ); + return $carry; + }, + array() + ); + } +} diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/SingleProductTemplateCompatibilityTests.php similarity index 94% rename from tests/php/Templates/BlockTemplatesCompatibilityTests.php rename to tests/php/Templates/SingleProductTemplateCompatibilityTests.php index d540c2f1d2c..5a92a9ce3a3 100644 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ b/tests/php/Templates/SingleProductTemplateCompatibilityTests.php @@ -3,13 +3,13 @@ namespace Automattic\WooCommerce\Blocks\Tests\Templates; use \WP_UnitTestCase; -use Automattic\WooCommerce\Blocks\Templates\BlockTemplatesCompatibility; +use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility; /** - * Tests the BlockTemplatesCompatibility class + * Tests the SingleProductTemplateCompatibility class * */ -class BlockTemplatesCompatibilityTests extends WP_UnitTestCase { +class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { /** * Test that the default Single Product Template is not wrapped in a div. @@ -35,7 +35,7 @@ public function test_no_wrap_single_product_template_with_default_single_product '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -70,7 +70,7 @@ public function test_wrap_single_product_template_if_contains_single_product_blo '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -153,7 +153,7 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -220,7 +220,7 @@ public function test_wrap_single_product_template_without_a_main_wrapper() { '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -249,7 +249,7 @@ public function test_wrap_single_product_template_with_multiple_header() { '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -279,7 +279,7 @@ public function test_wrap_single_product_template_with_multiple_footer() { '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -320,7 +320,7 @@ public function test_wrap_single_product_template_with_multiple_blocks_related_t '; - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); From 94ac4a0758f615df1043386a14e9f7662fa74b34 Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 14 Feb 2023 17:12:10 +0100 Subject: [PATCH 54/68] Add Hooks compatibility --- src/BlockTemplatesController.php | 8 +- .../SingleProductTemplateCompatibility.php | 213 +++++++++++++++++- ...ingleProductTemplateCompatibilityTests.php | 72 +++--- 3 files changed, 245 insertions(+), 48 deletions(-) diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index 60008e8b9f9..17d36938a57 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -2,8 +2,8 @@ namespace Automattic\WooCommerce\Blocks; use Automattic\WooCommerce\Blocks\Domain\Package; -use Automattic\WooCommerce\Blocks\Templates\BlockTemplatesCompatibility; use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate; +use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; /** @@ -324,8 +324,10 @@ function( $template ) { } if ( 'single-product' === $template->slug ) { - $new_content = BlockTemplatesCompatibility::wrap_single_product_template( $template->content ); - $template->content = $new_content; + if ( ! is_admin() ) { + $new_content = SingleProductTemplateCompatibility::add_compatibility_layer( $template->content ); + $template->content = $new_content; + } return $template; } diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php index 008b3cc8b54..a6e37182a08 100644 --- a/src/Templates/SingleProductTemplateCompatibility.php +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -8,7 +8,151 @@ * * @internal */ -class SingleProductTemplateCompatibility { +class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility { + const IS_FIRST_BLOCK = '__wooCommerceIsFirstBlock'; + const IS_LAST_BLOCK = '__wooCommerceIsLastBlock'; + + + /** + * Inject hooks to rendered content of corresponding blocks. + * + * @param mixed $block_content The rendered block content. + * @param mixed $block The parsed block data. + * @return string + */ + public function inject_hooks( $block_content, $block ) { + if ( ! $this->is_single_product_template() ) { + return $block_content; + } + + $this->remove_default_hooks(); + + if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) ) { + return sprintf( + '%1$s%2$s%3$s', + $this->get_hooks_buffer( + array( + 'woocommerce_before_single_product' => $this->hook_data['woocommerce_before_single_product'], + 'woocommerce_before_single_product_summary' => $this->hook_data['woocommerce_before_single_product_summary'], + ), + 'before' + ), + $block_content, + $this->get_hooks_buffer( + array( + 'woocommerce_single_product_summary' => $this->hook_data['woocommerce_single_product_summary'], + ), + 'after' + ) + ); + } + + if ( isset( $block['attrs'][ self::IS_LAST_BLOCK ] ) ) { + return sprintf( + '%1$s%2$s%3$s', + $this->get_hooks_buffer( array(), 'before' ), + $block_content, + $this->get_hooks_buffer( + array( + 'woocommerce_after_single_product' => $this->hook_data['woocommerce_after_single_product'], + ), + 'after' + ) + ); + } + + return $block_content; + } + + /** + * Update the render block data to inject our custom attribute needed to + * determine which is the first block of the Single Product Template. + * + * @param array $parsed_block The block being rendered. + * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content. + * @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block. + * + * @return array + */ + public function update_render_block_data( $parsed_block, $source_block, $parent_block ) { + return $parsed_block; + } + + /** + * Set supported hooks. + */ + protected function set_hook_data() { + $this->hook_data = array( + 'woocommerce_before_single_product' => array( + 'block_name' => '', + 'position' => 'before', + 'hooked' => array( + 'woocommerce_output_all_notices' => 10, + 'woocommerce_breadcrumb' => 20, + ), + ), + 'woocommerce_before_single_product_summary' => array( + 'block_name' => '', + 'position' => 'before', + 'hooked' => array( + 'woocommerce_show_product_sale_flash' => 10, + 'woocommerce_show_product_images' => 20, + ), + ), + 'woocommerce_single_product_summary' => array( + 'block_name' => '', + 'position' => 'after', + 'hooked' => array( + 'woocommerce_template_single_title' => 5, + 'woocommerce_template_single_rating' => 10, + 'woocommerce_template_single_price' => 10, + 'woocommerce_template_single_excerpt' => 20, + 'woocommerce_template_single_add_to_cart' => 30, + 'woocommerce_template_single_meta' => 40, + 'woocommerce_template_single_sharing' => 50, + ), + ), + 'woocommerce_after_single_product' => array( + 'block_name' => '', + 'position' => 'before', + 'hooked' => array(), + ), + ); + } + + + /** + * Check if the current template is a single product template. + * + * @return bool + */ + private function is_single_product_template() { + return is_product(); + } + + /** + * Add compatibility layer to the first and last block of the Single Product Template. + * + * @param string $template_content Template. + * @return string + */ + public static function add_compatibility_layer( $template_content ) { + $wrapped_blocks = self::wrap_single_product_template( $template_content ); + $template = self::inject_custom_attributes_to_first_and_last_block_single_product_template( $wrapped_blocks ); + + return array_reduce( + $template, + function( $carry, $item ) { + if ( is_array( $item ) ) { + return $carry . serialize_blocks( $item ); + } + return $carry . serialize_block( $item ); + }, + '' + ); + + } + /** * For compatibility reason, we need to wrap the Single Product template in a div with specific class. @@ -17,12 +161,12 @@ class SingleProductTemplateCompatibility { * @param string $template_content Template Content. * @return string Wrapped template content inside a div. */ - public static function wrap_single_product_template( $template_content ) { + private static function wrap_single_product_template( $template_content ) { $parsed_blocks = parse_blocks( $template_content ); $grouped_blocks = self::group_blocks( $parsed_blocks ); // WIP: The list of blocks is WIP. - $single_product_template_blocks = array( 'woocommerce/product-gallery-image', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); + $single_product_template_blocks = array( 'woocommerce/product-image-gallery', 'woocommerce/product-details', 'woocommerce/add-to-cart-form' ); $wrapped_blocks = array_map( function( $blocks ) use ( $single_product_template_blocks ) { @@ -40,19 +184,70 @@ function( $blocks ) use ( $single_product_template_blocks ) { }, $grouped_blocks ); + return $wrapped_blocks; + } - $template = array_reduce( + + /** + * Add custom attributes to the first group block and last group block that wrap Single Product Template blocks. + * This is necessary to add the hooks woocommerce_before_single_product and woocommerce_after_single_product to the right block compatibility layer to the Single Product Template. + * + * @param array $wrapped_blocks Wrapped blocks. + * @return array + */ + private static function inject_custom_attributes_to_first_and_last_block_single_product_template( $wrapped_blocks ) { + $template_with_custom_attributes = array_reduce( $wrapped_blocks, function( $carry, $item ) { - if ( is_array( $item ) ) { - return $carry . serialize_blocks( $item ); + + $index = $carry['index']; + $carry['index'] = $carry['index'] + 1; + $block = $item[0]; + + if ( 'core/template-part' === $block['blockName'] ) { + array_push( $carry['template'], $block ); + return $carry; } - return $carry . serialize_block( $item ); + + if ( false === $carry['first_block']['found'] ) { + $block['attrs'][ self::IS_FIRST_BLOCK ] = true; + $carry['first_block']['found'] = true; + } + + if ( true === $carry['last_block']['found'] ) { + $index_element = $carry['last_block']['index']; + $carry['last_block']['index'] = $index; + $block['attrs'][ self::IS_LAST_BLOCK ] = true; + unset( $carry['template'][ $index_element ]['attrs'][ self::IS_LAST_BLOCK ] ); + + array_push( $carry['template'], $block ); + + return $carry; + } + + $block['attrs'][ self::IS_LAST_BLOCK ] = true; + $carry['last_block']['found'] = true; + $carry['last_block']['index'] = $index; + + array_push( $carry['template'], $block ); + + return $carry; }, - '' + array( + 'template' => array(), + 'first_block' => array( + 'index' => '', + 'found' => false, + ), + 'last_block' => array( + 'index' => '', + 'found' => false, + ), + 'index' => 0, + ) ); - return $template; + return array( $template_with_custom_attributes['template'] ); } /** diff --git a/tests/php/Templates/SingleProductTemplateCompatibilityTests.php b/tests/php/Templates/SingleProductTemplateCompatibilityTests.php index 5a92a9ce3a3..fd87ba6c5f1 100644 --- a/tests/php/Templates/SingleProductTemplateCompatibilityTests.php +++ b/tests/php/Templates/SingleProductTemplateCompatibilityTests.php @@ -15,7 +15,7 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { * Test that the default Single Product Template is not wrapped in a div. * */ - public function test_no_wrap_single_product_template_with_default_single_product_template() { + public function test_no_add_compatibility_layer_with_default_single_product_template() { $default_single_product_template = ' @@ -28,14 +28,14 @@ public function test_no_wrap_single_product_template_with_default_single_product $expected_single_product_template = ' - +
    '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -46,31 +46,31 @@ public function test_no_wrap_single_product_template_with_default_single_product /** * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. */ - public function test_wrap_single_product_template_if_contains_single_product_blocks() { + public function test_add_compatibility_layer_if_contains_single_product_blocks() { $default_single_product_template = '
    - +
    '; $expected_single_product_template = ' - +
    - +
    '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -81,7 +81,7 @@ public function test_wrap_single_product_template_if_contains_single_product_blo /** * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. */ - public function test_wrap_single_product_template_if_contains_nested_single_product_blocks() { + public function test_add_compatibility_layer_if_contains_nested_single_product_blocks() { $default_single_product_template = ' @@ -89,7 +89,7 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod
    - +
    @@ -118,13 +118,13 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod $expected_single_product_template = ' - +
    - +
    @@ -153,7 +153,7 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -164,11 +164,11 @@ public function test_wrap_single_product_template_if_contains_nested_single_prod /** * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. */ - public function test_wrap_single_product_template_without_a_main_wrapper() { + public function test_add_compatibility_layer_without_a_main_wrapper() { $default_single_product_template = ' - +
    @@ -193,9 +193,9 @@ public function test_wrap_single_product_template_without_a_main_wrapper() { $expected_single_product_template = ' - +
    - +
    @@ -220,7 +220,7 @@ public function test_wrap_single_product_template_without_a_main_wrapper() { '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -231,25 +231,25 @@ public function test_wrap_single_product_template_without_a_main_wrapper() { /** * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. */ - public function test_wrap_single_product_template_with_multiple_header() { + public function test_add_compatibility_layer_with_multiple_header() { $default_single_product_template = ' - + '; $expected_single_product_template = ' - +
    - +
    '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -261,25 +261,25 @@ public function test_wrap_single_product_template_with_multiple_header() { /** * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. */ - public function test_wrap_single_product_template_with_multiple_footer() { + public function test_add_compatibility_layer_with_multiple_footer() { $default_single_product_template = ' - + '; $expected_single_product_template = ' - +
    - +
    '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); @@ -290,37 +290,37 @@ public function test_wrap_single_product_template_with_multiple_footer() { /** * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. */ - public function test_wrap_single_product_template_with_multiple_blocks_related_to_the_single_product_template() { + public function test_add_compatibility_layer_with_multiple_blocks_related_to_the_single_product_template() { $default_single_product_template = '

    test

    - + - + '; $expected_single_product_template = ' - +

    test

    - +
    - +
    - +
    '; - $result = SingleProductTemplateCompatibility::wrap_single_product_template( $default_single_product_template ); + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); $result_without_withespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); From 607dff4b76da0d5528daec093d299c00132fd898 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 15 Feb 2023 15:42:15 +0100 Subject: [PATCH 55/68] remove not used file --- .../BlockTemplatesCompatibilityTests.php | 330 ------------------ 1 file changed, 330 deletions(-) delete mode 100644 tests/php/Templates/BlockTemplatesCompatibilityTests.php diff --git a/tests/php/Templates/BlockTemplatesCompatibilityTests.php b/tests/php/Templates/BlockTemplatesCompatibilityTests.php deleted file mode 100644 index 071fd19ae55..00000000000 --- a/tests/php/Templates/BlockTemplatesCompatibilityTests.php +++ /dev/null @@ -1,330 +0,0 @@ - - -
    - -
    - - '; - - $expected_single_product_template = ' - - -
    - -
    - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } - - /** - * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. - */ - public function test_wrap_single_product_template_if_contains_single_product_blocks() { - - $default_single_product_template = ' - - -
    - -
    - - '; - - $expected_single_product_template = ' - - -
    - -
    - -
    - -
    - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } - - /** - * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. - */ - public function test_wrap_single_product_template_if_contains_nested_single_product_blocks() { - - $default_single_product_template = ' - - -
    - -
    - -
    - - -
    - - - - - - - - - - - - - -

    - - -
    - -
    - - '; - - $expected_single_product_template = ' - - -
    - -
    - -
    - -
    - - -
    - - - - - - - - - - - - - -

    - - -
    - -
    - -
    - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } - - /** - * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template in a nested structure. - */ - public function test_wrap_single_product_template_without_a_main_wrapper() { - - $default_single_product_template = ' - - - -
    - - - - - - - - - - - - - -

    - - -
    - - '; - - $expected_single_product_template = ' - - -
    - - -
    - - - - - - - - - - - - - -

    - - -
    - -
    - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } - - /** - * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. - */ - public function test_wrap_single_product_template_with_multiple_header() { - - $default_single_product_template = ' - - - - '; - - $expected_single_product_template = ' - - - -
    - -
    - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } - - - /** - * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. - */ - public function test_wrap_single_product_template_with_multiple_footer() { - - $default_single_product_template = ' - - - - '; - - $expected_single_product_template = ' - - -
    - -
    - - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } - - /** - * Test that the Single Product Template is wrapped in a div with the correct class if it contains a block related to the Single Product Template. - */ - public function test_wrap_single_product_template_with_multiple_blocks_related_to_the_single_product_template() { - - $default_single_product_template = ' - -

    test

    - - - - - - '; - - $expected_single_product_template = ' - -

    test

    - - - -
    - -
    - - - -
    - -
    - - '; - - $result = BlockTemplatesCompatibility::wrap_single_product_template( $default_single_product_template ); - - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); - } -} From 2a0848f8ef23f95922f6e68b32d3c38bcef1fe90 Mon Sep 17 00:00:00 2001 From: Luigi Date: Thu, 16 Feb 2023 12:33:02 +0100 Subject: [PATCH 56/68] remove not used files --- .../blocks/single-product-details/block.json | 13 --- .../blocks/single-product-details/block.tsx | 95 ------------------- .../js/blocks/single-product-details/edit.tsx | 32 ------- .../blocks/single-product-details/editor.scss | 5 - .../blocks/single-product-details/index.tsx | 17 ---- .../blocks/single-product-details/style.scss | 46 --------- .../js/blocks/single-product-details/types.ts | 3 - bin/webpack-entries.js | 1 - 8 files changed, 212 deletions(-) delete mode 100644 assets/js/blocks/single-product-details/block.json delete mode 100644 assets/js/blocks/single-product-details/block.tsx delete mode 100644 assets/js/blocks/single-product-details/edit.tsx delete mode 100644 assets/js/blocks/single-product-details/editor.scss delete mode 100644 assets/js/blocks/single-product-details/index.tsx delete mode 100644 assets/js/blocks/single-product-details/style.scss delete mode 100644 assets/js/blocks/single-product-details/types.ts diff --git a/assets/js/blocks/single-product-details/block.json b/assets/js/blocks/single-product-details/block.json deleted file mode 100644 index ac3f47665af..00000000000 --- a/assets/js/blocks/single-product-details/block.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "woocommerce/single-product-details", - "version": "1.0.0", - "title": "Product Details", - "description": "A block that allows your customers to see details and reviews about the product.", - "category": "woocommerce", - "keywords": [ "WooCommerce" ], - "supports": {}, - "attributes": {}, - "textdomain": "woo-gutenberg-products-block", - "apiVersion": 2, - "$schema": "https://schemas.wp.org/trunk/block.json" -} diff --git a/assets/js/blocks/single-product-details/block.tsx b/assets/js/blocks/single-product-details/block.tsx deleted file mode 100644 index 37db65931f0..00000000000 --- a/assets/js/blocks/single-product-details/block.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { __ } from '@wordpress/i18n'; -import { useBlockProps } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ - -interface SingleProductTab { - id: string; - title: string; - active: boolean; - content: string | undefined; -} - -const ProductTabTitle = ( { - id, - title, - active, -}: Pick< SingleProductTab, 'id' | 'title' | 'active' > ) => { - return ( -
  • - { title } -
  • - ); -}; - -const ProductTabContent = ( { - id, - content, -}: Pick< SingleProductTab, 'id' | 'content' > ) => { - return ( -
    - { content } -
    - ); -}; - -export const SingleProductDetails = () => { - const blockProps = useBlockProps(); - const productTabs = [ - { - id: 'description', - title: 'Description', - active: true, - content: __( - 'This block lists description, attributes and reviews for a single product.', - 'woo-gutenberg-products-block' - ), - }, - { - id: 'additional_information', - title: 'Additional Information', - active: false, - }, - { id: 'reviews', title: 'Reviews', active: false }, - ]; - const tabsTitle = productTabs.map( ( { id, title, active } ) => ( - - ) ); - const tabsContent = productTabs.map( ( { id, content } ) => ( - - ) ); - - return ( -
    -
      - { tabsTitle } -
    - { tabsContent } -
    - ); -}; - -export default SingleProductDetails; diff --git a/assets/js/blocks/single-product-details/edit.tsx b/assets/js/blocks/single-product-details/edit.tsx deleted file mode 100644 index 471f9f7e30b..00000000000 --- a/assets/js/blocks/single-product-details/edit.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/** - * External dependencies - */ -import { useBlockProps } from '@wordpress/block-editor'; -import { Disabled } from '@wordpress/components'; -import type { BlockEditProps } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import Block from './block'; -import { Attributes } from './types'; -import './editor.scss'; - -const Edit = ( { attributes }: BlockEditProps< Attributes > ) => { - const { className } = attributes; - const blockProps = useBlockProps( { - className, - } ); - - return ( - <> -
    - - - -
    - - ); -}; - -export default Edit; diff --git a/assets/js/blocks/single-product-details/editor.scss b/assets/js/blocks/single-product-details/editor.scss deleted file mode 100644 index da60cc6b925..00000000000 --- a/assets/js/blocks/single-product-details/editor.scss +++ /dev/null @@ -1,5 +0,0 @@ -.wc-block-single-product-details { - ul.tabs.li { - list-style-type: none; - } -} diff --git a/assets/js/blocks/single-product-details/index.tsx b/assets/js/blocks/single-product-details/index.tsx deleted file mode 100644 index d6ef97fddd2..00000000000 --- a/assets/js/blocks/single-product-details/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/** - * External dependencies - */ -import { registerBlockType } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import metadata from './block.json'; -import edit from './edit'; - -registerBlockType( metadata, { - edit, - save() { - return null; - }, -} ); diff --git a/assets/js/blocks/single-product-details/style.scss b/assets/js/blocks/single-product-details/style.scss deleted file mode 100644 index e32e9c1da22..00000000000 --- a/assets/js/blocks/single-product-details/style.scss +++ /dev/null @@ -1,46 +0,0 @@ -.wp-block-woocommerce-single-product-details { - ul.tabs { - list-style: none; - padding: 0 0 0 1em; - margin: 0 0 1.618em; - overflow: hidden; - position: relative; - border-bottom: 1px solid $gray-200; - - li { - border: 1px solid $gray-200; - background-color: $white; - display: inline-block; - position: relative; - z-index: 0; - border-radius: 4px 4px 0 0; - margin: 0; - padding: 0.5em 1em; - opacity: 0.5; - - a { - display: inline-block; - font-weight: 700; - color: $black; - text-decoration: none; - - &:hover { - text-decoration: none; - color: color.adjust($black, $lightness: 10%); - } - } - - &.active { - background: $gray-100; - z-index: 2; - border-bottom-color: $gray-200; - opacity: 1; - - a { - color: inherit; - text-shadow: inherit; - } - } - } - } -} diff --git a/assets/js/blocks/single-product-details/types.ts b/assets/js/blocks/single-product-details/types.ts deleted file mode 100644 index aa9b37b316a..00000000000 --- a/assets/js/blocks/single-product-details/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Attributes { - className?: string; -} diff --git a/bin/webpack-entries.js b/bin/webpack-entries.js index 7e55302dfea..3b03f77d997 100644 --- a/bin/webpack-entries.js +++ b/bin/webpack-entries.js @@ -67,7 +67,6 @@ const blocks = { 'single-product': { isExperimental: true, }, - 'single-product-details': {}, 'stock-filter': {}, }; From d715fa3ed60c7d3ea7c0c85924fcac4e113a502d Mon Sep 17 00:00:00 2001 From: Luigi Date: Thu, 16 Feb 2023 16:52:36 +0100 Subject: [PATCH 57/68] Add compatibility layer for the Single Product template --- .../SingleProductTemplateCompatibility.php | 106 +++++++++++++++--- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php index a6e37182a08..90d306f3794 100644 --- a/src/Templates/SingleProductTemplateCompatibility.php +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -27,41 +27,105 @@ public function inject_hooks( $block_content, $block ) { $this->remove_default_hooks(); - if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) ) { + $first_or_last_block_content = $this->inject_hook_to_first_and_last_blocks( $block_content, $block ); + + if ( isset( $first_or_last_block_content ) ) { + return $first_or_last_block_content; + } + + $block_name = $block['blockName']; + + $block_hooks = array_filter( + $this->hook_data, + function( $hook ) use ( $block_name ) { + return $hook['block_name'] === $block_name; + } + ); + + return sprintf( + '%1$s%2$s%3$s', + $this->get_hooks_buffer( $block_hooks, 'before' ), + $block_content, + $this->get_hooks_buffer( $block_hooks, 'after' ) + ); + } + + /** + * Inject custom hooks to the first and last blocks. + * Since that there is a custom logic for the first and last block, we have to inject the hooks manually. + * The first block supports the following hooks: + * woocommerce_before_single_product + * woocommerce_before_single_product_summary + * woocommerce_single_product_summary + * + * The last block supports the following hooks: + * woocommerce_after_single_product + * + * @param mixed $block_content The rendered block content. + * @param mixed $block The parsed block data. + * @return string + */ + private function inject_hook_to_first_and_last_blocks( $block_content, $block ) { + $first_block_hook = array( + 'before' => array( + 'woocommerce_before_single_product' => $this->hook_data['woocommerce_before_single_product'], + 'woocommerce_before_single_product_summary' => $this->hook_data['woocommerce_before_single_product_summary'], + 'woocommerce_single_product_summary' => $this->hook_data['woocommerce_single_product_summary'], + ), + 'after' => array(), + ); + + $last_block_hook = array( + 'before' => array(), + 'after' => array( + 'woocommerce_after_single_product' => $this->hook_data['woocommerce_after_single_product'], + ), + ); + + if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) && isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) ) { return sprintf( '%1$s%2$s%3$s', $this->get_hooks_buffer( - array( - 'woocommerce_before_single_product' => $this->hook_data['woocommerce_before_single_product'], - 'woocommerce_before_single_product_summary' => $this->hook_data['woocommerce_before_single_product_summary'], - ), + $first_block_hook['before'], 'before' ), $block_content, $this->get_hooks_buffer( - array( - 'woocommerce_single_product_summary' => $this->hook_data['woocommerce_single_product_summary'], + array_merge( + $first_block_hook['after'], + $last_block_hook['after'] ), 'after' ) ); } + if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) ) { + return sprintf( + '%1$s%2$s%3$s', + $this->get_hooks_buffer( + $first_block_hook['before'], + 'before' + ), + $block_content, + $this->get_hooks_buffer( + $first_block_hook['after'], + 'after' + ) + ); + } + if ( isset( $block['attrs'][ self::IS_LAST_BLOCK ] ) ) { return sprintf( '%1$s%2$s%3$s', $this->get_hooks_buffer( array(), 'before' ), $block_content, $this->get_hooks_buffer( - array( - 'woocommerce_after_single_product' => $this->hook_data['woocommerce_after_single_product'], - ), + $last_block_hook['after'], 'after' ) ); } - - return $block_content; } /** @@ -101,7 +165,7 @@ protected function set_hook_data() { ), 'woocommerce_single_product_summary' => array( 'block_name' => '', - 'position' => 'after', + 'position' => 'before', 'hooked' => array( 'woocommerce_template_single_title' => 5, 'woocommerce_template_single_rating' => 10, @@ -114,9 +178,23 @@ protected function set_hook_data() { ), 'woocommerce_after_single_product' => array( 'block_name' => '', + 'position' => 'after', + 'hooked' => array(), + ), + 'woocommerce_share' => array( + 'block_name' => 'woocommerce/product-details', 'position' => 'before', 'hooked' => array(), ), + 'woocommerce_after_single_product_summary' => array( + 'block_name' => 'woocommerce/product-details', + 'position' => 'before', + 'hooked' => array( + 'woocommerce_output_product_data_tabs' => 10, + 'woocommerce_upsell_display' => 15, + 'woocommerce_output_related_products' => 20, + ), + ), ); } @@ -190,7 +268,7 @@ function( $blocks ) use ( $single_product_template_blocks ) { /** * Add custom attributes to the first group block and last group block that wrap Single Product Template blocks. - * This is necessary to add the hooks woocommerce_before_single_product and woocommerce_after_single_product to the right block compatibility layer to the Single Product Template. + * This is necessary to add the hooks woocommerce_before_single_product and woocommerce_after_single_product to the right block. * * @param array $wrapped_blocks Wrapped blocks. * @return array From 6996ca8d54438ac69f60ff7b207b7f2be77b6cc7 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 17 Feb 2023 12:20:14 +0100 Subject: [PATCH 58/68] fix check --- src/Templates/SingleProductTemplateCompatibility.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php index 2842e4e59a4..36861e56c00 100644 --- a/src/Templates/SingleProductTemplateCompatibility.php +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -82,7 +82,7 @@ private function inject_hook_to_first_and_last_blocks( $block_content, $block ) ), ); - if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) && isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) ) { + if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) && isset( $block['attrs'][ self::IS_LAST_BLOCK ] ) ) { return sprintf( '%1$s%2$s%3$s', $this->get_hooks_buffer( From 7790a5d502162a3df50dba675fc3e8c3fbbe7b74 Mon Sep 17 00:00:00 2001 From: Luigi Date: Mon, 20 Feb 2023 16:46:35 +0100 Subject: [PATCH 59/68] implement Product meta template header --- .../shared/with-product-selector.js | 2 +- .../blocks/product-elements/sku/attributes.ts | 8 ++++ .../blocks/product-elements/sku/block.tsx | 46 ++++++++++++++----- .../blocks/product-elements/sku/edit.tsx | 18 +++++++- .../sku/{index.ts => index.tsx} | 28 +++++++---- .../blocks/product-elements/sku/types.ts | 2 + .../SingleProductTemplateCompatibility.php | 28 +++++++++-- templates/parts/product-meta.html | 3 ++ 8 files changed, 106 insertions(+), 29 deletions(-) rename assets/js/atomic/blocks/product-elements/sku/{index.ts => index.tsx} (60%) create mode 100644 templates/parts/product-meta.html diff --git a/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js b/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js index abaa13fa2ed..151a4c2b8d6 100644 --- a/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js +++ b/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js @@ -23,7 +23,7 @@ import './editor.scss'; const withProductSelector = ( selectorArgs ) => ( OriginalComponent ) => { return ( props ) => { const productDataContext = useProductDataContext(); - const { attributes, setAttributes } = props; + const { attributes, setAttributes, showProductSelector } = props; const { productId } = attributes; const [ isEditing, setIsEditing ] = useState( ! productId ); diff --git a/assets/js/atomic/blocks/product-elements/sku/attributes.ts b/assets/js/atomic/blocks/product-elements/sku/attributes.ts index d7194c6d347..80b6226b1dd 100644 --- a/assets/js/atomic/blocks/product-elements/sku/attributes.ts +++ b/assets/js/atomic/blocks/product-elements/sku/attributes.ts @@ -12,6 +12,14 @@ export const blockAttributes: BlockAttributes = { type: 'boolean', default: false, }, + isDescendentOfSingleProductTemplate: { + type: 'boolean', + default: false, + }, + showProductSelector: { + type: 'boolean', + default: false, + }, }; export default blockAttributes; diff --git a/assets/js/atomic/blocks/product-elements/sku/block.tsx b/assets/js/atomic/blocks/product-elements/sku/block.tsx index 187bc3d8fe9..33fad6fc911 100644 --- a/assets/js/atomic/blocks/product-elements/sku/block.tsx +++ b/assets/js/atomic/blocks/product-elements/sku/block.tsx @@ -18,29 +18,51 @@ import type { Attributes } from './types'; type Props = Attributes & HTMLAttributes< HTMLDivElement >; +const Preview = ( { + parentClassName, + sku, + className, +}: { + parentClassName: string; + sku: string; + className?: string | undefined; +} ) => ( +
    + { __( 'SKU:', 'woo-gutenberg-products-block' ) }{ ' ' } + { sku } +
    +); + const Block = ( props: Props ): JSX.Element | null => { const { className } = props; const { parentClassName } = useInnerBlockLayoutContext(); const { product } = useProductDataContext(); const sku = product.sku; + if ( props.isDescendentOfSingleProductTemplate ) { + return ( + + ); + } + if ( ! sku ) { return null; } return ( -
    - { __( 'SKU:', 'woo-gutenberg-products-block' ) }{ ' ' } - { sku } -
    + ); }; diff --git a/assets/js/atomic/blocks/product-elements/sku/edit.tsx b/assets/js/atomic/blocks/product-elements/sku/edit.tsx index 37874b43f09..04a0ba3cfef 100644 --- a/assets/js/atomic/blocks/product-elements/sku/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/sku/edit.tsx @@ -25,12 +25,26 @@ const Edit = ( { ...context, }; const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); + const isDescendentOfSingleProductTemplate = + attributes.isDescendentOfSingleProductTemplate; useEffect( - () => setAttributes( { isDescendentOfQueryLoop } ), - [ setAttributes, isDescendentOfQueryLoop ] + () => + setAttributes( { + isDescendentOfQueryLoop, + showProductSelector: + ! isDescendentOfQueryLoop && + ! isDescendentOfSingleProductTemplate, + } ), + [ + setAttributes, + isDescendentOfQueryLoop, + isDescendentOfSingleProductTemplate, + ] ); + console.log( blockAttrs ); + return ( <> diff --git a/assets/js/atomic/blocks/product-elements/sku/index.ts b/assets/js/atomic/blocks/product-elements/sku/index.tsx similarity index 60% rename from assets/js/atomic/blocks/product-elements/sku/index.ts rename to assets/js/atomic/blocks/product-elements/sku/index.tsx index 29741a3de3d..56148fbcd3e 100644 --- a/assets/js/atomic/blocks/product-elements/sku/index.ts +++ b/assets/js/atomic/blocks/product-elements/sku/index.tsx @@ -3,6 +3,7 @@ */ import { registerBlockType } from '@wordpress/blocks'; import type { BlockConfiguration } from '@wordpress/blocks'; +import classnames from 'classnames'; /** * Internal dependencies @@ -16,24 +17,31 @@ import { BLOCK_DESCRIPTION as description, } from './constants'; -type CustomBlockConfiguration = BlockConfiguration & { - ancestor: string[]; -}; +const { ancestor, ...configuration } = sharedConfig; -const blockConfig: CustomBlockConfiguration = { - ...sharedConfig, +const blockConfig: BlockConfiguration = { + ...configuration, apiVersion: 2, title, description, icon: { src: icon }, usesContext: [ 'query', 'queryId', 'postId' ], - ancestor: [ - 'woocommerce/all-products', - 'woocommerce/single-product', - 'core/post-template', - ], attributes, edit, + save: () => { + if ( + attributes.isDescendentOfQueryLoop || + attributes.isDescendentOfSingleProductTemplate + ) { + return null; + } + + return ( +
    + ); + }, }; registerBlockType( 'woocommerce/product-sku', { ...blockConfig } ); diff --git a/assets/js/atomic/blocks/product-elements/sku/types.ts b/assets/js/atomic/blocks/product-elements/sku/types.ts index a7d3a20b916..9651b739436 100644 --- a/assets/js/atomic/blocks/product-elements/sku/types.ts +++ b/assets/js/atomic/blocks/product-elements/sku/types.ts @@ -1,4 +1,6 @@ export interface Attributes { productId: number; isDescendentOfQueryLoop: boolean; + isDescendentOfSingleProductTemplate: boolean; + showProductSelector: boolean; } diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php index 36861e56c00..e76d63f7428 100644 --- a/src/Templates/SingleProductTemplateCompatibility.php +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -248,7 +248,7 @@ private static function wrap_single_product_template( $template_content ) { $wrapped_blocks = array_map( function( $blocks ) use ( $single_product_template_blocks ) { - if ( 'core/template-part' === $blocks[0]['blockName'] ) { + if ( self::is_template_part( $blocks[0] ) ) { return $blocks; } @@ -281,7 +281,7 @@ function( $carry, $item ) { $carry['index'] = $carry['index'] + 1; $block = $item[0]; - if ( 'core/template-part' === $block['blockName'] ) { + if ( self::is_template_part( $block ) ) { array_push( $carry['template'], $block ); return $carry; } @@ -392,7 +392,7 @@ private static function group_blocks( $parsed_blocks ) { return array_reduce( $parsed_blocks, function( $carry, $block ) { - if ( 'core/template-part' === $block['blockName'] ) { + if ( self::is_template_part( $block ) ) { array_push( $carry, array( $block ) ); return $carry; } @@ -400,7 +400,7 @@ function( $carry, $block ) { return $carry; } $last_element_index = count( $carry ) - 1; - if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && 'core/template-part' !== $carry[ $last_element_index ][0]['blockName'] ) { + if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && ! self::is_template_part( $carry[ $last_element_index ][0] ) ) { array_push( $carry[ $last_element_index ], $block ); return $carry; } @@ -410,4 +410,24 @@ function( $carry, $block ) { array() ); } + + + /** + * Check if the block is a template part except for the product meta template part. + * + * @param array $block Block object. + * @return bool True if the block is a template part, false otherwise. + */ + private static function is_template_part( $block ) { + if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['slug'] ) && 'product-meta' === $block['attrs']['slug'] ) { + return false; + } + + if ( 'core/template-part' === $block['blockName'] ) { + return true; + } + + return false; + + } } diff --git a/templates/parts/product-meta.html b/templates/parts/product-meta.html new file mode 100644 index 00000000000..d5fc82f4086 --- /dev/null +++ b/templates/parts/product-meta.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file From 5205f52ce3729da233c8c103c22a13c3af56533e Mon Sep 17 00:00:00 2001 From: Luigi Date: Thu, 2 Mar 2023 19:00:59 +0100 Subject: [PATCH 60/68] Product Price Block: Add support Single Product Template --- .../product-elements/price/attributes.ts | 4 + .../blocks/product-elements/price/block.tsx | 29 ++++-- .../blocks/product-elements/price/edit.tsx | 89 +++++++++++++++---- .../price/{index.ts => index.tsx} | 29 +++--- .../blocks/product-elements/price/types.ts | 1 + .../blocks/product-elements/shared/config.tsx | 4 +- .../shared/product-selector.tsx | 77 ++++++++++++++++ 7 files changed, 198 insertions(+), 35 deletions(-) rename assets/js/atomic/blocks/product-elements/price/{index.ts => index.tsx} (61%) create mode 100644 assets/js/atomic/blocks/product-elements/shared/product-selector.tsx diff --git a/assets/js/atomic/blocks/product-elements/price/attributes.ts b/assets/js/atomic/blocks/product-elements/price/attributes.ts index b1d89cf49ff..98babfd82d8 100644 --- a/assets/js/atomic/blocks/product-elements/price/attributes.ts +++ b/assets/js/atomic/blocks/product-elements/price/attributes.ts @@ -16,6 +16,10 @@ export const blockAttributes: BlockAttributes = { type: 'string', default: '', }, + isDescendentOfSingleProductTemplate: { + type: 'boolean', + default: false, + }, }; export default blockAttributes; diff --git a/assets/js/atomic/blocks/product-elements/price/block.tsx b/assets/js/atomic/blocks/product-elements/price/block.tsx index 32b87e6a872..ebbf433c2b4 100644 --- a/assets/js/atomic/blocks/product-elements/price/block.tsx +++ b/assets/js/atomic/blocks/product-elements/price/block.tsx @@ -40,7 +40,7 @@ interface PriceProps { } export const Block = ( props: Props ): JSX.Element | null => { - const { className, textAlign } = props; + const { className, textAlign, isDescendentOfSingleProductTemplate } = props; const { parentClassName } = useInnerBlockLayoutContext(); const { product } = useProductDataContext(); @@ -57,7 +57,7 @@ export const Block = ( props: Props ): JSX.Element | null => { } ); - if ( ! product.id ) { + if ( ! product.id && ! isDescendentOfSingleProductTemplate ) { return ( ); @@ -71,7 +71,11 @@ export const Block = ( props: Props ): JSX.Element | null => { ...spacingProps.style, }; const prices: PriceProps = product.prices; - const currency = getCurrencyFromPriceResponse( prices ); + const currency = isDescendentOfSingleProductTemplate + ? getCurrencyFromPriceResponse() + : getCurrencyFromPriceResponse( prices ); + + const pricePreview = '5000'; const isOnSale = prices.price !== prices.regular_price; const priceClassName = classnames( { [ `${ parentClassName }__product-price__value` ]: parentClassName, @@ -86,12 +90,20 @@ export const Block = ( props: Props ): JSX.Element | null => { priceStyle={ style } priceClassName={ priceClassName } currency={ currency } - price={ prices.price } + price={ + isDescendentOfSingleProductTemplate + ? pricePreview + : prices.price + } // Range price props minPrice={ prices?.price_range?.min_amount } maxPrice={ prices?.price_range?.max_amount } // This is the regular or original price when the `price` value is a sale price. - regularPrice={ prices.regular_price } + regularPrice={ + isDescendentOfSingleProductTemplate + ? pricePreview + : prices.price + } regularPriceClassName={ classnames( { [ `${ parentClassName }__product-price__regular` ]: parentClassName, @@ -101,4 +113,9 @@ export const Block = ( props: Props ): JSX.Element | null => { ); }; -export default withProductDataContext( Block ); +export default ( props: Props ) => { + if ( props.isDescendentOfSingleProductTemplate ) { + return ; + } + return withProductDataContext( Block )( props ); +}; diff --git a/assets/js/atomic/blocks/product-elements/price/edit.tsx b/assets/js/atomic/blocks/product-elements/price/edit.tsx index e29fb269251..58e3dc8b273 100644 --- a/assets/js/atomic/blocks/product-elements/price/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/price/edit.tsx @@ -8,13 +8,14 @@ import { } from '@wordpress/block-editor'; import { useEffect } from '@wordpress/element'; import type { BlockAlignment } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import Block from './block'; -import withProductSelector from '../shared/with-product-selector'; -import { BLOCK_TITLE as label, BLOCK_ICON as icon } from './constants'; +import { BLOCK_TITLE, BLOCK_ICON } from './constants'; +import { ProductSelector } from '../shared/product-selector'; type UnsupportedAligments = 'wide' | 'full'; type AllowedAlignments = Exclude< BlockAlignment, UnsupportedAligments >; @@ -51,26 +52,80 @@ const PriceEdit = ( { }; const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); + const { isDescendentOfSingleProductTemplate } = useSelect( + ( select ) => { + const store = select( 'core/edit-site' ); + const postId = store?.getEditedPostId(); + + return { + isDescendentOfSingleProductTemplate: + ( postId === 'woocommerce/woocommerce//product-meta' || + postId === + 'woocommerce/woocommerce//single-product' ) && + ! isDescendentOfQueryLoop, + }; + }, + [ isDescendentOfQueryLoop ] + ); + useEffect( - () => setAttributes( { isDescendentOfQueryLoop } ), - [ setAttributes, isDescendentOfQueryLoop ] + () => + setAttributes( { + isDescendentOfQueryLoop, + isDescendentOfSingleProductTemplate, + } ), + [ + isDescendentOfQueryLoop, + isDescendentOfSingleProductTemplate, + setAttributes, + ] ); + const showProductSelector = + ! isDescendentOfQueryLoop && ! isDescendentOfSingleProductTemplate; + + if ( ! showProductSelector ) { + return ( + <> + + { + setAttributes( { textAlign } ); + } } + /> + +
    + +
    + + ); + } + return ( - <> - - { - setAttributes( { textAlign } ); - } } - /> - -
    +
    + + + { + setAttributes( { textAlign } ); + } } + /> + -
    - + +
    ); }; -export default withProductSelector( { icon, label } )( PriceEdit ); +export default PriceEdit; diff --git a/assets/js/atomic/blocks/product-elements/price/index.ts b/assets/js/atomic/blocks/product-elements/price/index.tsx similarity index 61% rename from assets/js/atomic/blocks/product-elements/price/index.ts rename to assets/js/atomic/blocks/product-elements/price/index.tsx index 1934e31dc18..50f70ca53c2 100644 --- a/assets/js/atomic/blocks/product-elements/price/index.ts +++ b/assets/js/atomic/blocks/product-elements/price/index.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { registerBlockType } from '@wordpress/blocks'; -import type { BlockConfiguration } from '@wordpress/blocks'; +import classnames from 'classnames'; /** * Internal dependencies @@ -17,25 +17,32 @@ import { BLOCK_DESCRIPTION as description, } from './constants'; -type CustomBlockConfiguration = BlockConfiguration & { - ancestor: string[]; -}; +const { ancestor, ...configuration } = sharedConfig; -const blockConfig: CustomBlockConfiguration = { - ...sharedConfig, +const blockConfig = { + ...configuration, apiVersion: 2, title, description, - ancestor: [ - 'woocommerce/all-products', - 'woocommerce/single-product', - 'core/post-template', - ], usesContext: [ 'query', 'queryId', 'postId' ], icon: { src: icon }, attributes, supports, edit, + save: () => { + if ( + attributes.isDescendentOfQueryLoop || + attributes.isDescendentOfSingleProductTemplate + ) { + return null; + } + + return ( +
    + ); + }, }; registerBlockType( 'woocommerce/product-price', blockConfig ); diff --git a/assets/js/atomic/blocks/product-elements/price/types.ts b/assets/js/atomic/blocks/product-elements/price/types.ts index 81631b9698e..bfbdb99815a 100644 --- a/assets/js/atomic/blocks/product-elements/price/types.ts +++ b/assets/js/atomic/blocks/product-elements/price/types.ts @@ -3,4 +3,5 @@ export interface BlockAttributes { className?: string; textAlign?: 'left' | 'center' | 'right'; isDescendentOfQueryLoop?: boolean; + isDescendentOfSingleProductTemplate?: boolean; } diff --git a/assets/js/atomic/blocks/product-elements/shared/config.tsx b/assets/js/atomic/blocks/product-elements/shared/config.tsx index 86d07aedecb..d4523762784 100644 --- a/assets/js/atomic/blocks/product-elements/shared/config.tsx +++ b/assets/js/atomic/blocks/product-elements/shared/config.tsx @@ -14,7 +14,9 @@ import save from '../save'; * Holds default config for this collection of blocks. * attributes and title are omitted here as these are added on an individual block level. */ -const sharedConfig: Omit< BlockConfiguration, 'attributes' | 'title' > = { +const sharedConfig: Omit< BlockConfiguration, 'attributes' | 'title' > & { + ancestor: string[]; +} = { category: 'woocommerce-product-elements', keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ], icon: { diff --git a/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx b/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx new file mode 100644 index 00000000000..4b0039f8cd9 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx @@ -0,0 +1,77 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import ProductControl from '@woocommerce/editor-components/product-control'; +import { Placeholder, Button, ToolbarGroup } from '@wordpress/components'; +import { BlockControls } from '@wordpress/block-editor'; +import TextToolbarButton from '@woocommerce/editor-components/text-toolbar-button'; +import { useState } from 'react'; + +export const ProductSelector = ( { + productId, + icon, + label, + description, + setAttributes, + children, +}: { + productId: string; + icon: string; + label: string; + description: string; + setAttributes: ( obj: Record< string, string > ) => void; + children: React.ReactNode; +} ) => { + const [ isEditing, setIsEditing ] = useState( ! productId ); + + return ( + <> + { isEditing ? ( + + { !! description &&
    { description }
    } +
    + { + setAttributes( { + productId: value[ 0 ] ? value[ 0 ].id : 0, + } ); + } } + /> + +
    +
    + ) : ( + <> + + + setIsEditing( true ) } + > + { __( + 'Switch product…', + 'woo-gutenberg-products-block' + ) } + + + + { children } + + ) } + + ); +}; From a3d1ba37730ba796060e7b9b7e40594434ca2e20 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Mar 2023 12:46:38 +0100 Subject: [PATCH 61/68] fix missing import --- assets/js/atomic/blocks/product-elements/price/edit.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/atomic/blocks/product-elements/price/edit.tsx b/assets/js/atomic/blocks/product-elements/price/edit.tsx index 58e3dc8b273..8920223ebd1 100644 --- a/assets/js/atomic/blocks/product-elements/price/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/price/edit.tsx @@ -9,6 +9,7 @@ import { import { useEffect } from '@wordpress/element'; import type { BlockAlignment } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -110,7 +111,7 @@ const PriceEdit = ( { icon={ BLOCK_ICON } label={ BLOCK_TITLE } description={ __( - 'Choose a product to display its SKU.', + 'Choose a product to display its price.', 'woo-gutenberg-products-block' ) } > From a3c3673e78beca06e4862cb9ccba95acfcfec87d Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Mar 2023 12:48:58 +0100 Subject: [PATCH 62/68] add comment --- assets/js/atomic/blocks/product-elements/price/block.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/js/atomic/blocks/product-elements/price/block.tsx b/assets/js/atomic/blocks/product-elements/price/block.tsx index ebbf433c2b4..a1ca7e9e62e 100644 --- a/assets/js/atomic/blocks/product-elements/price/block.tsx +++ b/assets/js/atomic/blocks/product-elements/price/block.tsx @@ -102,7 +102,7 @@ export const Block = ( props: Props ): JSX.Element | null => { regularPrice={ isDescendentOfSingleProductTemplate ? pricePreview - : prices.price + : prices.regular_price } regularPriceClassName={ classnames( { [ `${ parentClassName }__product-price__regular` ]: @@ -114,6 +114,12 @@ export const Block = ( props: Props ): JSX.Element | null => { }; export default ( props: Props ) => { + // It is necessary because this block has to support serveral context: + // - Inside `All Products Block` -> `withProductDataContext` HOC + // - Inside `Products Block` -> Gutenberg Context + // - Inside `Single Product Template` -> Gutenberg Context + // - Without any parent -> `WithSelector` and `withProductDataContext` HOCs + // For more details, check https://github.com/woocommerce/woocommerce-blocks/pull/8609 if ( props.isDescendentOfSingleProductTemplate ) { return ; } From 8ccb2b305b42d0b9c5156836743f9c306d939409 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Mar 2023 12:51:58 +0100 Subject: [PATCH 63/68] return a value --- .../atomic/blocks/product-elements/price/edit.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/assets/js/atomic/blocks/product-elements/price/edit.tsx b/assets/js/atomic/blocks/product-elements/price/edit.tsx index 8920223ebd1..04c9f8a8d14 100644 --- a/assets/js/atomic/blocks/product-elements/price/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/price/edit.tsx @@ -53,18 +53,16 @@ const PriceEdit = ( { }; const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); - const { isDescendentOfSingleProductTemplate } = useSelect( + const isDescendentOfSingleProductTemplate = useSelect( ( select ) => { const store = select( 'core/edit-site' ); const postId = store?.getEditedPostId(); - return { - isDescendentOfSingleProductTemplate: - ( postId === 'woocommerce/woocommerce//product-meta' || - postId === - 'woocommerce/woocommerce//single-product' ) && - ! isDescendentOfQueryLoop, - }; + return ( + ( postId === 'woocommerce/woocommerce//product-meta' || + postId === 'woocommerce/woocommerce//single-product' ) && + ! isDescendentOfQueryLoop + ); }, [ isDescendentOfQueryLoop ] ); From 9d8a43a60dcb16bda85c5dde508bebc63c9b5f79 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Mar 2023 12:52:34 +0100 Subject: [PATCH 64/68] improve comment --- assets/js/atomic/blocks/product-elements/price/block.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/atomic/blocks/product-elements/price/block.tsx b/assets/js/atomic/blocks/product-elements/price/block.tsx index a1ca7e9e62e..95020978e38 100644 --- a/assets/js/atomic/blocks/product-elements/price/block.tsx +++ b/assets/js/atomic/blocks/product-elements/price/block.tsx @@ -114,8 +114,8 @@ export const Block = ( props: Props ): JSX.Element | null => { }; export default ( props: Props ) => { - // It is necessary because this block has to support serveral context: - // - Inside `All Products Block` -> `withProductDataContext` HOC + // It is necessary because this block has to support serveral contexts: + // - Inside `All Products Block` -> `withProductDataContext` HOC // - Inside `Products Block` -> Gutenberg Context // - Inside `Single Product Template` -> Gutenberg Context // - Without any parent -> `WithSelector` and `withProductDataContext` HOCs From 5b47d97800d2bffa4f327a02e33cf5c34e0cf5ed Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Mar 2023 12:53:53 +0100 Subject: [PATCH 65/68] fix import --- .../atomic/blocks/product-elements/shared/product-selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx b/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx index 4b0039f8cd9..68568fa718a 100644 --- a/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx +++ b/assets/js/atomic/blocks/product-elements/shared/product-selector.tsx @@ -6,7 +6,7 @@ import ProductControl from '@woocommerce/editor-components/product-control'; import { Placeholder, Button, ToolbarGroup } from '@wordpress/components'; import { BlockControls } from '@wordpress/block-editor'; import TextToolbarButton from '@woocommerce/editor-components/text-toolbar-button'; -import { useState } from 'react'; +import { useState } from '@wordpress/element'; export const ProductSelector = ( { productId, From 4c8f30d9fab02003567f5f64d5daed1fa0dce851 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 3 Mar 2023 15:00:19 +0100 Subject: [PATCH 66/68] Add Product Meta block --- assets/js/atomic/blocks/index.js | 1 + .../product-elements/product-meta/block.json | 17 +++++ .../product-elements/product-meta/edit.tsx | 50 ++++++++++++ .../product-elements/product-meta/editor.scss | 4 + .../product-elements/product-meta/index.tsx | 28 +++++++ .../product-elements/product-meta/save.tsx | 17 +++++ .../shared/with-product-selector.js | 2 +- .../blocks/product-elements/sku/edit.tsx | 76 +++---------------- .../blocks/product-elements/sku/index.tsx | 6 ++ .../SingleProductTemplateCompatibility.php | 56 +++----------- templates/parts/product-meta.html | 3 - 11 files changed, 144 insertions(+), 116 deletions(-) create mode 100644 assets/js/atomic/blocks/product-elements/product-meta/block.json create mode 100644 assets/js/atomic/blocks/product-elements/product-meta/edit.tsx create mode 100644 assets/js/atomic/blocks/product-elements/product-meta/editor.scss create mode 100644 assets/js/atomic/blocks/product-elements/product-meta/index.tsx create mode 100644 assets/js/atomic/blocks/product-elements/product-meta/save.tsx delete mode 100644 templates/parts/product-meta.html diff --git a/assets/js/atomic/blocks/index.js b/assets/js/atomic/blocks/index.js index ab691403fc8..8b66f58247a 100644 --- a/assets/js/atomic/blocks/index.js +++ b/assets/js/atomic/blocks/index.js @@ -17,3 +17,4 @@ import './product-elements/add-to-cart-form'; import './product-elements/product-image-gallery'; import './product-elements/product-details'; import './product-elements/related-products'; +import './product-elements/product-meta'; diff --git a/assets/js/atomic/blocks/product-elements/product-meta/block.json b/assets/js/atomic/blocks/product-elements/product-meta/block.json new file mode 100644 index 00000000000..f2d22b4f6b2 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-meta/block.json @@ -0,0 +1,17 @@ +{ + "name": "woocommerce/product-meta", + "version": "1.0.0", + "title": "Product Meta", + "icon": "product", + "description": "Display Product Meta", + "category": "woocommerce", + "supports": { + "align": true, + "reusable": false + }, + "keywords": [ "WooCommerce" ], + "usesContext": [ "postId", "postType", "queryId" ], + "textdomain": "woo-gutenberg-products-block", + "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json" +} diff --git a/assets/js/atomic/blocks/product-elements/product-meta/edit.tsx b/assets/js/atomic/blocks/product-elements/product-meta/edit.tsx new file mode 100644 index 00000000000..1058d11faa6 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-meta/edit.tsx @@ -0,0 +1,50 @@ +/** + * External dependencies + */ +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; +import { InnerBlockTemplate } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import './editor.scss'; + +const Edit = () => { + const TEMPLATE: InnerBlockTemplate[] = [ + [ + 'core/group', + { layout: { type: 'flex', flexWrap: 'nowrap' } }, + [ + [ + 'woocommerce/product-sku', + { + isDescendentOfSingleProductTemplate: true, + }, + ], + [ + 'core/post-terms', + { + prefix: 'Category: ', + term: 'product_cat', + }, + ], + [ + 'core/post-terms', + { + prefix: 'Tags: ', + term: 'product_tag', + }, + ], + ], + ], + ]; + const blockProps = useBlockProps(); + + return ( +
    + +
    + ); +}; + +export default Edit; diff --git a/assets/js/atomic/blocks/product-elements/product-meta/editor.scss b/assets/js/atomic/blocks/product-elements/product-meta/editor.scss new file mode 100644 index 00000000000..b3872daea3a --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-meta/editor.scss @@ -0,0 +1,4 @@ +.wc-block-editor-related-products__notice { + margin: 10px auto; + max-width: max-content; +} diff --git a/assets/js/atomic/blocks/product-elements/product-meta/index.tsx b/assets/js/atomic/blocks/product-elements/product-meta/index.tsx new file mode 100644 index 00000000000..1db84adc949 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-meta/index.tsx @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { box as icon } from '@wordpress/icons'; +import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; +import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils'; + +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import metadata from './block.json'; + +registerBlockSingleProductTemplate( { + registerBlockFn: () => { + // @ts-expect-error: `registerBlockType` is a function that is typed in WordPress core. + registerBlockType( metadata, { + icon, + edit, + save, + } ); + }, + unregisterBlockFn: () => { + unregisterBlockType( metadata.name ); + }, + blockName: metadata.name, +} ); diff --git a/assets/js/atomic/blocks/product-elements/product-meta/save.tsx b/assets/js/atomic/blocks/product-elements/product-meta/save.tsx new file mode 100644 index 00000000000..0feb6d8f950 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/product-meta/save.tsx @@ -0,0 +1,17 @@ +/** + * External dependencies + */ +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; + +const Save = () => { + const blockProps = useBlockProps.save(); + + return ( +
    + { /* @ts-expect-error: `InnerBlocks.Content` is a component that is typed in WordPress core*/ } + +
    + ); +}; + +export default Save; diff --git a/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js b/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js index 151a4c2b8d6..abaa13fa2ed 100644 --- a/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js +++ b/assets/js/atomic/blocks/product-elements/shared/with-product-selector.js @@ -23,7 +23,7 @@ import './editor.scss'; const withProductSelector = ( selectorArgs ) => ( OriginalComponent ) => { return ( props ) => { const productDataContext = useProductDataContext(); - const { attributes, setAttributes, showProductSelector } = props; + const { attributes, setAttributes } = props; const { productId } = attributes; const [ isEditing, setIsEditing ] = useState( ! productId ); diff --git a/assets/js/atomic/blocks/product-elements/sku/edit.tsx b/assets/js/atomic/blocks/product-elements/sku/edit.tsx index 401489c4ed1..b259af2065f 100644 --- a/assets/js/atomic/blocks/product-elements/sku/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/sku/edit.tsx @@ -1,7 +1,6 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; import type { BlockEditProps } from '@wordpress/blocks'; import EditProductLink from '@woocommerce/editor-components/edit-product-link'; import { ProductQueryContext as Context } from '@woocommerce/blocks/product-query/types'; @@ -11,13 +10,9 @@ import { useEffect } from '@wordpress/element'; * Internal dependencies */ import Block from './block'; -import { BLOCK_TITLE, BLOCK_ICON } from './constants'; import type { Attributes } from './types'; -import { ProductSelector } from '../shared/product-selector'; -import { useBlockProps } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; -export default ( { +const Edit = ( { attributes, setAttributes, context, @@ -28,70 +23,17 @@ export default ( { }; const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); - const { showProductSelector, isDescendentOfSingleProductTemplate } = - useSelect( - ( select ) => { - const store = select( 'core/edit-site' ); - const postId = store?.getEditedPostId(); - - const descendentOfSingleProductTemplate = - ( postId === 'woocommerce/woocommerce//product-meta' || - postId === - 'woocommerce/woocommerce//single-product' ) && - ! isDescendentOfQueryLoop; - - return { - isDescendentOfSingleProductTemplate: - descendentOfSingleProductTemplate, - showProductSelector: - ! isDescendentOfQueryLoop && - ! descendentOfSingleProductTemplate, - }; - }, - [ isDescendentOfQueryLoop ] - ); - - const blockProps = useBlockProps(); - useEffect( - () => - setAttributes( { - isDescendentOfQueryLoop, - isDescendentOfSingleProductTemplate, - showProductSelector, - } ), - [ - isDescendentOfQueryLoop, - isDescendentOfSingleProductTemplate, - setAttributes, - showProductSelector, - ] + () => setAttributes( { isDescendentOfQueryLoop } ), + [ setAttributes, isDescendentOfQueryLoop ] ); - if ( ! showProductSelector ) { - return ( -
    - - -
    - ); - } - return ( -
    - - - - -
    + <> + + + ); }; + +export default Edit; diff --git a/assets/js/atomic/blocks/product-elements/sku/index.tsx b/assets/js/atomic/blocks/product-elements/sku/index.tsx index 56148fbcd3e..4774bdc03bd 100644 --- a/assets/js/atomic/blocks/product-elements/sku/index.tsx +++ b/assets/js/atomic/blocks/product-elements/sku/index.tsx @@ -27,6 +27,12 @@ const blockConfig: BlockConfiguration = { icon: { src: icon }, usesContext: [ 'query', 'queryId', 'postId' ], attributes, + ancestor: [ + 'woocommerce/all-products', + 'woocommerce/single-product', + 'core/post-template', + 'woocommerce/product-meta', + ], edit, save: () => { if ( diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php index 07603ff24a8..7ba8f89ed4c 100644 --- a/src/Templates/SingleProductTemplateCompatibility.php +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -85,21 +85,6 @@ private function inject_hook_to_first_and_last_blocks( $block_content, $block ) ), ); - if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['slug'] ) && 'product-meta' === $block['attrs']['slug'] ) { - return sprintf( - '%1$s%2$s%3$s', - $this->get_hooks_buffer( - $this->hook_data['woocommerce_product_meta_start'], - 'before' - ), - $block_content, - $this->get_hooks_buffer( - $this->hook_data['woocommerce_product_meta_end'], - 'after' - ) - ); - } - if ( isset( $block['attrs'][ self::IS_FIRST_BLOCK ] ) && isset( $block['attrs'][ self::IS_LAST_BLOCK ] ) ) { return sprintf( '%1$s%2$s%3$s', @@ -218,18 +203,18 @@ protected function set_hook_data() { 'woocommerce_template_single_sharing' => 50, ), ), - 'woocommerce_product_meta_start' => array( + 'woocommerce_after_single_product' => array( 'block_name' => '', - 'position' => 'before', + 'position' => 'after', 'hooked' => array(), ), - 'woocommerce_product_meta_end' => array( - 'block_name' => '', - 'position' => 'after', + 'woocommerce_product_meta_start' => array( + 'block_name' => 'woocommerce/product-meta', + 'position' => 'before', 'hooked' => array(), ), - 'woocommerce_after_single_product' => array( - 'block_name' => '', + 'woocommerce_product_meta_end' => array( + 'block_name' => 'woocommerce/product-meta', 'position' => 'after', 'hooked' => array(), ), @@ -289,7 +274,7 @@ private static function wrap_single_product_template( $template_content ) { $wrapped_blocks = array_map( function( $blocks ) use ( $single_product_template_blocks ) { - if ( self::is_template_part( $blocks[0] ) ) { + if ( 'core/template-part' === $blocks[0]['blockName'] ) { return $blocks; } @@ -321,7 +306,7 @@ function( $carry, $item ) { $carry['index'] = $carry['index'] + 1; $block = $item[0]; - if ( self::is_template_part( $block ) ) { + if ( 'core/template-part' === $block['blockName'] ) { $carry['template'][] = $block; return $carry; } @@ -429,7 +414,7 @@ private static function group_blocks( $parsed_blocks ) { return array_reduce( $parsed_blocks, function( $carry, $block ) { - if ( self::is_template_part( $block ) ) { + if ( 'core/template-part' === $block['blockName'] ) { $carry[] = array( $block ); return $carry; } @@ -437,7 +422,7 @@ function( $carry, $block ) { return $carry; } $last_element_index = count( $carry ) - 1; - if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && ! self::is_template_part( $carry[ $last_element_index ][0] ) ) { + if ( isset( $carry[ $last_element_index ][0]['blockName'] ) && 'core/template-part' !== $carry[ $last_element_index ][0]['blockName'] ) { $carry[ $last_element_index ][] = $block; return $carry; } @@ -447,23 +432,4 @@ function( $carry, $block ) { array() ); } - - /** - * Check if the block is a template part except for the product meta template part. - * - * @param array $block Block object. - * @return bool True if the block is a template part, false otherwise. - */ - private static function is_template_part( $block ) { - if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['slug'] ) && 'product-meta' === $block['attrs']['slug'] ) { - return false; - } - - if ( 'core/template-part' === $block['blockName'] ) { - return true; - } - - return false; - - } } diff --git a/templates/parts/product-meta.html b/templates/parts/product-meta.html deleted file mode 100644 index d5fc82f4086..00000000000 --- a/templates/parts/product-meta.html +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file From 749c4e5a82b072e7af80a2dbc871af0e582825c7 Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 7 Mar 2023 16:48:21 +0100 Subject: [PATCH 67/68] remove unnecessary change --- assets/js/shared/context/product-data-context.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/assets/js/shared/context/product-data-context.tsx b/assets/js/shared/context/product-data-context.tsx index aa0c157464f..deb6f2d8d0e 100644 --- a/assets/js/shared/context/product-data-context.tsx +++ b/assets/js/shared/context/product-data-context.tsx @@ -72,28 +72,25 @@ interface ProductDataContextProviderProps { product: ProductResponseItem | null; children: JSX.Element | JSX.Element[]; isLoading: boolean; - hasContext?: boolean; } /** * This context is used to pass product data down to all children blocks in a given tree. * - * @param {Object} object A react context object - * @param {any|null} object.product The product data to be passed down - * @param {Object} object.children The product data to be passed down - * @param {boolean} object.isLoading The product data to be passed down - * @param {boolean} object.hasContext The product data to be passed down + * @param {Object} object A react context object + * @param {any|null} object.product The product data to be passed down + * @param {Object} object.children The product data to be passed down + * @param {boolean} object.isLoading The product data to be passed down */ export const ProductDataContextProvider = ( { product = null, children, isLoading, - hasContext = true, }: ProductDataContextProviderProps ) => { const contextValue = { product: product || defaultProductData, isLoading, - hasContext, + hasContext: true, }; return ( From d9c9b960b17b2e4bdd9e3d85f8bb7a6a46450395 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 8 Mar 2023 15:27:53 +0100 Subject: [PATCH 68/68] fix compatibility layer when the Single Product template has the classic block --- src/BlockTemplatesController.php | 3 +- .../SingleProductTemplateCompatibility.php | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index a08918ef4e1..e1c5c82c7d8 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -327,7 +327,8 @@ function( $template ) { } if ( 'single-product' === $template->slug ) { - if ( ! is_admin() ) { + if ( ! is_admin() && ! BlockTemplateUtils::template_has_legacy_template_block( $template ) ) { + $new_content = SingleProductTemplateCompatibility::add_compatibility_layer( $template->content ); $template->content = $new_content; } diff --git a/src/Templates/SingleProductTemplateCompatibility.php b/src/Templates/SingleProductTemplateCompatibility.php index 7ba8f89ed4c..ca60ecdf3a2 100644 --- a/src/Templates/SingleProductTemplateCompatibility.php +++ b/src/Templates/SingleProductTemplateCompatibility.php @@ -27,12 +27,6 @@ public function inject_hooks( $block_content, $block ) { $this->remove_default_hooks(); - $first_or_last_block_content = $this->inject_hook_to_first_and_last_blocks( $block_content, $block ); - - if ( isset( $first_or_last_block_content ) ) { - return $first_or_last_block_content; - } - $block_name = $block['blockName']; $block_hooks = array_filter( @@ -42,6 +36,12 @@ function( $hook ) use ( $block_name ) { } ); + $first_or_last_block_content = $this->inject_hook_to_first_and_last_blocks( $block_content, $block, $block_hooks ); + + if ( isset( $first_or_last_block_content ) ) { + return $first_or_last_block_content; + } + return sprintf( '%1$s%2$s%3$s', $this->get_hooks_buffer( $block_hooks, 'before' ), @@ -63,9 +63,10 @@ function( $hook ) use ( $block_name ) { * * @param mixed $block_content The rendered block content. * @param mixed $block The parsed block data. + * @param array $block_hooks The hooks that should be injected to the block. * @return string */ - private function inject_hook_to_first_and_last_blocks( $block_content, $block ) { + private function inject_hook_to_first_and_last_blocks( $block_content, $block, $block_hooks ) { $first_block_hook = array( 'before' => array( 'woocommerce_before_main_content' => $this->hook_data['woocommerce_before_main_content'], @@ -91,6 +92,7 @@ private function inject_hook_to_first_and_last_blocks( $block_content, $block ) $this->get_hooks_buffer( array_merge( $first_block_hook['before'], + $block_hooks, $last_block_hook['before'] ), 'before' @@ -99,6 +101,7 @@ private function inject_hook_to_first_and_last_blocks( $block_content, $block ) $this->get_hooks_buffer( array_merge( $first_block_hook['after'], + $block_hooks, $last_block_hook['after'] ), 'after' @@ -110,12 +113,18 @@ private function inject_hook_to_first_and_last_blocks( $block_content, $block ) return sprintf( '%1$s%2$s%3$s', $this->get_hooks_buffer( - $first_block_hook['before'], + array_merge( + $first_block_hook['before'], + $block_hooks + ), 'before' ), $block_content, $this->get_hooks_buffer( - $first_block_hook['after'], + array_merge( + $first_block_hook['after'], + $block_hooks + ), 'after' ) ); @@ -124,10 +133,19 @@ private function inject_hook_to_first_and_last_blocks( $block_content, $block ) if ( isset( $block['attrs'][ self::IS_LAST_BLOCK ] ) ) { return sprintf( '%1$s%2$s%3$s', - $this->get_hooks_buffer( $last_block_hook['before'], 'before' ), + $this->get_hooks_buffer( + array_merge( + $last_block_hook['before'], + $block_hooks + ), + 'before' + ), $block_content, $this->get_hooks_buffer( - $last_block_hook['after'], + array_merge( + $block_hooks, + $last_block_hook['after'] + ), 'after' ) );