diff --git a/assets/js/blocks/mini-cart/utils/data.ts b/assets/js/blocks/mini-cart/utils/data.ts index 7cb5c78c316..55734b2d259 100644 --- a/assets/js/blocks/mini-cart/utils/data.ts +++ b/assets/js/blocks/mini-cart/utils/data.ts @@ -6,7 +6,20 @@ import { getCurrencyFromPriceResponse, formatPrice, } from '@woocommerce/price-format'; -import { CartResponse } from '@woocommerce/types'; +import { CartResponse, isBoolean } from '@woocommerce/types'; +import { getSettingWithCoercion } from '@woocommerce/settings'; + +const getPrice = ( cartResponse: CartResponse, showIncludingTax: boolean ) => { + const { totals } = cartResponse; + const currency = getCurrencyFromPriceResponse( totals ); + + const subTotal = showIncludingTax + ? parseInt( totals.total_items, 10 ) + + parseInt( totals.total_items_tax, 10 ) + : parseInt( totals.total_items, 10 ); + + return formatPrice( subTotal, currency ); +}; export const updateTotals = ( totals: [ string, number ] | undefined ) => { if ( ! totals ) { @@ -86,11 +99,12 @@ export const getMiniCartTotalsFromLocalStorage = (): return undefined; } const miniCartTotals = JSON.parse( rawMiniCartTotals ); - const currency = getCurrencyFromPriceResponse( miniCartTotals.totals ); - const formattedPrice = formatPrice( - miniCartTotals.totals.total_price, - currency + const showIncludingTax = getSettingWithCoercion( + 'displayCartPricesIncludingTax', + false, + isBoolean ); + const formattedPrice = getPrice( miniCartTotals, showIncludingTax ); return [ formattedPrice, miniCartTotals.itemsCount ] as [ string, number ]; }; @@ -107,11 +121,12 @@ export const getMiniCartTotalsFromServer = async (): Promise< return response.json(); } ) .then( ( data: CartResponse ) => { - const currency = getCurrencyFromPriceResponse( data.totals ); - const formattedPrice = formatPrice( - data.totals.total_price, - currency + const showIncludingTax = getSettingWithCoercion( + 'displayCartPricesIncludingTax', + false, + isBoolean ); + const formattedPrice = getPrice( data, showIncludingTax ); // Save server data to local storage, so we can re-fetch it faster // on the next page load. localStorage.setItem( diff --git a/assets/js/blocks/mini-cart/utils/test/data.ts b/assets/js/blocks/mini-cart/utils/test/data.ts index 73e72b73f8e..341a590f813 100644 --- a/assets/js/blocks/mini-cart/utils/test/data.ts +++ b/assets/js/blocks/mini-cart/utils/test/data.ts @@ -3,6 +3,7 @@ * External dependencies */ import { getByTestId, waitFor } from '@testing-library/dom'; +import { getSettingWithCoercion } from '@woocommerce/settings'; /** * Internal dependencies @@ -18,7 +19,9 @@ const responseMock = { ok: true, json: async () => ( { totals: { - total_price: '1600', + total_price: '1800', + total_items: '1400', + total_items_tax: '200', currency_code: 'USD', currency_symbol: '$', currency_minor_unit: 2, @@ -32,7 +35,9 @@ const responseMock = { } as Response; const localStorageMock = { totals: { - total_price: '1600', + total_price: '1800', + total_items: '1400', + total_items_tax: '200', currency_code: 'USD', currency_symbol: '$', currency_minor_unit: 2, @@ -67,7 +72,21 @@ const getMiniCartDOM = () => { return div; }; -describe( 'Mini-Cart frontend script', () => { +jest.mock( '@woocommerce/settings', () => { + return { + ...jest.requireActual( '@woocommerce/settings' ), + getSettingWithCoercion: jest.fn(), + }; +} ); + +describe( 'Mini-Cart frontend script when "the display prices during cart and checkout" option is set to "Including Tax"', () => { + beforeAll( () => { + ( getSettingWithCoercion as jest.Mock ).mockReturnValue( true ); + } ); + + afterAll( () => { + jest.resetModules(); + } ); it( 'updates the cart contents based on the localStorage values', async () => { initializeLocalStorage(); const container = getMiniCartDOM(); @@ -125,3 +144,65 @@ describe( 'Mini-Cart frontend script', () => { jest.restoreAllMocks(); } ); } ); + +describe( 'Mini-Cart frontend script when "the display prices during cart and checkout" option is set to "Excluding Tax"', () => { + beforeAll( () => { + ( getSettingWithCoercion as jest.Mock ).mockReturnValue( false ); + } ); + it( 'updates the cart contents based on the localStorage values', async () => { + initializeLocalStorage(); + const container = getMiniCartDOM(); + document.body.appendChild( container ); + + updateTotals( getMiniCartTotalsFromLocalStorage() ); + + // Assert that we are rendering the amount. + await waitFor( () => + expect( getByTestId( container, 'amount' ).textContent ).toBe( + '$14.00' + ) + ); + // Assert that we are rendering the quantity. + await waitFor( () => + expect( getByTestId( container, 'quantity' ).textContent ).toBe( + '2' + ) + ); + } ); + + it( 'updates the cart contents based on the API response', async () => { + jest.spyOn( window, 'fetch' ).mockResolvedValue( responseMock ); + const container = getMiniCartDOM(); + document.body.appendChild( container ); + + getMiniCartTotalsFromServer().then( updateTotals ); + + // Assert we called the correct endpoint. + await waitFor( () => + expect( window.fetch ).toHaveBeenCalledWith( + '/wp-json/wc/store/v1/cart/' + ) + ); + + // Assert we saved the values returned to the localStorage. + await waitFor( () => + expect( window.localStorage.setItem.mock.calls[ 0 ][ 1 ] ).toEqual( + JSON.stringify( localStorageMock ) + ) + ); + + // Assert that we are rendering the amount. + await waitFor( () => + expect( getByTestId( container, 'amount' ).textContent ).toBe( + '$14.00' + ) + ); + // Assert that we are rendering the quantity. + await waitFor( () => + expect( getByTestId( container, 'quantity' ).textContent ).toBe( + '2' + ) + ); + jest.restoreAllMocks(); + } ); +} ); diff --git a/assets/js/blocks/product-query/constants.ts b/assets/js/blocks/product-query/constants.ts index 61e0b9771cc..ec91fd2f4f1 100644 --- a/assets/js/blocks/product-query/constants.ts +++ b/assets/js/blocks/product-query/constants.ts @@ -1,10 +1,10 @@ /** * External dependencies */ -import { getSetting } from '@woocommerce/settings'; +import { getSetting, getSettingWithCoercion } from '@woocommerce/settings'; import { objectOmit } from '@woocommerce/utils'; import type { InnerBlockTemplate } from '@wordpress/blocks'; - +import { isBoolean } from '@woocommerce/types'; /** * Internal dependencies */ @@ -69,6 +69,13 @@ export const QUERY_DEFAULT_ATTRIBUTES: QueryBlockAttributes = { }, }; +// This is necessary to fix https://github.com/woocommerce/woocommerce-blocks/issues/9884. +const postTemplateHasSupportForGridView = getSettingWithCoercion( + 'post_template_has_support_for_grid_view', + false, + isBoolean +); + export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [ [ 'core/post-template', @@ -78,6 +85,9 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [ * This class is used to add default styles for inner blocks. */ className: 'products-block-post-template', + ...( postTemplateHasSupportForGridView && { + layout: { type: 'grid', columnCount: 3 }, + } ), }, [ [ diff --git a/assets/js/blocks/product-query/variations/related-products.tsx b/assets/js/blocks/product-query/variations/related-products.tsx index e13a243f48b..7fa859db0a9 100644 --- a/assets/js/blocks/product-query/variations/related-products.tsx +++ b/assets/js/blocks/product-query/variations/related-products.tsx @@ -6,6 +6,8 @@ import { Icon } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { stacks } from '@woocommerce/icons'; import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils'; +import { getSettingWithCoercion } from '@woocommerce/settings'; +import { isBoolean } from '@woocommerce/types'; /** * Internal dependencies @@ -43,6 +45,12 @@ export const BLOCK_ATTRIBUTES = { }, }; +const postTemplateHasSupportForGridView = getSettingWithCoercion( + 'post_template_has_support_for_grid_view', + false, + isBoolean +); + export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [ [ 'core/heading', @@ -53,7 +61,12 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [ ], [ 'core/post-template', - { __woocommerceNamespace: PRODUCT_TEMPLATE_ID }, + { + __woocommerceNamespace: PRODUCT_TEMPLATE_ID, + ...( postTemplateHasSupportForGridView && { + layout: { type: 'grid', columnCount: 3 }, + } ), + }, [ [ 'woocommerce/product-image', diff --git a/composer.json b/composer.json index f492882d3d2..24db222009a 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "WooCommerce blocks for the Gutenberg editor.", "homepage": "https://woocommerce.com/", "type": "wordpress-plugin", - "version": "10.4.2", + "version": "10.4.3", "keywords": [ "gutenberg", "woocommerce", diff --git a/docs/internal-developers/testing/releases/1043.md b/docs/internal-developers/testing/releases/1043.md new file mode 100644 index 00000000000..703dcb7ea9d --- /dev/null +++ b/docs/internal-developers/testing/releases/1043.md @@ -0,0 +1,36 @@ +# Testing notes and ZIP for release 10.4.3 + +Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github.com/woocommerce/woocommerce-blocks/files/11801978/woocommerce-gutenberg-products-block.zip) + +## WooCommerce Core + +### Products block: fix compatibility with Gutenberg 16. ([9916](https://github.com/woocommerce/woocommerce-blocks/pull/9916)) + +1. Ensure that you have Gutenberg 16 installed and enabled. +2. Open the post/page editor. +3. Add the Products Block. +4. Ensure that the Products Block defaults to the grid layout. + +### Mini Cart Block: show the total price, including tax, according to the option. ([9878](https://github.com/woocommerce/woocommerce-blocks/pull/9878)) + +1. Open the WooCommerce Settings via WooCommerce > Settings from the sidebar menu of the WP-Admin. +2. Enable the option "Enable tax rates and calculations". +3. Click on the "Tax" tab. +4. Click on "Standard Rates" and configure a tax rate. Save. +5. Click on the "Tax" tab. +6. Set the "Display prices during cart and checkout" option to "Including Tax". +7. Now set up a Shipping method going to WooCommerce > Settings > Shipping > Add shipping zone. Create the zone and add a shipping method with a price different from 0. +8. With the Site Editor adds the Mini Cart in the header. +9. On the front end, add a product to the cart and go to the Cart page, so shipping price is calculated. +10. Go back to the Shop page +11. Ensure that the Mini Cart shows the price including the tax, but not including the Shipping prive. +12. Hover the Mini Cart. +13. Ensure that the Mini Cart shows always the same price. +14. Open the WooCommerce Settings. +15. Click on the "Tax" tab. +16. Set the "Display prices during cart and checkout" option to "Excluding Tax". +17. On the front end, add a product to the cart. +18. Refresh the page. +19. Ensure that the Mini Cart shows the price excluding the tax. +20. Hover the Mini Cart. +21. Ensure that the Mini Cart shows always the same price. diff --git a/docs/internal-developers/testing/releases/README.md b/docs/internal-developers/testing/releases/README.md index f1d32de06ae..b4801314dc1 100644 --- a/docs/internal-developers/testing/releases/README.md +++ b/docs/internal-developers/testing/releases/README.md @@ -150,7 +150,9 @@ Every release includes specific testing instructions for new features and bug fi - [10.2.2](./1022.md) - [10.3.0](./1030.md) - [10.4.0](./1040.md) -- [10.4.2](./1042.md) + - [10.4.2](./1042.md) + - [10.4.3](./1043.md) + --- diff --git a/package-lock.json b/package-lock.json index dc293f75b52..709c08117cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@woocommerce/block-library", - "version": "10.4.2", + "version": "10.4.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@woocommerce/block-library", - "version": "10.4.2", + "version": "10.4.3", "hasInstallScript": true, "license": "GPL-3.0+", "dependencies": { diff --git a/package.json b/package.json index 1a69849ff72..f8f02495a2d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@woocommerce/block-library", "title": "WooCommerce Blocks", "author": "Automattic", - "version": "10.4.2", + "version": "10.4.3", "description": "WooCommerce blocks for the Gutenberg editor.", "homepage": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/", "keywords": [ diff --git a/readme.txt b/readme.txt index 4206e7d6eac..9d9ac41301b 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: gutenberg, woocommerce, woo commerce, products, blocks, woocommerce blocks Requires at least: 6.2 Tested up to: 6.2 Requires PHP: 7.3 -Stable tag: 10.4.2 +Stable tag: 10.4.3 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -81,6 +81,12 @@ Release and roadmap notes available on the [WooCommerce Developers Blog](https:/ == Changelog == += 10.4.3 - 2023-06-20 = + +#### Bug Fixes + +- Products block: fix compatibility with Gutenberg 16. ([9878](https://github.com/woocommerce/woocommerce-blocks/pull/9878)) + = 10.4.2 - 2023-06-12 = #### Bug Fixes diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index 2edd0a5a956..380f9a34224 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -75,6 +75,38 @@ protected function initialize() { add_filter( 'rest_product_collection_params', array( $this, 'extend_rest_query_allowed_params' ), 10, 1 ); } + /** + * Extra data passed through from server to client for block. + * + * @param array $attributes Any attributes that currently are available from the block. + * Note, this will be empty in the editor context when the block is + * not in the post content on editor load. + */ + protected function enqueue_data( array $attributes = [] ) { + parent::enqueue_data( $attributes ); + + $gutenberg_version = ''; + + if ( is_plugin_active( 'gutenberg/gutenberg.php' ) ) { + if ( defined( 'GUTENBERG_VERSION' ) ) { + $gutenberg_version = GUTENBERG_VERSION; + } + + if ( ! $gutenberg_version ) { + $gutenberg_data = get_file_data( + WP_PLUGIN_DIR . '/gutenberg/gutenberg.php', + array( 'Version' => 'Version' ) + ); + $gutenberg_version = $gutenberg_data['Version']; + } + } + + $this->asset_data_registry->add( + 'post_template_has_support_for_grid_view', + version_compare( $gutenberg_version, '16.0', '>=' ) + ); + } + /** * Check if a given block * diff --git a/src/Package.php b/src/Package.php index 8aaeeddc506..40f58f983b1 100644 --- a/src/Package.php +++ b/src/Package.php @@ -109,7 +109,7 @@ public static function container( $reset = false ) { NewPackage::class, function ( $container ) { // leave for automated version bumping. - $version = '10.4.2'; + $version = '10.4.3'; return new NewPackage( $version, dirname( __DIR__ ), diff --git a/woocommerce-gutenberg-products-block.php b/woocommerce-gutenberg-products-block.php index f6eb385846c..197cc0d2a9e 100644 --- a/woocommerce-gutenberg-products-block.php +++ b/woocommerce-gutenberg-products-block.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce Blocks * Plugin URI: https://github.com/woocommerce/woocommerce-gutenberg-products-block * Description: WooCommerce blocks for the Gutenberg editor. - * Version: 10.4.2 + * Version: 10.4.3 * Author: Automattic * Author URI: https://woocommerce.com * Text Domain: woo-gutenberg-products-block