From 6f04cb6493b52948d63b5bc1aa606eea45e8b324 Mon Sep 17 00:00:00 2001 From: Thomas Roberts <5656702+opr@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:00:40 +0000 Subject: [PATCH 01/48] Migrate cart/checkout tax tests to Playwright (#11954) --- .../specs/shopper/cart-checkout/tax.test.js | 80 -------------- ...ut-block-taxes.shopper.block_theme.spec.ts | 102 ++++++++++++++++++ .../e2e/utils/frontend/frontend-utils.page.ts | 2 +- 3 files changed, 103 insertions(+), 81 deletions(-) delete mode 100644 tests/e2e-jest/specs/shopper/cart-checkout/tax.test.js create mode 100644 tests/e2e/tests/cart/cart-checkout-block-taxes.shopper.block_theme.spec.ts diff --git a/tests/e2e-jest/specs/shopper/cart-checkout/tax.test.js b/tests/e2e-jest/specs/shopper/cart-checkout/tax.test.js deleted file mode 100644 index 157f36b1182..00000000000 --- a/tests/e2e-jest/specs/shopper/cart-checkout/tax.test.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Internal dependencies - */ -import { - shopper, - getExpectedTaxes, - getTaxesFromCurrentPage, - getTaxesFromOrderSummaryPage, - showTaxes, - SIMPLE_VIRTUAL_PRODUCT_NAME, -} from '../../../../utils'; -import { Taxes, Products } from '../../../fixtures/fixture-data'; - -const taxRates = Taxes(); -const productWooSingle1 = Products().find( - ( prod ) => prod.name === 'Woo Single #1' -); - -if ( process.env.WOOCOMMERCE_BLOCKS_PHASE < 2 ) { - // Skips all the tests if it's a WooCommerce Core process environment. - // eslint-disable-next-line jest/no-focused-tests, jest/expect-expect - test.only( `Skipping Cart & Checkout tests`, () => {} ); -} - -describe.skip( 'Shopper → Cart & Checkout → Taxes', () => { - beforeEach( async () => { - await shopper.block.emptyCart(); - } ); - - describe( '"Enable tax rate calculations" is unchecked in WC settings -> general', () => { - it( 'User cannot view the tax on Cart, Checkout & Order Summary', async () => { - await showTaxes( false ); - await shopper.block.goToShop(); - await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); - await shopper.block.goToCart(); - - const cartTaxes = await getTaxesFromCurrentPage(); - expect( cartTaxes ).toEqual( [] ); - - await shopper.block.goToCheckout(); - const checkoutTaxes = await getTaxesFromCurrentPage(); - expect( checkoutTaxes ).toEqual( [] ); - - await shopper.block.fillInCheckoutWithTestData(); - await shopper.block.placeOrder(); - await page.waitForSelector( 'h1.entry-title' ); - const orderSummaryTaxes = await getTaxesFromOrderSummaryPage( - taxRates.filter( ( taxRate ) => taxRate.country === 'US' ) - ); - expect( orderSummaryTaxes ).toEqual( [] ); - } ); - } ); - - describe( '"Enable tax rate calculations" is checked in WC settings -> general', () => { - it( 'User can view the tax on Cart, Checkout & Order Summary', async () => { - await showTaxes( true ); - await shopper.block.goToShop(); - await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); - await shopper.block.goToCart(); - - const expectedTaxes = getExpectedTaxes( taxRates, 'US', [ - productWooSingle1, - ] ); - const cartTaxes = await getTaxesFromCurrentPage(); - expect( cartTaxes.sort() ).toEqual( expectedTaxes.sort() ); - - await shopper.block.goToCheckout(); - const checkoutTaxes = await getTaxesFromCurrentPage(); - expect( checkoutTaxes.sort() ).toEqual( expectedTaxes.sort() ); - - await shopper.block.fillInCheckoutWithTestData(); - await shopper.block.placeOrder(); - await page.waitForSelector( 'h1.entry-title' ); - const orderSummaryTaxes = await getTaxesFromOrderSummaryPage( - taxRates.filter( ( taxRate ) => taxRate.country === 'US' ) - ); - expect( orderSummaryTaxes.sort() ).toEqual( expectedTaxes.sort() ); - } ); - } ); -} ); diff --git a/tests/e2e/tests/cart/cart-checkout-block-taxes.shopper.block_theme.spec.ts b/tests/e2e/tests/cart/cart-checkout-block-taxes.shopper.block_theme.spec.ts new file mode 100644 index 00000000000..d722467e412 --- /dev/null +++ b/tests/e2e/tests/cart/cart-checkout-block-taxes.shopper.block_theme.spec.ts @@ -0,0 +1,102 @@ +/** + * External dependencies + */ +import { expect, test as base } from '@woocommerce/e2e-playwright-utils'; + +/** + * Internal dependencies + */ +import { + DISCOUNTED_PRODUCT_NAME, + REGULAR_PRICED_PRODUCT_NAME, +} from '../checkout/constants'; +import { CheckoutPage } from '../checkout/checkout.page'; + +const test = base.extend< { checkoutPageObject: CheckoutPage } >( { + checkoutPageObject: async ( { page }, use ) => { + const pageObject = new CheckoutPage( { + page, + } ); + await use( pageObject ); + }, +} ); + +test.describe( 'Shopper → Taxes', () => { + test( 'Tax visibility on Cart/Checkout/OrderSummary blocks depends on "Enable tax rate calculations" option in WC settings -> general', async ( { + requestUtils, + frontendUtils, + page, + checkoutPageObject, + } ) => { + // Turn off tax display. + await requestUtils.rest( { + method: 'PUT', + path: 'wc/v3/settings/general/woocommerce_calc_taxes', + data: { value: 'no' }, + } ); + await frontendUtils.goToShop(); + await frontendUtils.addToCart( DISCOUNTED_PRODUCT_NAME ); + await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME ); + await frontendUtils.goToCart(); + + let cartSidebar = page.locator( + '.wp-block-woocommerce-cart-totals-block' + ); + const taxRow = cartSidebar + .locator( '.wc-block-components-totals-taxes' ) + .getByText( 'Tax' ); + await expect( taxRow ).toBeHidden(); + + // Move to Checkout and look for Tax row. + await frontendUtils.goToCheckout(); + let checkoutSidebar = page.locator( + '.wp-block-woocommerce-checkout-totals-block' + ); + const checkoutTaxRow = checkoutSidebar + .locator( '.wc-block-components-totals-taxes' ) + .getByText( 'Tax' ); + await expect( checkoutTaxRow ).toBeHidden(); + + // Check out and look for tax on order confirmation page. + await checkoutPageObject.fillInCheckoutWithTestData(); + await checkoutPageObject.placeOrder(); + const taxOnOrderConfirmation = page.getByText( 'Tax:' ); + await expect( taxOnOrderConfirmation ).toBeHidden(); + + // Empty the cart (it should be empty already, but just in case). + await frontendUtils.emptyCart(); + + // Turn on tax display. + await requestUtils.rest( { + method: 'PUT', + path: 'wc/v3/settings/general/woocommerce_calc_taxes', + data: { value: 'yes' }, + } ); + await frontendUtils.goToShop(); + await frontendUtils.addToCart( DISCOUNTED_PRODUCT_NAME ); + await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME ); + await frontendUtils.goToCart(); + + cartSidebar = page.locator( '.wp-block-woocommerce-cart-totals-block' ); + const visibleTaxRow = cartSidebar + .locator( '.wc-block-components-totals-taxes' ) + .getByText( 'Tax' ); + await expect( visibleTaxRow ).toBeVisible(); + + // Move to Checkout and look for Tax row. + await frontendUtils.goToCheckout(); + checkoutSidebar = page.locator( + '.wp-block-woocommerce-checkout-totals-block' + ); + const visibleCheckoutTaxRow = checkoutSidebar + .locator( '.wc-block-components-totals-taxes' ) + .getByText( 'Tax' ); + await expect( visibleCheckoutTaxRow ).toBeVisible(); + + // Check out and look for tax on order confirmation page. + await checkoutPageObject.fillInCheckoutWithTestData(); + await checkoutPageObject.placeOrder(); + const visibleTaxOnOrderConfirmation = page.getByText( 'Tax:' ); + await expect( visibleTaxOnOrderConfirmation ).toBeVisible(); + } ); +} ); diff --git a/tests/e2e/utils/frontend/frontend-utils.page.ts b/tests/e2e/utils/frontend/frontend-utils.page.ts index ee79927fabf..1704085dd8f 100644 --- a/tests/e2e/utils/frontend/frontend-utils.page.ts +++ b/tests/e2e/utils/frontend/frontend-utils.page.ts @@ -46,7 +46,7 @@ export class FrontendUtils { async goToCheckout() { await this.page.goto( '/checkout', { - waitUntil: 'commit', + waitUntil: 'domcontentloaded', } ); } From 6aeed332d9193f8151ef13e52382b275db14b01e Mon Sep 17 00:00:00 2001 From: Thomas Roberts <5656702+opr@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:16:25 +0000 Subject: [PATCH 02/48] Move the Checkout -> Account tests from puppeteer to Playwright (#11961) --- .../shopper/cart-checkout/account.test.js | 70 --------------- tests/e2e/playwright.config.ts | 2 + ...k.shopper.block_theme.side_effects.spec.ts | 90 +++++++++++++++++++ 3 files changed, 92 insertions(+), 70 deletions(-) delete mode 100644 tests/e2e-jest/specs/shopper/cart-checkout/account.test.js create mode 100644 tests/e2e/tests/checkout/checkout-block.shopper.block_theme.side_effects.spec.ts diff --git a/tests/e2e-jest/specs/shopper/cart-checkout/account.test.js b/tests/e2e-jest/specs/shopper/cart-checkout/account.test.js deleted file mode 100644 index 47a6cb7b6d3..00000000000 --- a/tests/e2e-jest/specs/shopper/cart-checkout/account.test.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * External dependencies - */ -import { setCheckbox } from '@woocommerce/e2e-utils'; -import { visitAdminPage } from '@wordpress/e2e-test-utils'; - -/** - * Internal dependencies - */ -import { shopper, merchant, clickLink } from '../../../../utils'; -import { SIMPLE_PHYSICAL_PRODUCT_NAME } from '.../../../../utils/constants'; - -if ( process.env.WOOCOMMERCE_BLOCKS_PHASE < 2 ) { - // Skips all the tests if it's a WooCommerce Core process environment. - // eslint-disable-next-line jest/no-focused-tests, jest/expect-expect - test.only( 'Skipping Cart & Checkout tests', () => {} ); -} - -describe( 'Shopper → Checkout → Account', () => { - beforeAll( async () => { - await merchant.login(); - await merchant.openSettings( 'account' ); - //Enable create an account at checkout option. - await setCheckbox( '#woocommerce_enable_checkout_login_reminder' ); - //Enable login at checkout option. - await setCheckbox( - '#woocommerce_enable_signup_and_login_from_checkout' - ); - //Enable guest checkout option. - await setCheckbox( '#woocommerce_enable_guest_checkout' ); - await clickLink( 'button[name="save"]' ); - await merchant.logout(); - } ); - - beforeEach( async () => { - await shopper.block.emptyCart(); - await shopper.block.goToShop(); - await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); - await shopper.block.goToCheckout(); - } ); - - it( 'user can login to existing account', async () => { - //Get the login link from checkout page. - const loginLink = await page.$eval( - 'span.wc-block-components-checkout-step__heading-content a', - ( el ) => el.href - ); - //Confirm login link is correct. - await expect( loginLink ).toContain( - `${ process.env.WORDPRESS_BASE_URL }/my-account/?redirect_to` - ); - await expect( loginLink ).toContain( `checkout` ); - } ); - - it.skip( 'user can can create an account', async () => { - await page.waitForSelector( '.wc-block-checkout__create-account' ); - await expect( page ).toClick( 'span', { - text: 'Create an account?', - } ); - //Create random email to place an order. - const testEmail = `test${ Math.random() * 10 }@example.com`; - await shopper.block.fillInCheckoutWithTestData( { email: testEmail } ); - await shopper.block.placeOrder(); - await expect( page ).toMatch( 'Order received' ); - await merchant.login(); - await visitAdminPage( 'users.php' ); - //Confirm account is being created with the email. - await expect( page ).toMatch( testEmail ); - } ); -} ); diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts index 27a511bd070..efcf3eebc0c 100644 --- a/tests/e2e/playwright.config.ts +++ b/tests/e2e/playwright.config.ts @@ -7,6 +7,8 @@ import path from 'path'; import { fileURLToPath } from 'url'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +require( 'dotenv' ).config(); interface ExtendedPlaywrightTestConfig extends PlaywrightTestConfig { use: { stateDir?: string; diff --git a/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.side_effects.spec.ts b/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.side_effects.spec.ts new file mode 100644 index 00000000000..8e5409b674e --- /dev/null +++ b/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.side_effects.spec.ts @@ -0,0 +1,90 @@ +/** + * External dependencies + */ +import { expect, test as base } from '@woocommerce/e2e-playwright-utils'; + +/** + * Internal dependencies + */ +import { REGULAR_PRICED_PRODUCT_NAME } from './constants'; +import { CheckoutPage } from './checkout.page'; + +const test = base.extend< { checkoutPageObject: CheckoutPage } >( { + checkoutPageObject: async ( { page }, use ) => { + const pageObject = new CheckoutPage( { + page, + } ); + await use( pageObject ); + }, +} ); + +test.describe( 'Shopper → Account', () => { + // Become a logged out user. + test.use( { + storageState: { + origins: [], + cookies: [], + }, + } ); + + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.rest( { + method: 'PUT', + path: 'wc/v3/settings/account/woocommerce_enable_guest_checkout', + data: { value: 'yes' }, + } ); + await requestUtils.rest( { + method: 'PUT', + path: 'wc/v3/settings/account/woocommerce_enable_checkout_login_reminder', + data: { value: 'yes' }, + } ); + } ); + test.beforeEach( async ( { frontendUtils } ) => { + await frontendUtils.emptyCart(); + await frontendUtils.goToShop(); + await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME ); + await frontendUtils.goToCheckout(); + } ); + + test( 'Shopper can log in to an existing account and can create an account', async ( { + requestUtils, + checkoutPageObject, + page, + } ) => { + //Get the login link from checkout page. + const loginLink = page.getByText( 'Log in.' ); + const loginLinkHref = await loginLink.getAttribute( 'href' ); + + //Confirm login link is correct. + expect( loginLinkHref ).toContain( + `${ process.env.WORDPRESS_BASE_URL }/my-account/?redirect_to` + ); + expect( loginLinkHref ).toContain( `checkout` ); + + await requestUtils.rest( { + method: 'PUT', + path: 'wc/v3/settings/account/woocommerce_enable_signup_and_login_from_checkout', + data: { value: 'yes' }, + } ); + + await page.reload(); + + const createAccount = page.getByLabel( 'Create an account?' ); + await createAccount.check(); + const testEmail = `test${ Math.random() * 10 }@example.com`; + await checkoutPageObject.fillInCheckoutWithTestData( { + email: testEmail, + } ); + await checkoutPageObject.placeOrder(); + + // Get users from API with same email used when purchasing. + await requestUtils + .rest( { + method: 'GET', + path: `wc/v3/customers?email=${ testEmail }`, + } ) + .then( ( response ) => { + expect( response[ 0 ].email ).toBe( testEmail ); + } ); + } ); +} ); From 998dc2f031bd0fe360538571ad5a5cc5bb98c3bf Mon Sep 17 00:00:00 2001 From: Sam Seay Date: Wed, 29 Nov 2023 12:04:35 +0800 Subject: [PATCH 03/48] Update interactivity price filter block to use latest Interactivity Store API (#11943) --- .../inner-blocks/price-filter/frontend.ts | 98 +++++++++++-------- .../inner-blocks/price-filter/types.ts | 20 +--- src/BlockTypes/CollectionPriceFilter.php | 70 ++++++------- 3 files changed, 97 insertions(+), 91 deletions(-) diff --git a/assets/js/blocks/collection-filters/inner-blocks/price-filter/frontend.ts b/assets/js/blocks/collection-filters/inner-blocks/price-filter/frontend.ts index 6c79a2a596b..e8d52e9a3f9 100644 --- a/assets/js/blocks/collection-filters/inner-blocks/price-filter/frontend.ts +++ b/assets/js/blocks/collection-filters/inner-blocks/price-filter/frontend.ts @@ -3,14 +3,15 @@ */ import { store, navigate } from '@woocommerce/interactivity'; import { formatPrice, getCurrency } from '@woocommerce/price-format'; +import { HTMLElementEvent } from '@woocommerce/types'; /** * Internal dependencies */ -import { ActionProps, StateProps } from './types'; +import { PriceFilterState } from './types'; -const getHrefWithFilters = ( { state }: StateProps ) => { - const { minPrice, maxPrice } = state.filters; +const getHrefWithFilters = ( state: PriceFilterState ) => { + const { minPrice = 0, maxPrice = 0, maxRange = 0 } = state; const url = new URL( window.location.href ); const { searchParams } = url; @@ -20,7 +21,7 @@ const getHrefWithFilters = ( { state }: StateProps ) => { searchParams.delete( 'min_price' ); } - if ( maxPrice < state.filters.maxRange ) { + if ( maxPrice < maxRange ) { searchParams.set( 'max_price', maxPrice.toString() ); } else { searchParams.delete( 'max_price' ); @@ -33,12 +34,27 @@ const getHrefWithFilters = ( { state }: StateProps ) => { return url.href; }; -store( { - state: { - filters: { - rangeStyle: ( { state }: StateProps ) => { - const { minPrice, maxPrice, minRange, maxRange } = - state.filters; +interface PriceFilterStore { + state: PriceFilterState; + actions: { + setMinPrice: ( event: HTMLElementEvent< HTMLInputElement > ) => void; + setMaxPrice: ( event: HTMLElementEvent< HTMLInputElement > ) => void; + updateProducts: () => void; + reset: () => void; + }; +} + +const { state } = store< PriceFilterStore >( + 'woocommerce/collection-price-filter', + { + state: { + get rangeStyle(): string { + const { + minPrice = 0, + maxPrice = 0, + minRange = 0, + maxRange = 0, + } = state; return [ `--low: ${ ( 100 * ( minPrice - minRange ) ) / @@ -50,48 +66,48 @@ store( { }%`, ].join( ';' ); }, - formattedMinPrice: ( { state }: StateProps ) => { - const { minPrice } = state.filters; + get formattedMinPrice(): string { + const { minPrice = 0 } = state; return formatPrice( minPrice, getCurrency( { minorUnit: 0 } ) ); }, - formattedMaxPrice: ( { state }: StateProps ) => { - const { maxPrice } = state.filters; + get formattedMaxPrice(): string { + const { maxPrice = 0 } = state; return formatPrice( maxPrice, getCurrency( { minorUnit: 0 } ) ); }, }, - }, - actions: { - filters: { - setMinPrice: ( { state, event }: ActionProps ) => { + actions: { + setMinPrice: ( event: HTMLElementEvent< HTMLInputElement > ) => { + const { minRange = 0, maxPrice = 0, maxRange = 0 } = state; const value = parseFloat( event.target.value ); - state.filters.minPrice = Math.min( - Number.isNaN( value ) ? state.filters.minRange : value, - state.filters.maxRange - 1 - ); - state.filters.maxPrice = Math.max( - state.filters.maxPrice, - state.filters.minPrice + 1 + state.minPrice = Math.min( + Number.isNaN( value ) ? minRange : value, + maxRange - 1 ); + state.maxPrice = Math.max( maxPrice, state.minPrice + 1 ); }, - setMaxPrice: ( { state, event }: ActionProps ) => { + setMaxPrice: ( event: HTMLElementEvent< HTMLInputElement > ) => { + const { + minRange = 0, + minPrice = 0, + maxPrice = 0, + maxRange = 0, + } = state; const value = parseFloat( event.target.value ); - state.filters.maxPrice = Math.max( - Number.isNaN( value ) ? state.filters.maxRange : value, - state.filters.minRange + 1 - ); - state.filters.minPrice = Math.min( - state.filters.minPrice, - state.filters.maxPrice - 1 + state.maxPrice = Math.max( + Number.isNaN( value ) ? maxRange : value, + minRange + 1 ); + state.minPrice = Math.min( minPrice, maxPrice - 1 ); }, - updateProductsWithPriceFilter: ( { state }: ActionProps ) => { - navigate( getHrefWithFilters( { state } ) ); + updateProducts: () => { + navigate( getHrefWithFilters( state ) ); }, - reset: ( { state }: ActionProps ) => { - state.filters.minPrice = 0; - state.filters.maxPrice = state.filters.maxRange; - navigate( getHrefWithFilters( { state } ) ); + reset: () => { + const { maxRange = 0 } = state; + state.minPrice = 0; + state.maxPrice = maxRange; + navigate( getHrefWithFilters( state ) ); }, }, - }, -} ); + } +); diff --git a/assets/js/blocks/collection-filters/inner-blocks/price-filter/types.ts b/assets/js/blocks/collection-filters/inner-blocks/price-filter/types.ts index 7ccd5023f15..488bf60a554 100644 --- a/assets/js/blocks/collection-filters/inner-blocks/price-filter/types.ts +++ b/assets/js/blocks/collection-filters/inner-blocks/price-filter/types.ts @@ -2,7 +2,6 @@ * External dependencies */ import { BlockEditProps } from '@wordpress/blocks'; -import { HTMLElementEvent } from '@woocommerce/types'; export type BlockAttributes = { showInputFields: boolean; @@ -16,24 +15,15 @@ export interface EditProps extends BlockEditProps< BlockAttributes > { } export type PriceFilterState = { - minPrice: number; - maxPrice: number; - minRange: number; - maxRange: number; + minPrice?: number; + maxPrice?: number; + minRange?: number; + maxRange?: number; + rangeStyle: string; formattedMinPrice: string; formattedMaxPrice: string; }; -export type StateProps = { - state: { - filters: PriceFilterState; - }; -}; - -export interface ActionProps extends StateProps { - event: HTMLElementEvent< HTMLInputElement >; -} - export type FilterComponentProps = BlockEditProps< BlockAttributes > & { collectionData: Partial< PriceFilterState >; }; diff --git a/src/BlockTypes/CollectionPriceFilter.php b/src/BlockTypes/CollectionPriceFilter.php index 458f03d0a22..a983051bb82 100644 --- a/src/BlockTypes/CollectionPriceFilter.php +++ b/src/BlockTypes/CollectionPriceFilter.php @@ -44,20 +44,15 @@ protected function render( $attributes, $content, $block ) { $formatted_max_price = wc_price( $max_price, array( 'decimals' => 0 ) ); $data = array( - 'minPrice' => $min_price, - 'maxPrice' => $max_price, - 'minRange' => $min_range, - 'maxRange' => $max_range, - 'formattedMinPrice' => $formatted_min_price, - 'formattedMaxPrice' => $formatted_max_price, + 'minPrice' => $min_price, + 'maxPrice' => $max_price, + 'minRange' => $min_range, + 'maxRange' => $max_range, ); - wc_store( - array( - 'state' => array( - 'filters' => $data, - ), - ) + wc_initial_state( + 'woocommerce/collection-price-filter', + $data ); list ( @@ -75,9 +70,12 @@ protected function render( $attributes, $content, $block ) { $__high = 100 * ( $max_price - $min_range ) / ( $max_range - $min_range ); $range_style = "--low: $__low%; --high: $__high%"; + $data_directive = wp_json_encode( array( 'namespace' => 'woocommerce/collection-price-filter' ) ); + $wrapper_attributes = get_block_wrapper_attributes( array( - 'class' => $show_input_fields && $inline_input ? 'inline-input' : '', + 'class' => $show_input_fields && $inline_input ? 'inline-input' : '', + 'data-wc-interactive' => $data_directive, ) ); @@ -87,14 +85,15 @@ protected function render( $attributes, $content, $block ) { class="min" type="text" value="%d" - data-wc-bind--value="state.filters.minPrice" - data-wc-on--input="actions.filters.setMinPrice" - data-wc-on--change="actions.filters.updateProductsWithPriceFilter" + data-wc-bind--value="state.minPrice" + data-wc-on--input="actions.setMinPrice" + data-wc-on--change="actions.updateProducts" />', esc_attr( $min_price ) ) : sprintf( - '%s', - esc_attr( $formatted_min_price ) + '%s', + // Not escaped, as this is HTML. + $formatted_min_price ); $price_max = $show_input_fields ? @@ -103,14 +102,15 @@ class="min" class="max" type="text" value="%d" - data-wc-bind--value="state.filters.maxPrice" - data-wc-on--input="actions.filters.setMaxPrice" - data-wc-on--change="actions.filters.updateProductsWithPriceFilter" + data-wc-bind--value="state.maxPrice" + data-wc-on--input="actions.setMaxPrice" + data-wc-on--change="actions.updateProducts" />', esc_attr( $max_price ) ) : sprintf( - '%s', - esc_attr( $formatted_max_price ) + '%s', + // Not escaped, as this is HTML. + $formatted_max_price ); ob_start(); @@ -119,7 +119,7 @@ class="max"
- +
From 2529d30bd9337dfcfcb0d4b5808332a464990803 Mon Sep 17 00:00:00 2001 From: Sam Seay Date: Wed, 29 Nov 2023 13:27:38 +0800 Subject: [PATCH 04/48] Migrate attribute-filter block to use new interactivity store API (#11911) --- .../inner-blocks/attribute-filter/frontend.ts | 97 ++++++++----------- .../inner-blocks/attribute-filter/style.scss | 4 + src/BlockTypes/CollectionAttributeFilter.php | 7 +- 3 files changed, 49 insertions(+), 59 deletions(-) diff --git a/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/frontend.ts b/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/frontend.ts index e63ca28d637..3ed6331fe6b 100644 --- a/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/frontend.ts +++ b/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/frontend.ts @@ -1,10 +1,7 @@ /** * External dependencies */ -import { - store as interactivityStore, - navigate, -} from '@woocommerce/interactivity'; +import { store, navigate, getContext } from '@woocommerce/interactivity'; import { DropdownContext } from '@woocommerce/interactivity-components/dropdown'; import { HTMLElementEvent } from '@woocommerce/types'; @@ -14,15 +11,6 @@ type AttributeFilterContext = { selectType: 'single' | 'multiple'; }; -interface AttributeFilterDropdownContext - extends AttributeFilterContext, - DropdownContext {} - -type ActionProps = { - event: HTMLElementEvent< HTMLInputElement >; - context: AttributeFilterContext; -}; - function getUrl( selectedTerms: string[], slug: string, @@ -49,58 +37,55 @@ function getSelectedTermsFromUrl( slug: string ) { .filter( Boolean ); } -interactivityStore( { - state: { - filters: {}, - }, +store( 'woocommerce/collection-attribute-filter', { actions: { - filters: { - navigateWithAttributeFilter: ( { - context, - }: { - context: AttributeFilterDropdownContext; - } ) => { - if ( context.woocommerceDropdown.selectedItem.value ) { - navigate( - getUrl( - [ context.woocommerceDropdown.selectedItem.value ], - context.attributeSlug, - context.queryType - ) - ); - } - }, - updateProductsWithAttributeFilter: ( { - event, - context, - }: ActionProps ) => { - if ( ! event.target.value ) return; + navigate: () => { + const dropdownContext = getContext< DropdownContext >( + 'woocommerce/interactivity-dropdown' + ); - let selectedTerms = getSelectedTermsFromUrl( - context.attributeSlug - ); + const context = getContext< AttributeFilterContext >(); - if ( - event.target.checked && - ! selectedTerms.includes( event.target.value ) - ) { - if ( context.selectType === 'multiple' ) - selectedTerms.push( event.target.value ); - if ( context.selectType === 'single' ) - selectedTerms = [ event.target.value ]; - } else { - selectedTerms = selectedTerms.filter( - ( value ) => value !== event.target.value - ); - } + if ( dropdownContext.selectedItem.value ) { navigate( getUrl( - selectedTerms, + [ dropdownContext.selectedItem.value ], context.attributeSlug, context.queryType ) ); - }, + } + }, + updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => { + if ( ! event.target.value ) return; + + const context = getContext< AttributeFilterContext >(); + + let selectedTerms = getSelectedTermsFromUrl( + context.attributeSlug + ); + + if ( + event.target.checked && + ! selectedTerms.includes( event.target.value ) + ) { + if ( context.selectType === 'multiple' ) + selectedTerms.push( event.target.value ); + if ( context.selectType === 'single' ) + selectedTerms = [ event.target.value ]; + } else { + selectedTerms = selectedTerms.filter( + ( value ) => value !== event.target.value + ); + } + + navigate( + getUrl( + selectedTerms, + context.attributeSlug, + context.queryType + ) + ); }, }, } ); diff --git a/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/style.scss b/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/style.scss index 29879c2a2b2..488d46c50f0 100644 --- a/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/style.scss +++ b/assets/js/blocks/collection-filters/inner-blocks/attribute-filter/style.scss @@ -1,3 +1,7 @@ +// Import styles we need to render the checkbox list and checkbox control. +@import "../../../../../../packages/components/checkbox-list/style"; +@import "../../../../../../packages/components/checkbox-control/style"; + .wp-block-woocommerce-collection-attribute-filter { .attribute-dropdown { position: relative; diff --git a/src/BlockTypes/CollectionAttributeFilter.php b/src/BlockTypes/CollectionAttributeFilter.php index 2bb02614cc0..c5325a7c700 100644 --- a/src/BlockTypes/CollectionAttributeFilter.php +++ b/src/BlockTypes/CollectionAttributeFilter.php @@ -79,7 +79,8 @@ function( $term ) use ( $attribute_counts, $selected_terms ) { '
%2$s
', get_block_wrapper_attributes( array( - 'data-wc-context' => wp_json_encode( $context ), + 'data-wc-context' => wp_json_encode( $context ), + 'data-wc-interactive' => wp_json_encode( array( 'namespace' => 'woocommerce/collection-attribute-filter' ) ), ) ), $filter_content @@ -112,7 +113,7 @@ private function render_attribute_dropdown( $options, $attributes ) { return Dropdown::render( array( 'items' => $list_items, - 'action' => 'actions.filters.navigateWithAttributeFilter', + 'action' => 'woocommerce/collection-attribute-filter::actions.navigate', 'selected_item' => $selected_item, ) ); @@ -160,7 +161,7 @@ private function render_list_item_template( $option, $show_counts ) { class="wc-block-components-checkbox__input" type="checkbox" aria-invalid="false" - data-wc-on--change="actions.filters.updateProductsWithAttributeFilter" + data-wc-on--change="actions.updateProducts" data-wc-context=\'{ "attributeTermSlug": "%5$s" }\' value="%5$s" %4$s From 313e57c5ca18781806703ccbf5c604c3e699390a Mon Sep 17 00:00:00 2001 From: Sam Seay Date: Wed, 29 Nov 2023 13:27:59 +0800 Subject: [PATCH 05/48] Adjust the store config for Dropdown to keep selectors in state portion. (#11968) --- packages/interactivity-components/dropdown/index.ts | 7 +++---- src/InteractivityComponents/Dropdown.php | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/interactivity-components/dropdown/index.ts b/packages/interactivity-components/dropdown/index.ts index 1285048ac2b..0aae0bedff6 100644 --- a/packages/interactivity-components/dropdown/index.ts +++ b/packages/interactivity-components/dropdown/index.ts @@ -20,15 +20,14 @@ export type DropdownContext = { }; store( 'woocommerce/interactivity-dropdown', { - state: {}, - selectors: { - placeholderText: () => { + state: { + get placeholderText() { const context = getContext< DropdownContext >(); const { selectedItem } = context; return selectedItem.label || 'Select an option'; }, - isSelected: () => { + get isSelected() { const context = getContext< DropdownContext >(); const { diff --git a/src/InteractivityComponents/Dropdown.php b/src/InteractivityComponents/Dropdown.php index a6893c76272..f3be2426d7e 100644 --- a/src/InteractivityComponents/Dropdown.php +++ b/src/InteractivityComponents/Dropdown.php @@ -47,7 +47,7 @@ public static function render( $props ) { tabindex="-1" data-wc-on--click="actions.toggleIsOpen" > - + + ) } + + ); +}; + +/** + * Button for purchasable products. + */ +const ButtonComponent = ( { + className, + quantityInCart, + isProcessing, + isDisabled, + isDone, + onClick, +}: ButtonComponentProps ) => { + return ( + + ); +}; + +/** + * Add to Cart Form Button Component. + */ +const AddToCartButton = () => { + // @todo Add types for `useAddToCartFormContext` + const { + showFormElements, + productIsPurchasable, + productHasOptions, + product, + productType, + isDisabled, + isProcessing, + eventRegistration, + hasError, + dispatchActions, + } = useAddToCartFormContext(); + const { parentName } = useInnerBlockLayoutContext(); + const { dispatchStoreEvent } = useStoreEvents(); + const { cartQuantity } = useStoreAddToCart( product.id || 0 ); + const [ addedToCart, setAddedToCart ] = useState( false ); + const addToCartButtonData = product.add_to_cart || { + url: '', + text: '', + }; + + // Subscribe to emitter for after processing. + useEffect( () => { + const onSuccess = () => { + if ( ! hasError ) { + setAddedToCart( true ); + } + return true; + }; + const unsubscribeProcessing = + eventRegistration.onAddToCartAfterProcessingWithSuccess( + onSuccess, + 0 + ); + return () => { + unsubscribeProcessing(); + }; + }, [ eventRegistration, hasError ] ); + + /** + * We can show a real button if we are: + * + * a) Showing a full add to cart form. + * b) The product doesn't have options and can therefore be added directly to the cart. + * c) The product is purchasable. + * + * Otherwise we show a link instead. + */ + const showButton = + ( showFormElements || + ( ! productHasOptions && productType === 'simple' ) ) && + productIsPurchasable; + + return showButton ? ( + { + dispatchActions.submitForm( + `woocommerce/single-product/${ product?.id || 0 }` + ); + dispatchStoreEvent( 'cart-add-item', { + product, + listName: parentName, + } ); + } } + /> + ) : ( + { + dispatchStoreEvent( 'product-view-link', { + product, + listName: parentName, + } ); + } } + /> + ); +}; + +export default AddToCartButton; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.ts b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.ts new file mode 100644 index 00000000000..9fe6eed8226 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.ts @@ -0,0 +1,3 @@ +export { default as AddToCartButton } from './add-to-cart-button'; +export { default as QuantityInput } from './quantity-input'; +export { default as ProductUnavailable } from './product-unavailable'; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.tsx b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.tsx new file mode 100644 index 00000000000..77644f5bcf6 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.tsx @@ -0,0 +1,19 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; + +const ProductUnavailable = ( { + reason = __( + 'Sorry, this product cannot be purchased.', + 'woo-gutenberg-products-block' + ), +} ) => { + return ( +
+ { reason } +
+ ); +}; + +export default ProductUnavailable; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.tsx b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.tsx new file mode 100644 index 00000000000..a9c1e257ec4 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.tsx @@ -0,0 +1,85 @@ +/** + * External dependencies + */ +import { useDebouncedCallback } from 'use-debounce'; + +type JSXInputProps = JSX.IntrinsicElements[ 'input' ]; + +interface QuantityInputProps extends Omit< JSXInputProps, 'onChange' > { + max: number; + min: number; + onChange: ( val: number | string ) => void; + step: number; +} + +/** + * Quantity Input Component. + */ +const QuantityInput = ( { + disabled, + min, + max, + step = 1, + value, + onChange, +}: QuantityInputProps ) => { + const hasMaximum = typeof max !== 'undefined'; + + /** + * The goal of this function is to normalize what was inserted, + * but after the customer has stopped typing. + * + * It's important to wait before normalizing or we end up with + * a frustrating experience, for example, if the minimum is 2 and + * the customer is trying to type "10", premature normalizing would + * always kick in at "1" and turn that into 2. + * + * Copied from + */ + const normalizeQuantity = useDebouncedCallback< ( val: number ) => void >( + ( initialValue ) => { + // We copy the starting value. + let newValue = initialValue; + + // We check if we have a maximum value, and select the lowest between what was inserted and the maximum. + if ( hasMaximum ) { + newValue = Math.min( + newValue, + // the maximum possible value in step increments. + Math.floor( max / step ) * step + ); + } + + // Select the biggest between what's inserted, the the minimum value in steps. + newValue = Math.max( newValue, Math.ceil( min / step ) * step ); + + // We round off the value to our steps. + newValue = Math.floor( newValue / step ) * step; + + // Only commit if the value has changed + if ( newValue !== initialValue ) { + onChange?.( newValue ); + } + }, + 300 + ); + + return ( + { + onChange?.( e.target.value ); + normalizeQuantity( Number( e.target.value ) ); + } } + /> + ); +}; + +export default QuantityInput; diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/style.scss b/assets/js/atomic/blocks/product-elements/add-to-cart/style.scss new file mode 100644 index 00000000000..686d216aff6 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/add-to-cart/style.scss @@ -0,0 +1,49 @@ +.wc-block-components-product-add-to-cart { + margin: 0; + display: flex; + flex-wrap: wrap; + + .wc-block-components-product-add-to-cart-button { + margin: 0 0 em($gap-small) 0; + + .wc-block-components-button__text { + display: block; + + > svg { + fill: currentColor; + vertical-align: top; + width: 1.5em; + height: 1.5em; + margin: -0.25em 0 -0.25em 0.5em; + } + } + } + + .wc-block-components-product-add-to-cart-quantity { + margin: 0 1em em($gap-small) 0; + flex-basis: 5em; + padding: 0.618em; + background: $white; + border: 1px solid #ccc; + border-radius: $universal-border-radius; + color: #43454b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.125); + text-align: center; + } +} + +.is-loading .wc-block-components-product-add-to-cart, +.wc-block-components-product-add-to-cart--placeholder { + .wc-block-components-product-add-to-cart-quantity, + .wc-block-components-product-add-to-cart-button { + @include placeholder(); + } +} + +.wc-block-grid .wc-block-components-product-add-to-cart { + justify-content: center; +} + +.wc-block-components-product-add-to-cart-notice { + margin: 0; +} diff --git a/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/editor.scss b/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/editor.scss index bbd36223c67..77083516b47 100644 --- a/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/editor.scss +++ b/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/editor.scss @@ -21,5 +21,18 @@ display: block; } } + + .wc-block-components-product-add-to-cart-button:not(.is-link) { + background-color: #eee; + color: #333; + margin-top: 1em; + + &:focus, + &:hover { + background-color: #d5d5d5; + border-color: #d5d5d5; + color: #333; + } + } } } diff --git a/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/style.scss b/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/style.scss index 52f626c5104..4bf8332ddda 100644 --- a/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/style.scss +++ b/assets/js/blocks/cart/inner-blocks/cart-cross-sells-products/style.scss @@ -38,6 +38,24 @@ .wc-block-components-product-button__button { margin-top: 1em; } + + .wc-block-components-product-add-to-cart { + justify-content: center; + + .wc-block-components-product-add-to-cart-button:not(.is-link) { + background-color: #eee; + color: #333; + font-weight: 600; + margin-top: 1em; + + &:focus, + &:hover { + background-color: #d5d5d5; + border-color: #d5d5d5; + color: #333; + } + } + } } } } diff --git a/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md b/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md index 123212be351..ee2a32211a1 100644 --- a/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md +++ b/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md @@ -39,6 +39,7 @@ The majority of our feature flagging is blocks, this is a list of them: - Product Gallery Next/Previous Buttons ([PHP flag](https://github.com/woocommerce/woocommerce-blocks/blob/7f0d55d54885f436778f04a6389e92b8785d5c68/src/BlockTypesController.php#L236) | [webpack flag](https://github.com/woocommerce/woocommerce-blocks/blob/7f0d55d54885f436778f04a6389e92b8785d5c68/bin/webpack-entries.js#L60-L63)). - Product Gallery Pager ([PHP flag](https://github.com/woocommerce/woocommerce-blocks/blob/7f0d55d54885f436778f04a6389e92b8785d5c68/src/BlockTypesController.php#L237) | [webpack flag](https://github.com/woocommerce/woocommerce-blocks/blob/7f0d55d54885f436778f04a6389e92b8785d5c68/bin/webpack-entries.js#L64-L67)). - Product Gallery Thumbnails ([PHP flag](https://github.com/woocommerce/woocommerce-blocks/blob/7f0d55d54885f436778f04a6389e92b8785d5c68/src/BlockTypesController.php#L238) | [webpack flag](https://github.com/woocommerce/woocommerce-blocks/blob/7f0d55d54885f436778f04a6389e92b8785d5c68/bin/webpack-entries.js#L68-L71)). +- ⚛️ Add to cart ([JS flag](https://github.com/woocommerce/woocommerce-blocks/blob/dfd2902bd8a247b5d048577db6753c5e901fc60f/assets/js/atomic/blocks/product-elements/add-to-cart/index.ts#L26-L29)). ## Features behind flags diff --git a/docs/internal-developers/translations/translation-loading.md b/docs/internal-developers/translations/translation-loading.md index f0ae7edf74b..0abe8fb3905 100644 --- a/docs/internal-developers/translations/translation-loading.md +++ b/docs/internal-developers/translations/translation-loading.md @@ -31,6 +31,11 @@ The POT file is human-readable and named `woo-gutenberg-products-block.pot`. It "X-Poedit-SearchPathExcluded-1: vendor\n" "X-Poedit-SearchPathExcluded-2: node_modules\n" +#: assets/js/atomic/blocks/product-elements/add-to-cart/constants.js:8 +msgid "Add to Cart" +msgstr "" + +#: assets/js/atomic/blocks/product-elements/add-to-cart/edit.js:39 #: assets/js/blocks/handpicked-products/block.js:42 #: assets/js/blocks/product-best-sellers/block.js:34 #: assets/js/blocks/product-category/block.js:157 diff --git a/src/BlockTypes/ProductAddToCart.php b/src/BlockTypes/ProductAddToCart.php new file mode 100644 index 00000000000..4be0031c989 --- /dev/null +++ b/src/BlockTypes/ProductAddToCart.php @@ -0,0 +1,32 @@ +register_chunk_translations( [ $this->block_name ] ); + } +} diff --git a/src/BlockTypesController.php b/src/BlockTypesController.php index f4a63ceb4fe..7dfeb72e2a3 100644 --- a/src/BlockTypesController.php +++ b/src/BlockTypesController.php @@ -233,6 +233,7 @@ protected function get_block_types() { 'MiniCart', 'StoreNotices', 'PriceFilter', + 'ProductAddToCart', 'ProductBestSellers', 'ProductButton', 'ProductCategories', From 14367aa6c7d1dc68d9b3cb1e27c95e6d5aa09ead Mon Sep 17 00:00:00 2001 From: Paulo Arromba <17236129+wavvves@users.noreply.github.com> Date: Mon, 4 Dec 2023 17:24:58 +0000 Subject: [PATCH 32/48] Add/11.6.2 testing instructions and changelog (#12040) * Updated readme.txt, versions, added testing steps * Updated testing zip link --- docs/internal-developers/testing/releases/1162.md | 13 +++++++++++++ docs/internal-developers/testing/releases/README.md | 1 + readme.txt | 8 +++++++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 docs/internal-developers/testing/releases/1162.md diff --git a/docs/internal-developers/testing/releases/1162.md b/docs/internal-developers/testing/releases/1162.md new file mode 100644 index 00000000000..5e9c7d36c4c --- /dev/null +++ b/docs/internal-developers/testing/releases/1162.md @@ -0,0 +1,13 @@ +# Testing notes and ZIP for release 11.6.2 + +Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github.com/woocommerce/woocommerce-blocks/files/13545737/woocommerce-gutenberg-products-block.zip) + +## Testing Notes + +### Update the "Give us your feedback" link to point to the WooCommerce repo discussions. [#11999](https://github.com/woocommerce/woocommerce-blocks/pull/11999) [#12006](https://github.com/woocommerce/woocommerce-blocks/pull/12006) + +1. Create pages and add the Cart Block and Checkout block +2. Select each of those blocks +3. Verify that the Give us your feedback link (see printscreen) now directs to [https://github.com/woocommerce/woocommerce/discussions/new?category=checkout-flow&labels=type%3A+product%20feedback](https://github.com/woocommerce/woocommerce/discussions/new?category=checkout-flow&labels=type%3A+product%20feedback). + +Feedback diff --git a/docs/internal-developers/testing/releases/README.md b/docs/internal-developers/testing/releases/README.md index ae6ba05abeb..de0fc438b26 100644 --- a/docs/internal-developers/testing/releases/README.md +++ b/docs/internal-developers/testing/releases/README.md @@ -197,3 +197,4 @@ Every release includes specific testing instructions for new features and bug fi - [11.5.4](./1154.md) - [11.6.0](./1160.md) - [11.6.1](./1161.md) + - [11.6.2](./1162.md) diff --git a/readme.txt b/readme.txt index f064c49175a..57f9971ed2c 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: gutenberg, woocommerce, woo commerce, products, blocks, woocommerce blocks Requires at least: 6.4 Tested up to: 6.4 Requires PHP: 7.4 -Stable tag: 11.6.1 +Stable tag: 11.6.2 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 == += 11.6.2 - 2023-12-04 = + +#### Enhancements + +- Update the "Give us your feedback" link to point to the WooCommerce repo discussions. [#11999](https://github.com/woocommerce/woocommerce-blocks/pull/11999) [#12006](https://github.com/woocommerce/woocommerce-blocks/pull/12006) + = 11.6.1 - 2023-11-23 = #### Bug Fixes From e1f4cb36e0f8050e452cdab4303a860a269496d9 Mon Sep 17 00:00:00 2001 From: Sam Seay Date: Tue, 5 Dec 2023 13:17:04 +0800 Subject: [PATCH 33/48] Upgrade TypeScript to 5.3.2 (#11926) * Also add skipLibCheck option to TS Config. --- package-lock.json | 59 +++++++--------------------------------------- package.json | 2 +- tsconfig.base.json | 1 + 3 files changed, 10 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca062ef9d75..42d38616823 100644 --- a/package-lock.json +++ b/package-lock.json @@ -194,7 +194,7 @@ "storybook": "^7.5.2", "storybook-addon-react-docgen": "1.2.44", "terser-webpack-plugin": "5.3.6", - "typescript": "4.7.4", + "typescript": "5.3.2", "utility-types": "3.10.0", "webpack": "5.88.2", "webpack-bundle-analyzer": "4.7.0", @@ -38167,21 +38167,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-package-json-lint/node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/npm-package-json-lint/node_modules/yallist": { "version": "4.0.0", "dev": true, @@ -39632,21 +39617,6 @@ "node": ">=10" } }, - "node_modules/postcss-loader/node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/postcss-loader/node_modules/yallist": { "version": "4.0.0", "dev": true, @@ -45328,15 +45298,16 @@ } }, "node_modules/typescript": { - "version": "4.7.4", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uc.micro": { @@ -74430,14 +74401,6 @@ "dev": true, "peer": true }, - "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "optional": true, - "peer": true - }, "yallist": { "version": "4.0.0", "dev": true, @@ -75373,14 +75336,6 @@ "lru-cache": "^6.0.0" } }, - "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "optional": true, - "peer": true - }, "yallist": { "version": "4.0.0", "dev": true @@ -79181,7 +79136,9 @@ } }, "typescript": { - "version": "4.7.4", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true }, "uc.micro": { diff --git a/package.json b/package.json index f5cce225c59..2a413aa3425 100644 --- a/package.json +++ b/package.json @@ -249,7 +249,7 @@ "storybook": "^7.5.2", "storybook-addon-react-docgen": "1.2.44", "terser-webpack-plugin": "5.3.6", - "typescript": "4.7.4", + "typescript": "5.3.2", "utility-types": "3.10.0", "webpack": "5.88.2", "webpack-bundle-analyzer": "4.7.0", diff --git a/tsconfig.base.json b/tsconfig.base.json index 68f757b563b..28b91a9b6fa 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -15,6 +15,7 @@ "moduleResolution": "node", "lib": [ "dom", "esnext" ], "checkJs": true, + "skipLibCheck": true, "strict": true, "strictNullChecks": true, "allowSyntheticDefaultImports": true, From b9ea5a76abbe462239634d0a99e21d8f3327e5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 5 Dec 2023 10:57:17 +0100 Subject: [PATCH 34/48] Add 11.7.0 testing steps and changelog --- .../testing/releases/1170.md | 499 ++++++++++++++++++ .../testing/releases/README.md | 1 + readme.txt | 42 ++ 3 files changed, 542 insertions(+) create mode 100644 docs/internal-developers/testing/releases/1170.md diff --git a/docs/internal-developers/testing/releases/1170.md b/docs/internal-developers/testing/releases/1170.md new file mode 100644 index 00000000000..cbc05f87a22 --- /dev/null +++ b/docs/internal-developers/testing/releases/1170.md @@ -0,0 +1,499 @@ +# Testing notes and ZIP for release 11.7.0 + +Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github.com/woocommerce/woocommerce-blocks/files/13549019/woocommerce-gutenberg-products-block.zip) + +## Testing Notes + +### [Store Customization] Update the "Footer with 3 Menus" pattern to remove the last 2 menus [#11980](https://github.com/woocommerce/woocommerce-blocks/pull/11980) + +1. Create a new page or post. +2. Insert the "Footer with Menus". +3. Check it only has one menu. + +| Before | After | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Screenshot 2023-11-29 at 11 23 27 | Screenshot 2023-11-29 at 11 23 21 | + +### [Store Customization] Update the default content in patterns [#11997](https://github.com/woocommerce/woocommerce-blocks/pull/11997) + +1. Insert [all the patterns](https://i.imgflip.com/87rb8e.jpg) from the next list: + +- Intro patterns + - Featured category cover image + - Hero product split + - Just arrived full hero +- Featured selling patterns: + - Product Collection 4 Columns + - Product Collection 5 Columns + - Product Collection 3 Columns + - Product Collection: Featured Products 5 Columns + - Featured category triple + - Large image product gallery + - Product gallery + - Hero product 3-split + - Hero product chessboard + - Featured category focus + - Banner +- About patterns + - Alternating image and text +- Review patterns + - Testimonials 3 Columns + - Testimonials single +- Social media patterns + - Social: Follow us on social media + +2. Check the copy is the same expected in the designs (see screenshots here: [#11875](https://github.com/woocommerce/woocommerce-blocks/issues/11875)). + +### Update patterns design [#12005](https://github.com/woocommerce/woocommerce-blocks/pull/12005) + +1. Create a new page or post. +2. Insert the "Just Arrived Full Hero" and check it matches the new design below FlPl7fHaGv9mpu8Nfl0joH-fi-158_32406 (the button colors may differ between themes because they are no hard-coded). +Screenshot 2023-12-04 at 14 53 27 + +3. Insert the "Product Collection 3 Columns" and "Product Gallery" and check they don't have titles anymore. +4. Insert the "Hero Product Chessboard" and check the bottom-left square has the following copy: + +```TXT +Quality Materials +We use only the highest-quality materials in our products, ensuring that they look great and last for years to come. + +Unique Design +From bold prints and colors to intricate details and textures, our products are a perfect combination of style and function. +``` + +### Hook `woocommerce_single_product_summary` action to `core/post-excerpt` block [#11953](https://github.com/woocommerce/woocommerce-blocks/pull/11953) + +1. Ensure that you are using a block theme. +2. Install [Code Snippets](https://wordpress.org/plugins/code-snippets/), that will allow us to insert some PHP code into our store. +3. Go to Snippets > Add New. Set the code snippet title to `Product summary` and add this PHP code: + +```PHP +add_action('woocommerce_single_product_summary', function() { + echo 'woocommerce_single_product_summary'; +}); +``` + +4. In the frontend, go to a Single product page and ensure that the string `woocommerce_single_product_summary` is visible before the Product Summary. + +| Before | After | +|-----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| ![image](https://github.com/woocommerce/woocommerce-blocks/assets/4463174/8489872a-7f4b-4bcd-8345-144672a793e3) | ![image](https://github.com/woocommerce/woocommerce-blocks/assets/4463174/328adbb7-19af-4b96-ab1e-395e40414738) | + +### Remove isExperimental flag from product-query module which is not experimental [#10531](https://github.com/woocommerce/woocommerce-blocks/pull/10531) + +1. Go to Editor +2. Insert Products (Beta) block in some template +3. Smoke test in Editor +4. Save and go to frontend - verify it works normal +5. Repeat the above in new post + +### [Product Collection] Fix: HTML entity decoding for product names in Hand-Picked Products [#11927](https://github.com/woocommerce/woocommerce-blocks/pull/11927) + +1. Add a hyphen `–` to the name of any product, like **`Hoodie - Black`**. +2. Insert a Product Collection block into a new post. +3. Apply the “Hand-picked Products” filter in Inspector Controls. +4. Search for the product with the hyphen and confirm it displays correctly, as shown below: + +![image](https://github.com/woocommerce/woocommerce-blocks/assets/16707866/ae7c96e0-99fa-495b-8b83-1d0d0fbf3fde) + +5. Feel free to test other [HTML entities](https://developer.mozilla.org/en-US/docs/Glossary/Entity) too. + +**Verifying Label Update**: + +- Ensure the text reads “HAND-PICKED PRODUCTS” instead of “PICK SOME PRODUCTS”, as depicted in the image below: + +![image](https://github.com/woocommerce/woocommerce-blocks/assets/16707866/66637971-647b-4fa7-a591-a211052fb61e) + +### Fix: Product Collection show products with correct stock statuses [#11708](https://github.com/woocommerce/woocommerce-blocks/pull/11708) + +1. Set some product to be out of stock. +2. Add Product Collection block to a page. +3. Set the stock status filter to all options except `outofstock`. +4. See the out of stock products **not show up** in the editor preview. +5. Save and see the same on the front end. Out of stock products don't show up. + +### Migration of Product Collection and Product Button blocks to the new `store()` API [#11558](https://github.com/woocommerce/woocommerce-blocks/pull/11558) + +1. Load a template that contains the Product Button blocks: ie, the `/shop` page. +2. Do some smoke testing and make sure there are no regressions and products can be added to the cart without issues. + +### Preserve shrinkColumns value when switching the layout type of Product Collection [#11905](https://github.com/woocommerce/woocommerce-blocks/pull/11905) + +1. Go to Editor +2. Add Product Collection block +3. Enable "Responsive" option +4. Switch view from Grid to Stack and back to Grid - try both Inspector Controls and Toolbar option (red rectangles on the screenshot). +![Screen Shot 2023-11-22 at 17 03 21 PM](https://github.com/woocommerce/woocommerce-blocks/assets/20098064/ea3a20da-f4c5-49cc-acff-de600139ca28) +5. When switching back to Grid view, "Responsive" should be still ENABLED +6. Repeat with the option DISABLED + +### Enable shrink columns option in Product Collection by default [#11821](https://github.com/woocommerce/woocommerce-blocks/pull/11821) + +#### Case 1: Default Product Collection + +1. Go to Editor +2. Add Product Collection block +3. Expected: it has "Responsive" enabled by default + +#### Case 2: Patterns + +1. Go to Editor +2. Insert patterns: + +- Product Collection Grid +- Product Collection Full Grid +- Product Collection Simple Grid +- Product Gallery + +3. Make sure each of them has "Responsive" option enabled by default +4. Insert pattern Product Collection Rows - it has `Stack` view enabled by default and then "Responsive" is not available. Change the view from `Stack` to `Grid` +5. Make sure it has "Responsive" option enabled by default + +#### Testing notes for the development team + +1. While on `trunk`, add Product Collection block +6. It has "Responsive" option disabled by default +7. Enter "Code editor" and +8. Find and remove `"shrinkColumns":false"` - this step is to mimic the case when someone added the block before the option was even introduce +9. Save the post/template +10. Switch to this branch `add/enable-shrink-column-in-product-collection-by-default` +11. Refresh the Editor +12. Change number of columns - this step is to trigger `setAttribute` function +13. Expected: make sure this block still has the "Responsive" option DISABLED + +### Reorganise Columns controls and fix undefined problem in Product Collection settings [#11937](https://github.com/woocommerce/woocommerce-blocks/pull/11937) + +1. Go to Editor +2. Add Product Collection block +3. Check Inspector Controls +4. "Columns" and "Responsive" look like in the AFTER image: + +| Before | After | +|--------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| image | image | + +6. Click three dots next to "Settings" +7. There's no `undefined` entry, but "Columns" and "Responsive" like in the AFTER image: + +| Before | After | +|--------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| image | image | + +### [Product Collection] Fix: HTML Entity Decoding in Taxonomy Controls [#11982](https://github.com/woocommerce/woocommerce-blocks/pull/11982) + +1. Create a new post & add Product Collection block to it. +2. From filters dropdown, Add “Taxonomies” filter. This will add “Product Categories” and “Product Tag” taxonomy filters to inspector control. +3. Inside “Product Categories” filter, search a category that contains an HTML Entity. + 1. If you don’t have any category with HTML entities, then you will need to either rename the existing category or create a new category with HTML entities. For example, “Electronics & Gadgets” or “Health & Wellness / Supplements” + 2. Verify that HTML entities show as expected. +4. Perform step 3 for “Product Tags” too. + +### Reviews by Product: fix reviews count not appearing in product selector [#11976](https://github.com/woocommerce/woocommerce-blocks/pull/11976) + +1. Create a post or page. +2. Add the Reviews by Product block. +3. Verify the number of reviews next to the product names: + + + +4. Using the browser Inspector (F12), select one of the checkboxes. +5. Verify the value of the `aria-label` shows the correct value (`, has reviews`). Ie: `Hoodie with Logo, has 1 review`. + + + +### Move `CheckboxControl` to components package and leave alias in checkout package [#11662](https://github.com/woocommerce/woocommerce-blocks/pull/11662) + +1. Install [WooCommerce Stripe Payment Gateway](https://wordpress.org/plugins/woocommerce-gateway-stripe/) and set it up +2. Edit the Checkout page in the Site editor, select the "Terms and Conditions block" and set the "Require checkbox" option to true. +3. Ensure that order notes are enabled on the block. Ensure the checkboxes for these blocks all appear and the block as a whole renders OK. +4. As a logged-in user, add an item to your cart and go to the Checkout block in the front-end. +5. Ensure the "Use same address for billing" checkbox is available and works as intended. +6. Scroll down to the Terms and conditions, ensure the checkbox renders and works correctly. +7. Check the "Add a note to your order" box. Ensure it works and the textarea renders. +8. Check out using Stripe and ensure the "Save payment information to my account for future purchases." renders. +9. Check this box and check out, ensure checkout is successful. +10. Add an item to your cart again and go back to the Checkout block. Ensure your card was saved. +11. In the site editor, create a page and add the "All Products" and "Filter by Attribute" blocks. +12. On "Filter by Attribute" choose an attribute with multiple terms and ensure the checkboxes show up in the editor. +13. View this page on the front-end and ensure they render there, too. + + +### Import CheckboxControl from a single place [#12015](https://github.com/woocommerce/woocommerce-blocks/pull/12015) + +1. In Checkout, click the order note label. +2. It should not open Shipping as billing checkbox. + +### Update notice for default cart and checkout [#11861](https://github.com/woocommerce/woocommerce-blocks/pull/11861) + +1. Create a test page and add the Cart block to it. +2. Open the settings sidebar. +3. Select the Cart block. +4. Verify that the notice _"If you would like to use this block as your default cart, update your page settings."_ no longer appears as a warning (yellow border and background), but as info (blue border, white background). +5. Click the link _"[update your page settings"_. +6. Verify that the notice _"Page settings updated"_ becomes visible. +7. Repeat steps 1. until 6. with the Checkout block. In step 4., the message should say _"... as your default checkout, ..."_ instead of _"... as your default cart, ..."_. + +#### Cart block + + + + + + +
Before: +

+Screenshot 2023-11-21 at 18 17 37 +
After: +

+Screenshot 2023-11-21 at 17 57 27 +
+ +#### Checkout block + + + + + + +
Before: +

+Screenshot 2023-11-21 at 18 11 48 +
After: +

+Screenshot 2023-11-21 at 18 08 52 +
+ +### Validate coupon usage against customer id AND emails [#11860](https://github.com/woocommerce/woocommerce-blocks/pull/11860) + +#### From logged in to logged out + +1. Create a new coupon and limit its usage to 1 per customer. +2. Logged in, with an email you remember, place an order using that coupon, it should pass. +3. Logged out, using the same email, try placing an order with that coupon, you should get a top level error "coupon_name" was removed from the cart. Coupon usage limit has been reached.". + +#### User email vs billing email + +1. Create a new coupon and limit its usage to 1 per customer. +2. Logged in, with an user email you remember, place an order using that coupon, and a different billing email. It should pass. +3. Logged out, using the same user email (not the billing email), try placing an order with that coupon, you should get a top level error "coupon_name" was removed from the cart. Coupon usage limit has been reached.". + +#### From logged out to logged in + +1. Create a new coupon and limit its usage to 1 per customer. +4. Logged out, use the coupon with an email you remember, that email should belong to an existing user. +5. You should be able to place the order fine. +6. Logged in with the user that own that email. +7. Go to checkout, add the coupon, using the same email, try to place the order. +8. You should get a top level error. +9. Change your billing email to something else, add the coupon again. +10. Try to place the order, you should get an error. + +#### General regression testing + +1. Create a new coupon and limit its usage to 1 per customer. +2. Logged in, with an email you remember, place an order using that coupon, it should pass. +3. Logged in again, back to checkout, change your email, and try adding the coupon, you should get an inline error that you can't use the coupon. + +### fix: store notices always shows as an error type #11768 [#11932](https://github.com/woocommerce/woocommerce-blocks/pull/11932) + +1. Go to the Cart block. +2. In the web dev console type: `wp.data.dispatch('core/notices').createSuccessNotice( 'This is a success message, it should show in green!', { context: 'wc/cart' } )` +3. See the "success" notice display in green. + +| Before | After | +|---------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| ![Screenshot 2023-11-24 at 09 15 27](https://github.com/woocommerce/woocommerce-blocks/assets/4479106/cddb89e8-36a2-4326-8953-88851f39973b) | ![Screenshot 2023-11-24 at 10 25 10](https://github.com/woocommerce/woocommerce-blocks/assets/4479106/ff9c2ee9-67d2-4cd1-8498-19793c148b72) | + +### Switch to NoticeBanner component inside Store Notices Block placeholder [#11920](https://github.com/woocommerce/woocommerce-blocks/pull/11920) + +1. Go to Appearance > Editor > Templates > Page: Cart. +2. Confirm the Store Notices Block has an informational notice appearance (blue banner). + +| Before | After | +|-------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| ![Screenshot 2023-11-23 at 12 21 56](https://github.com/woocommerce/woocommerce-blocks/assets/90977/3d68acba-08e3-4207-b29a-2debc38f5e5f) | ![Screenshot 2023-11-23 at 12 21 15](https://github.com/woocommerce/woocommerce-blocks/assets/90977/3cb98e30-22d4-44ac-8a0b-dc2abbb6d113) | + +### Improve readability of order note [#11944](https://github.com/woocommerce/woocommerce-blocks/pull/11944) + +1. Add a product to your cart and go to the checkout. +2. Add an order note and place the order. +3. Verify that the order note is visible below the order detail table. +4. Verify that the order detail table has the same border radius as the order note section. + + + + + + +
Before: +

+Screenshot 2023-11-27 at 14 37 44 + +
After: +

+Screenshot 2023-11-27 at 14 36 28
+ +### Limit number of visible incompatible extensions in sidebar notice [#11972](https://github.com/woocommerce/woocommerce-blocks/pull/11972) + +> [!NOTE] +> Things to keey an eye on while testing: +> +> - Number of visible extensions by default and within dropdown. +> - String for exact 1 vs. more than 1 incompatible extension. +> - Chevron changed direction based on opening/closing the dropdown. +> - Link color is gray and link is underlined. +> - The dropdown functionality works is cross-browser compatible. + +
+ +Test with 2 extensions + + +1. Install and activate the following helper plugins: + +- [helper-plugin-01.zip](https://github.com/woocommerce/woocommerce-blocks/files/13487656/helper-plugin-01.zip) +- [helper-plugin-02.zip](https://github.com/woocommerce/woocommerce-blocks/files/13487657/helper-plugin-02.zip) + +2. Create a test page and add the Cart block. +3. Select the Cart block and open the sidebar. +4. Verify that the two incompatible extensions are visible. +5. Verify that no dropdown, to more extensions, is visible. +6. Repeat steps 2. until 5. using the Checkout block. + +Screenshot 2023-11-28 at 19 13 46 + +
+ +
+ +Test with 3 extensions + + +1. Install and activate the following helper plugins: + +- [helper-plugin-01.zip](https://github.com/woocommerce/woocommerce-blocks/files/13487757/helper-plugin-01.zip) +- [helper-plugin-02.zip](https://github.com/woocommerce/woocommerce-blocks/files/13487758/helper-plugin-02.zip) +- [helper-plugin-03.zip](https://github.com/woocommerce/woocommerce-blocks/files/13487759/helper-plugin-03.zip) + +2. Create a test page and add the Cart block. +3. Select the Cart block and open the sidebar. +4. Verify that the two incompatible extensions are visible by default. +5. Verify that the dropdown says `1 more incompatibility`. +6. Click the dropdown link and verify that 1 incompatible extension is visible. +7. Repeat steps 2. until 6. using the Checkout block. + + + + + + +
Closed: +

+Screenshot 2023-11-28 at 21 32 30 +
Open: +

+Screenshot 2023-11-28 at 21 32 38 +
+ +
+ +
+ +Test with 10 extensions + + +1. Install and activate the following helper plugins: + +- [helper-plugin-01.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488042/helper-plugin-01.zip) +- [helper-plugin-02.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488043/helper-plugin-02.zip) +- [helper-plugin-03.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488045/helper-plugin-03.zip) +- [helper-plugin-04.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488046/helper-plugin-04.zip) +- [helper-plugin-05.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488047/helper-plugin-05.zip) +- [helper-plugin-06.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488049/helper-plugin-06.zip) +- [helper-plugin-07.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488050/helper-plugin-07.zip) +- [helper-plugin-08.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488051/helper-plugin-08.zip) +- [helper-plugin-09.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488052/helper-plugin-09.zip) +- [helper-plugin-10.zip](https://github.com/woocommerce/woocommerce-blocks/files/13488053/helper-plugin-10.zip) + +2. Create a test page and add the Cart block. +3. Select the Cart block and open the sidebar. +4. Verify that the two incompatible extensions are visible by default. +5. Verify that the dropdown says `8 more incompatibilities`. +7. Click the dropdown link and verify that 8 incompatible extension are visible. +8. Repeat steps 2. until 6. using the Checkout block. + + + + + + +
Closed: +

+Screenshot 2023-11-28 at 21 24 23 +
Open: +

+Screenshot 2023-11-28 at 21 30 26 +
+ +
+ +### Decrease modal width [#12003](https://github.com/woocommerce/woocommerce-blocks/pull/12003) + +1. Install and activate the following helper plugin: +[helper-plugin-01.zip](https://github.com/woocommerce/woocommerce-blocks/files/13522252/helper-plugin-01.zip) +2. Create a test page and add the Cart block to it. +3. Select the Cart block and open the sidebar. +4. Click the button `Switch to classic cart` in the incompatible extension notice. +5. Verify that the model has a max-width of 480px. +6. Replace the Cart block with the Checkout block. +7. Select the Checkout block and open the sidebar. +8. Click the button `Switch to classic checkout`. +9. Verify that this model also has a max-width of 480px. + + + + + + +
Before: +

+Screenshot 2023-12-01 at 14 23 02 +
After: +

+Screenshot 2023-12-01 at 14 24 20 +
+ +### Revert "Remove unused Add to Cart product element (#11948)" [#12033](https://github.com/woocommerce/woocommerce-blocks/pull/12033) + +1. Add the Mini-Cart block and the All Products block to the same post or page. +2. View the post in the frontend. +3. Open the Mini-Cart. +4. Verify there is no JS error and the Mini-Cart contents (in case you have added a product to your cart) can be seen. + +### Implement the Block Hooks API to automatically inject the Mini-Cart block [#11745](https://github.com/woocommerce/woocommerce-blocks/pull/11745) + +Currently, the application of this auto-injection _only_ applies when Twenty Twenty-Four is active. In future iterations, we can expand the list of themes this supports as we test this out and/or eventually remove the restrictions once the Block Hooks API has more support. + +General expectations for testing: + +- The mini-cart block should only appear after the navigation block in header patterns or template parts. +- It should not appear in custom patterns or template parts. +- It should only be auto-injected when the Twenty Twenty-Four theme is active. +- You should never see two mini-cart blocks in a header pattern or template part (for example WooCommerce header patterns should be untouched). +- If the mini-cart block is moved or removed and the template part/pattern/template is saved, that should persist and not change on reload. +- The auto-injection should be reflected on the frontend. + +With TT4 active + +- [ ] First test without these changes and take note of header template parts and patterns that don't have the mini-cart block (via the site editor). +- [ ] With this work enabled, verify that header template parts and patterns have the mini-cart block injected (see GIFs below for expectations). + +This is how things look _before_ this PR's changes: + +[https://github.com/woocommerce/woocommerce-blocks/assets/1429108/4d61ec4e-f1ab-478a-850f-d568993599df](https://github.com/woocommerce/woocommerce-blocks/assets/1429108/4d61ec4e-f1ab-478a-850f-d568993599df) + +And how things look after: + +[https://github.com/woocommerce/woocommerce-blocks/assets/1429108/289aaccb-0877-462f-8190-fa046aba1799](https://github.com/woocommerce/woocommerce-blocks/assets/1429108/289aaccb-0877-462f-8190-fa046aba1799) + +- [ ] Validate the general expectations and verify they are true. diff --git a/docs/internal-developers/testing/releases/README.md b/docs/internal-developers/testing/releases/README.md index de0fc438b26..1701ac7c708 100644 --- a/docs/internal-developers/testing/releases/README.md +++ b/docs/internal-developers/testing/releases/README.md @@ -198,3 +198,4 @@ Every release includes specific testing instructions for new features and bug fi - [11.6.0](./1160.md) - [11.6.1](./1161.md) - [11.6.2](./1162.md) +- [11.7.0](./1170.md) diff --git a/readme.txt b/readme.txt index 57f9971ed2c..5d3232c5fd9 100644 --- a/readme.txt +++ b/readme.txt @@ -81,6 +81,48 @@ Release and roadmap notes available on the [WooCommerce Developers Blog](https:/ == Changelog == += 11.7.0 - 2023-12-05 = + +#### Enhancements + +- The Block Hooks API is implemented to auto-inject the Mini-Cart block in header patterns and template parts when the "Twenty Twenty-Four" theme is active. The Mini-Cart block also now defaults to not show the total for the items in the cart when inserted into content. ([11745](https://github.com/woocommerce/woocommerce-blocks/pull/11745)) +- Decrease modal width. ([12003](https://github.com/woocommerce/woocommerce-blocks/pull/12003)) +- [Store Customization] Update the default content in patterns. ([11997](https://github.com/woocommerce/woocommerce-blocks/pull/11997)) +- [Store Customization] Update the "Footer with 3 Menus" pattern to remove the last 2 menus. ([11980](https://github.com/woocommerce/woocommerce-blocks/pull/11980)) +- Limit number of visible incompatible extensions in sidebar notice. ([11972](https://github.com/woocommerce/woocommerce-blocks/pull/11972)) +- Improve readability of order note. ([11944](https://github.com/woocommerce/woocommerce-blocks/pull/11944)) +- Reorganise Columns controls and fix undefined problem in Product Collection settings. ([11937](https://github.com/woocommerce/woocommerce-blocks/pull/11937)) +- Switch to NoticeBanner component inside Store Notices Block placeholder. ([11920](https://github.com/woocommerce/woocommerce-blocks/pull/11920)) +- Preserve shrinkColumns value when switching the layout type of Product Collection. ([11905](https://github.com/woocommerce/woocommerce-blocks/pull/11905)) +- Tweak the product prompt. ([11903](https://github.com/woocommerce/woocommerce-blocks/pull/11903)) +- Add DELETE `private/ai/pattern` endpoint. ([11890](https://github.com/woocommerce/woocommerce-blocks/pull/11890)) +- Update notice for default cart and checkout. ([11861](https://github.com/woocommerce/woocommerce-blocks/pull/11861)) +- Enable shrink columns option in Product Collection by default. ([11821](https://github.com/woocommerce/woocommerce-blocks/pull/11821)) +- Move `Combobox` to components package. ([11353](https://github.com/woocommerce/woocommerce-blocks/pull/11353)) +- Interactivity API: Implement the new `store()` API. ([11071](https://github.com/woocommerce/woocommerce-blocks/pull/11071)) + +#### Bug Fixes + +- [CYS] Fix regression and ensure AI-generated content is assigned to products after the third attempt. ([12016](https://github.com/woocommerce/woocommerce-blocks/pull/12016)) +- [Product Collection] Fix: HTML Entity Decoding in Taxonomy Controls. ([11982](https://github.com/woocommerce/woocommerce-blocks/pull/11982)) +- Product Gallery: Add a Product Image fallback. ([11978](https://github.com/woocommerce/woocommerce-blocks/pull/11978)) +- Reviews by Product: Fix reviews count not appearing in product selector. ([11976](https://github.com/woocommerce/woocommerce-blocks/pull/11976)) +- Hook `woocommerce_single_product_summary` action to `core/post-excerpt` block. ([11953](https://github.com/woocommerce/woocommerce-blocks/pull/11953)) +- fix: Store notices always shows as an error type #11768. ([11932](https://github.com/woocommerce/woocommerce-blocks/pull/11932)) +- [Product Collection] Fix: HTML entity decoding for product names in Hand-Picked Products. ([11927](https://github.com/woocommerce/woocommerce-blocks/pull/11927)) +- Validate coupon usage against customer id AND emails. ([11860](https://github.com/woocommerce/woocommerce-blocks/pull/11860)) +- Pass order ID to woocommerce_before_thankyou hook. ([11852](https://github.com/woocommerce/woocommerce-blocks/pull/11852)) +- Translate the prefixes passed to post-terms in product-meta. ([11811](https://github.com/woocommerce/woocommerce-blocks/pull/11811)) +- Prevent fatal errors when using Cart Tokens and creating new accounts on checkout. ([11785](https://github.com/woocommerce/woocommerce-blocks/pull/11785)) +- Product Gallery Thumbnails: Add support for cropping. ([11718](https://github.com/woocommerce/woocommerce-blocks/pull/11718)) +- Fix: Product Collection show products with correct stock statuses. ([11708](https://github.com/woocommerce/woocommerce-blocks/pull/11708)) +- Product Gallery Thumbnails: Fix overflow issues and improve responsiveness. ([11665](https://github.com/woocommerce/woocommerce-blocks/pull/11665)) + +#### Various + +- Update extensibility doc. ([11924](https://github.com/woocommerce/woocommerce-blocks/pull/11924)) +- Move `CheckboxControl` to components package and leave alias in checkout package. ([11662](https://github.com/woocommerce/woocommerce-blocks/pull/11662)) + = 11.6.2 - 2023-12-04 = #### Enhancements From 836548ad42c0d3bca3db0c21abea6b6d770cf24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 5 Dec 2023 10:58:14 +0100 Subject: [PATCH 35/48] Update versions to 11.8.0-dev --- composer.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/Package.php | 2 +- woocommerce-gutenberg-products-block.php | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 5928f32a031..c42d8f8411f 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": "11.7.0-dev", + "version": "11.8.0-dev", "keywords": [ "gutenberg", "woocommerce", diff --git a/package-lock.json b/package-lock.json index 42d38616823..7ff5aea4eb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@woocommerce/block-library", - "version": "11.7.0-dev", + "version": "11.8.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@woocommerce/block-library", - "version": "11.7.0-dev", + "version": "11.8.0-dev", "hasInstallScript": true, "license": "GPL-3.0+", "dependencies": { diff --git a/package.json b/package.json index 2a413aa3425..286d4107c3e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@woocommerce/block-library", "title": "WooCommerce Blocks", "author": "Automattic", - "version": "11.7.0-dev", + "version": "11.8.0-dev", "description": "WooCommerce blocks for the Gutenberg editor.", "homepage": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/", "keywords": [ diff --git a/src/Package.php b/src/Package.php index 9eadf123814..94a855975e8 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 = '11.7.0-dev'; + $version = '11.8.0-dev'; return new NewPackage( $version, dirname( __DIR__ ), diff --git a/woocommerce-gutenberg-products-block.php b/woocommerce-gutenberg-products-block.php index 834e35df691..ca9c03f5919 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: 11.7.0-dev + * Version: 11.8.0-dev * Author: Automattic * Author URI: https://woocommerce.com * Text Domain: woo-gutenberg-products-block From 6067a27d24e85a2bb2d1247a5b6991e2abab7e5f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Dec 2023 12:37:19 +0000 Subject: [PATCH 36/48] Remove "shopper's" from string (#12049) --- src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php b/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php index dc1ae187cf7..eac48fbfb74 100644 --- a/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php +++ b/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php @@ -23,7 +23,7 @@ public function get_id() { * @return string */ public function get_title() { - return __( 'Review your shopper\'s checkout experience', 'woo-gutenberg-products-block' ); + return __( 'Review your checkout experience', 'woo-gutenberg-products-block' ); } /** From 3a02b3336974f2afec9be29fa1ecd66ce68befdd Mon Sep 17 00:00:00 2001 From: Roy Ho Date: Tue, 5 Dec 2023 07:25:41 -0800 Subject: [PATCH 37/48] Product gallery/fix/view all link (#11995) * Center align view all link * Add thumbnail count specific styling * Product Gallery Thumbnails: Improve the responsiveness of the View All overlay text --------- Co-authored-by: Daniel Dudzic --- assets/js/blocks/product-gallery/style.scss | 29 ++++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/assets/js/blocks/product-gallery/style.scss b/assets/js/blocks/product-gallery/style.scss index 191bc88bf4b..ce2c93be5c8 100644 --- a/assets/js/blocks/product-gallery/style.scss +++ b/assets/js/blocks/product-gallery/style.scss @@ -7,7 +7,6 @@ $next-previous-right-off: "#{$next-previous}-right--off"; $gallery-next-previous-outside-image: "#{$gallery}[data-next-previous-buttons-position='outsideTheImage']:not(.is-single-product-gallery-image)"; $gallery-next-previous-inside-image: "#{$gallery}:not([data-next-previous-buttons-position='outsideTheImage']:not(.is-single-product-gallery-image))"; - $outside-image-offset: 30px; $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); $thumbnails-gap: 15px; @@ -317,7 +316,6 @@ $default-number-of-thumbnails: 3; flex: 0 0 $thumbnail-width; } } - } .wc-block-product-gallery-thumbnails__thumbnail__overlay { @@ -333,12 +331,10 @@ $default-number-of-thumbnails: 3; height: 100%; .wc-block-product-gallery-thumbnails__thumbnail__remaining-thumbnails-count { - @include font-size(large); font-weight: 700; } .wc-block-product-gallery-thumbnails__thumbnail__view-all { - @include font-size(smaller); text-decoration: underline; } @@ -346,5 +342,30 @@ $default-number-of-thumbnails: 3; .wc-block-product-gallery-thumbnails__thumbnail__view-all { color: #fff; } + + @mixin thumbnail-overlay-text-font-size($remaining-count-size, $view-all-size, $view-all-display: block) { + .wc-block-product-gallery-thumbnails__thumbnail__remaining-thumbnails-count { + font-size: $remaining-count-size; + } + + .wc-block-product-gallery-thumbnails__thumbnail__view-all { + font-size: $view-all-size; + display: $view-all-display; + } + } + + @include thumbnail-overlay-text-font-size(clamp(1rem, 1.5vw, 2rem), clamp(0.75rem, 0.5vw, 2rem)); + + @for $i from 3 through 8 { + #{$gallery}[data-thumbnails-number-of-thumbnails='#{$i}'] & { + @if $i == 3 { + @include thumbnail-overlay-text-font-size(clamp(1rem, 1.5vw, 2rem), clamp(0.75rem, 0.5vw, 2rem)); + } @else if $i == 4 or $i == 5 { + @include thumbnail-overlay-text-font-size(clamp(1rem, 1.25vw, 2rem), clamp(0.75rem, 0.25vw, 2rem)); + } @else { // For 6, 7, and 8 + @include thumbnail-overlay-text-font-size(clamp(1rem, 1.25vw, 2rem), null, none); + } + } + } } } From e5378fd631ec3088d9e6191afd960343159dd7dc Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Wed, 6 Dec 2023 09:05:49 +0100 Subject: [PATCH 38/48] fix: Product Collection: Featured Products 5 Columns improve spacing (#12041) --- assets/js/blocks/product-template/style.scss | 2 +- patterns/product-collection-featured-products-5-columns.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/blocks/product-template/style.scss b/assets/js/blocks/product-template/style.scss index 7cdba7a05db..772d7915c25 100644 --- a/assets/js/blocks/product-template/style.scss +++ b/assets/js/blocks/product-template/style.scss @@ -66,7 +66,7 @@ $min-product-width: 150px; /** * Default spacing between product elements */ -:where(.wc-block-product-template .wc-block-product) > * { +:where(.wc-block-product-template .wc-block-product) > *:not(:last-child) { margin-bottom: 0.75rem; margin-top: 0; } diff --git a/patterns/product-collection-featured-products-5-columns.php b/patterns/product-collection-featured-products-5-columns.php index 3bb6e12175c..7ce4b8e88a3 100644 --- a/patterns/product-collection-featured-products-5-columns.php +++ b/patterns/product-collection-featured-products-5-columns.php @@ -10,7 +10,7 @@ $collection_title = $content['titles'][0]['default'] ?? ''; ?> - +

From 95afb1e66bc757f003a9cd6e597ea10a65f7474f Mon Sep 17 00:00:00 2001 From: Niels Lange Date: Wed, 6 Dec 2023 15:46:22 +0700 Subject: [PATCH 39/48] Enable new notice styles for all themes (#12043) * Enable new notice styles * Fix Twenty Twenty-One button background color issue --- .../base/components/notice-banner/style.scss | 3 +- src/Domain/Services/Notices.php | 28 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/assets/js/base/components/notice-banner/style.scss b/assets/js/base/components/notice-banner/style.scss index 45a757f419d..6f80588b349 100644 --- a/assets/js/base/components/notice-banner/style.scss +++ b/assets/js/base/components/notice-banner/style.scss @@ -46,7 +46,7 @@ .wc-forward { float: right; color: $gray-800 !important; - background: transparent; + background: transparent !important; // For transparent notice button in Twenty Twenty-One theme. padding: 0 !important; margin: 0; border: 0; @@ -70,6 +70,7 @@ background-color: $gray-800; flex-shrink: 0; flex-grow: 0; + height: 100%; } > .wc-block-components-button { diff --git a/src/Domain/Services/Notices.php b/src/Domain/Services/Notices.php index 12090af3cf1..0d060645167 100644 --- a/src/Domain/Services/Notices.php +++ b/src/Domain/Services/Notices.php @@ -38,23 +38,12 @@ public function __construct( Package $package ) { } /** - * Set all hooks related to adding Checkout Draft order functionality to Woo Core. This is only enabled if the user - * is using the new block based cart/checkout. + * Initialize notice hooks. */ public function init() { - if ( CartCheckoutUtils::is_cart_block_default() || CartCheckoutUtils::is_checkout_block_default() ) { - add_filter( 'woocommerce_kses_notice_allowed_tags', [ $this, 'add_kses_notice_allowed_tags' ] ); - add_filter( 'wc_get_template', [ $this, 'get_notices_template' ], 10, 5 ); - add_action( - 'wp_head', - function() { - // These pages may return notices in ajax responses, so we need the styles to be ready. - if ( is_cart() || is_checkout() ) { - wp_enqueue_style( 'wc-blocks-style' ); - } - } - ); - } + add_filter( 'woocommerce_kses_notice_allowed_tags', [ $this, 'add_kses_notice_allowed_tags' ] ); + add_filter( 'wc_get_template', [ $this, 'get_notices_template' ], 10, 5 ); + add_action( 'wp_head', [ $this, 'enqueue_notice_styles' ] ); } /** @@ -101,4 +90,13 @@ public function get_notices_template( $template, $template_name, $args, $templat } return $template; } + + /** + * Replaces all notices with the new block based notices. + * + * @return void + */ + public function enqueue_notice_styles() { + wp_enqueue_style( 'wc-blocks-style' ); + } } From d83d5d2e6c6ff8eb4398f5e9f7e8172155792aa1 Mon Sep 17 00:00:00 2001 From: Tarun Vijwani Date: Wed, 6 Dec 2023 12:58:25 +0400 Subject: [PATCH 40/48] Add missing actions to data store documentation (#11986) * Add missing actions to the documentation - Add the missing actions to the following: - data-store/cart - data-store/checkout - data-store/collections - data-store/validation * Move actions above selectors in Cart doc * Add more info to cart actions * Fix syntax of Cart actions examples * Fix syntax and examples - Use store name constant instead of strings - Fix description and examples of collections document * Add more info about the keys of objects and arrays * Update billing address info in cart data * Update parameter names and descriptions in checkout.md and collections.md * Update docs/third-party-developers/extensibility/data-store/checkout.md Co-authored-by: Niels Lange --------- Co-authored-by: Niels Lange --- .../extensibility/data-store/cart.md | 562 +++++++++++++++++- .../extensibility/data-store/checkout.md | 51 +- .../extensibility/data-store/collections.md | 49 ++ .../extensibility/data-store/validation.md | 34 +- 4 files changed, 647 insertions(+), 49 deletions(-) diff --git a/docs/third-party-developers/extensibility/data-store/cart.md b/docs/third-party-developers/extensibility/data-store/cart.md index 96fde82fc13..e48f8e9108b 100644 --- a/docs/third-party-developers/extensibility/data-store/cart.md +++ b/docs/third-party-developers/extensibility/data-store/cart.md @@ -10,6 +10,28 @@ - [Overview](#overview) - [Usage](#usage) +- [Actions](#actions) + - [setCartData](#setcartdata) + - [setErrorData](#seterrordata) + - [receiveCartContents](#receivecartcontents) + - [receiveApplyingCoupon](#receiveapplyingcoupon) + - [receiveRemovingCoupon](#receiveremovingcoupon) + - [receiveCartItem](#receivecartitem) + - [itemIsPendingQuantity](#itemispendingquantity) + - [itemIsPendingDelete](#itemispendingdelete) + - [setIsCartDataStale](#setiscartdatastale) + - [updatingCustomerData](#updatingcustomerdata) + - [shippingRatesBeingSelected](#shippingratesbeingselected) + - [applyExtensionCartUpdate](#applyextensioncartupdate) + - [applyCoupon](#applycoupon) + - [removeCoupon](#removecoupon) + - [addItemToCart](#additemtocart) + - [removeItemFromCart](#removeitemfromcart) + - [changeCartItemQuantity](#changecartitemquantity) + - [selectShippingRate](#selectshippingrate) + - [setBillingAddress](#setbillingaddress) + - [setShippingAddress](#setshippingaddress) + - [updateCustomerData](#updatecustomerdata) - [Selectors](#selectors) - [getCartData](#getcartdata) - [getCustomerData](#getcustomerdata) @@ -44,6 +66,489 @@ To utilize this store you will import the `CART_STORE_KEY` in any module referen const { CART_STORE_KEY } = window.wc.wcBlocksData; ``` +## Actions + +### setCartData + +This action is used to set the cart data in the store. + +#### _Parameters_ + +- _cartData_ `object`: The current cart data with the following keys: + - _coupons_ `array`: The coupon items in the cart. + - _shippingRates_ `array`: The cart shipping rates (see `getShippingRates` selector). + - _shippingAddress_ `object`: The shipping address (see `getCustomerData` selector). + - _billingAddress_ `object`: The billing address (see `getCustomerData` selector). + - _items_ `array`: The cart items. + - _itemsCount_ `number`: The total number of items in the cart + - _itemsWeight_ `number`: The total weight of items in the cart. + - _crossSells_ `array`: The cross sells items. + - _needsPayment_ `boolean`: If the cart needs payment. + - _needsShipping_ `boolean`: If the cart needs shipping. + - _hasCalculatedShipping_ `boolean`: If the cart has calculated shipping. + - _fees_ `array`: The cart fees. + - _totals_ `object`: The cart totals (see `getCartTotals` selector). + - _errors_ `array`: The cart errors (see `getCartErrors` selector). + - _paymentRequirements_ `object`: The payment requirements for the cart. + - _extensions_ `object`: The extensions data. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( setCartData( newCartData ) ); +``` + +### setErrorData + +This action is used to set the error data in the store. + +#### _Parameters_ + +- _errorData_ `object`: The error data that needs to be set in the store. + - _code_ `string`: The error code. + - _message_ `string`: The error message. + - _data_ `object`: Additional error data. This is an optional object with the following keys: + - _status_ `number`: The error status. + - _params_ `string`: The error params. + - _message_ `string`: The error message. + - _cart_ `object`: The cart data. This is an optional object with the following keys: + - _coupons_ `array`: The coupon items in the cart. + - _shippingRates_ `array`: The cart shipping rates (see `getShippingRates` selector). + - _shippingAddress_ `object`: The shipping address (see `getCustomerData` selector). + - _billingAddress_ `object`: The billing address (see `getCustomerData` selector). + - _items_ `array`: The cart items. + - _itemsCount_ `number`: The total number of items in the cart + - _itemsWeight_ `number`: The total weight of items in the cart. + - _crossSells_ `array`: The cross sells items. + - _needsPayment_ `boolean`: If the cart needs payment. + - _needsShipping_ `boolean`: If the cart needs shipping. + - _hasCalculatedShipping_ `boolean`: If the cart has calculated shipping. + - _fees_ `array`: The cart fees. + - _totals_ `object`: The cart totals (see `getCartTotals` selector). + - _errors_ `array`: The cart errors (see `getCartErrors` selector). + - _paymentRequirements_ `object`: The payment requirements for the cart. + - _extensions_ `object`: The extensions data. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( setErrorData( newErrorData ) ); +``` + +### receiveCartContents + +This action returns an action object used in updating the store with the provided cart. It omits the customer addresses so that only updates to cart items and totals are received. + +#### _Parameters_ + +- _cartContents_ `object`: A cart contents API response. + - _coupons_ `array`: The coupon items in the cart. + - _shippingRates_ `array`: The cart shipping rates (see `getShippingRates` selector). + - _shippingAddress_ `object`: The shipping address (see `getCustomerData` selector). + - _billingAddress_ `object`: The billing address (see `getCustomerData` selector). + - _items_ `array`: The cart items. + - _itemsCount_ `number`: The total number of items in the cart + - _itemsWeight_ `number`: The total weight of items in the cart. + - _crossSells_ `array`: The cross sells items. + - _needsPayment_ `boolean`: If the cart needs payment. + - _needsShipping_ `boolean`: If the cart needs shipping. + - _hasCalculatedShipping_ `boolean`: If the cart has calculated shipping. + - _fees_ `array`: The cart fees. + - _totals_ `object`: The cart totals (see `getCartTotals` selector). + - _errors_ `array`: The cart errors (see `getCartErrors` selector). + - _paymentRequirements_ `object`: The payment requirements for the cart. + - _extensions_ `object`: The extensions data. + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _cartContents_ `object`: The cart contents with the following keys: + - _coupons_ `array`: The coupon items in the cart. + - _shippingRates_ `array`: The cart shipping rates (see `getShippingRates` selector). + - _items_ `array`: The cart items. + - _itemsCount_ `number`: The total number of items in the cart + - _itemsWeight_ `number`: The total weight of items in the cart. + - _crossSells_ `array`: The cross sells items. + - _needsPayment_ `boolean`: If the cart needs payment. + - _needsShipping_ `boolean`: If the cart needs shipping. + - _hasCalculatedShipping_ `boolean`: If the cart has calculated shipping. + - _fees_ `array`: The cart fees. + - _totals_ `object`: The cart totals (see `getCartTotals` selector). + - _errors_ `array`: The cart errors (see `getCartErrors` selector). + - _paymentRequirements_ `object`: The payment requirements for the cart. + - _extensions_ `object`: The extensions data. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( receiveCartContents( newCartContents ) ); +``` + +### receiveApplyingCoupon + +This action returns an action object used to track when a coupon is applying. + +#### _Parameters_ + +- _couponCode_ `string`: The code of the coupon being applied. + +#### _Returns_ + +- `object`: The action object with following keys: + - _type_ `string`: The action type. + - _couponCode_ `string`: The code of the coupon being applied. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( receiveApplyingCoupon( couponCode ) ); +``` + +### receiveRemovingCoupon + +This action returns an action object used to track when a coupon is removing. + +#### _Parameters_ + +- _couponCode_ `string`: The code of the coupon being removed. + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _couponCode_ `string`: The code of the coupon being removed. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( receiveRemovingCoupon( couponCode ) ); +``` + +### receiveCartItem + +This action is used to update a specific item in the cart. + +#### _Parameters_ + +- _cartResponseItem_ `object`: Cart response object with the following keys: + - _cartItem_ `object`: The cart item (see `getCartItem` selector). + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _cartItem_ `object`: The cart item (see `getCartItem` selector). + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( receiveCartItem( CartResponseItem ) ); +``` + +### itemIsPendingQuantity + +This action returns an action object to indicate if the specified cart item quantity is being updated. + +#### _Parameters_ + +- _cartItemKey_ `string`: The key of the cart item. +- _isPending_ `boolean` (default: `true`): Whether the cart item quantity is being updated. + +#### _Returns_ + +- `object`: The action object with following keys: + - _type_ `string`: The action type. + - _cartItemKey_ `string`: The key of the cart item. + - _isPending_ `boolean`: Whether the cart item quantity is being updated. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( itemIsPendingQuantity( cartItemKey, isPending ) ); +``` + +### itemIsPendingDelete + +This action returns an action object to indicate if the specified cart item is being deleted. + +#### _Parameters_ + +- _cartItemKey_ `string`: The key of the cart item. +- _isPending_ `boolean` (default: `true`): Whether the cart item is being deleted. + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _cartItemKey_ `string`: The key of the cart item. + - _isPending_ `boolean`: Whether the cart item is being deleted. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( itemIsPendingDelete( cartItemKey, isPending ) ); +``` + +### setIsCartDataStale + +This action returns an action object to indicate if the cart data is stale. + +#### _Parameters_ + +- _isCartDataStale_ `boolean` (default: `true`): Flag to mark cart data as stale; true if `lastCartUpdate` timestamp is newer than the one in wcSettings. + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _isCartDataStale_ `boolean`: Flag to mark cart data as stale; true if `lastCartUpdate` timestamp is newer than the one in wcSettings. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( setIsCartDataStale( isCartDataStale ) ); +``` + +### updatingCustomerData + +This action returns an action object to indicate if the customer data is being updated. + +#### _Parameters_ + +- _isResolving_ `boolean`: Whether the customer data is being updated. + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _isResolving_ `boolean`: Whether the customer data is being updated. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( updatingCustomerData( isResolving ) ); +``` + +### shippingRatesBeingSelected + +This action returns an action object to indicate if the shipping rates are being selected. + +#### _Parameters_ + +- _isResolving_ `boolean`: True if shipping rate is being selected. + +#### _Returns_ + +- `object`: The action object with the following keys: + - _type_ `string`: The action type. + - _isResolving_ `boolean`: True if shipping rate is being selected. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( shippingRatesBeingSelected( isResolving ) ); +``` + +### applyExtensionCartUpdate + +This action is used to send POSTs request to the /cart/extensions endpoint with the data supplied by the extension. + +#### _Parameters_ + +- _args_ `object`: The arguments for the request with the following keys: + - _extensionId_ `string`: The extension ID. + - _data_ `object`: The data to send to the endpoint with the following keys: + - _key_ `string`: The key of the extension. + - _value_ `string`: The value of the extension. + + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( applyExtensionCartUpdate( args ) ); +``` + +### applyCoupon + +This action is used to apply a coupon to the cart. + +#### _Parameters_ + +- _couponCode_ `string`: The code of the coupon to apply. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( applyCoupon( couponCode ) ); +``` + +### removeCoupon + +This action is used to remove a coupon from the cart. + +#### _Parameters_ + +- _couponCode_ `string`: The code of the coupon to remove. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( removeCoupon( couponCode ) ); +``` + +### addItemToCart + +This action is used to add an item to the cart. + +#### _Parameters_ + +- _productId_ `number`: Product ID to add to cart. +- _quantity_ `number` (default: `1`): The quantity of the product to add. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( addItemToCart( productId, quantity ) ); +``` + +### removeItemFromCart + +This action is used to remove an item from the cart. + +#### _Parameters_ + +- _cartItemKey_ `string`: Cart item being updated. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( removeItemFromCart( cartItemKey ) ); +``` + +### changeCartItemQuantity + +This action is used to change the quantity of an item in the cart. + +#### _Parameters_ + +- _cartItemKey_ `string`: Cart item being updated. +- _quantity_ `number`: Quantity of the item. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( changeCartItemQuantity( cartItemKey, quantity ) ); +``` + +### selectShippingRate + +This action is used to select a shipping rate for the cart. + +#### _Parameters_ + +- _rateId_ `string`: The ID of the shipping rate to select. +- _packageId_ `number | string` (default: `null`): The key of the packages that will select within the shipping rate. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( selectShippingRate( rateId, packageId ) ); +``` + +### setBillingAddress + +This action is used to set the billing address for the cart locally, as opposed to updateCustomerData which sends it to the server. + +#### _Parameters_ + +- _billingAddress_ `object`: Billing address that needs to be set. The keys are as following: + - _first_name_ `string`: The first name. + - _last_name_ `string`: The last name. + - _company_ `string`: The company name. + - _address_1_ `string`: The address line 1. + - _address_2_ `string`: The address line 2. + - _city_ `string`: The city name. + - _state_ `string`: The state name. + - _postcode_ `string`: The postcode. + - _country_ `string`: The country name. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( setBillingAddress( billingAddress ) ); +``` + +### setShippingAddress + +This action is used to set the shipping address for the cart locally, as opposed to updateCustomerData which sends it to the server. + +#### _Parameters_ + +- _shippingAddress_ `object`: Shipping address that needs to be set. The keys are as following: + - _first_name_ `string`: The first name. + - _last_name_ `string`: The last name. + - _company_ `string`: The company name. + - _address_1_ `string`: The address line 1. + - _address_2_ `string`: The address line 2. + - _city_ `string`: The city name. + - _state_ `string`: The state name. + - _postcode_ `string`: The postcode. + - _country_ `string`: The country name. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( setShippingAddress( shippingAddress ) ); +``` + +### updateCustomerData + +This action is used to updates the shipping and/or billing address for the customer and returns an updated cart. + +#### _Parameters_ + +- _customerData_ `object`: Customer billing and shipping address. The keys are as following: + - _shippingAddress_ `object`: The shipping address with the following keys: + - _first_name_ `string`: The first name. + - _last_name_ `string`: The last name. + - _company_ `string`: The company name. + - _address_1_ `string`: The address line 1. + - _address_2_ `string`: The address line 2. + - _city_ `string`: The city name. + - _state_ `string`: The state name. + - _postcode_ `string`: The postcode. + - _country_ `string`: The country name. + - _billingAddress_ `object`: The billing address (same keys as shipping address). +- `editing: boolean` (default: `true`): If the address is being edited, we don't update the customer data in the store from the response. + +#### _Example_ + +```js +const { dispatch } = useDispatch( CART_STORE_KEY ); +dispatch( updateCustomerData( customerData, editing ) ); +``` + ## Selectors ### getCartData @@ -73,7 +578,7 @@ Returns the Cart data from the state. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const cartData = store.getCartData(); ``` @@ -99,7 +604,7 @@ Returns the shipping and billing address from the state. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const customerData = store.getCustomerData(); ``` @@ -109,12 +614,21 @@ Returns the shipping rates from the state. #### _Returns_ -- `array`: The shipping rates. +- `array`: The shipping rates. They keys are as following: + - _id_ `string`: The shipping rate ID. + - _label_ `string`: The shipping rate label. + - _cost_ `string`: The shipping rate cost. + - _package_id_ `number`: The shipping rate package ID. + - _meta_data_ `array`: The shipping rate meta data. The keys are as following: + - _id_ `number`: The shipping rate meta data ID. + - _key_ `string`: The shipping rate meta data key. + - _value_ `string`: The shipping rate meta data value. + - _taxes_ `array`: The shipping rate taxes. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const shippingRates = store.getShippingRates(); ``` @@ -129,7 +643,7 @@ Queries whether the cart needs shipping. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const needsShipping = store.getNeedsShipping(); ``` @@ -144,7 +658,7 @@ Queries whether the cart shipping has been calculated. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const hasCalculatedShipping = store.getHasCalculatedShipping(); ``` @@ -177,7 +691,7 @@ Returns the cart totals from state. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const cartTotals = store.getCartTotals(); ``` @@ -197,7 +711,7 @@ Returns the cart meta from state. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const cartMeta = store.getCartMeta(); ``` @@ -207,12 +721,16 @@ Returns the cart errors from state if cart receives customer facing errors from #### _Returns_ -- `array`: The cart errors. +- `array`: The cart errors with the following keys: + - _code_ `string`: The error code. + - _message_ `string`: The error message. + - _data_ `object`: API response data. + #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const cartErrors = store.getCartErrors(); ``` @@ -227,7 +745,7 @@ Queries whether a coupon is being applied. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isApplyingCoupon = store.isApplyingCoupon(); ``` @@ -242,7 +760,7 @@ Queries whether the cart data is stale. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isCartDataStale = store.isCartDataStale(); ``` @@ -257,7 +775,7 @@ Returns the coupon code being applied. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const couponBeingApplied = store.getCouponBeingApplied(); ``` @@ -272,7 +790,7 @@ Queries whether a coupon is being removed. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isRemovingCoupon = store.isRemovingCoupon(); ``` @@ -287,7 +805,7 @@ Returns the coupon code being removed. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const couponBeingRemoved = store.getCouponBeingRemoved(); ``` @@ -346,7 +864,7 @@ Returns a cart item from the state. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const cartItem = store.getCartItem( cartItemKey ); ``` @@ -365,7 +883,7 @@ Queries whether a cart item is pending quantity. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isItemPendingQuantity = store.isItemPendingQuantity( cartItemKey ); ``` @@ -384,7 +902,7 @@ Queries whether a cart item is pending delete. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isItemPendingDelete = store.isItemPendingDelete( cartItemKey ); ``` @@ -399,7 +917,7 @@ Queries whether the customer data is being updated. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isCustomerDataUpdating = store.isCustomerDataUpdating(); ``` @@ -414,7 +932,7 @@ Queries whether a shipping rate is being selected. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const isShippingRateBeingSelected = store.isShippingRateBeingSelected(); ``` @@ -429,7 +947,7 @@ Retrieves the item keys for items whose quantity is currently being updated. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const itemsPendingQuantityUpdate = store.getItemsPendingQuantityUpdate(); ``` @@ -444,7 +962,7 @@ Retrieves the item keys for items that are currently being deleted. #### _Example_ ```js -const store = select( 'wc/store/cart' ); +const store = select( CART_STORE_KEY ); const itemsPendingDelete = store.getItemsPendingDelete(); ``` diff --git a/docs/third-party-developers/extensibility/data-store/checkout.md b/docs/third-party-developers/extensibility/data-store/checkout.md index 044cb8e506f..314238c9178 100644 --- a/docs/third-party-developers/extensibility/data-store/checkout.md +++ b/docs/third-party-developers/extensibility/data-store/checkout.md @@ -27,6 +27,8 @@ - [isAfterProcessing](#isafterprocessing) - [isComplete](#iscomplete) - [isCalculating](#iscalculating) +- [Actions](#actions) + - [setPrefersCollection](#setpreferscollection) ## Overview @@ -53,7 +55,7 @@ Returns the WordPress user ID of the customer whose order is currently processed #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const customerId = store.getCustomerId(); ``` @@ -68,7 +70,7 @@ Returns the WooCommerce order ID of the order that is currently being processed #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const orderId = store.getOrderId(); ``` @@ -83,7 +85,7 @@ Returns the order notes. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const orderNotes = store.getOrderNotes(); ``` @@ -98,7 +100,7 @@ Returns the URL to redirect to after checkout is complete. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const redirectUrl = store.getRedirectUrl(); ``` @@ -121,7 +123,7 @@ Returns the extra data registered by extensions. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const extensionData = store.getExtensionData(); ``` @@ -136,7 +138,7 @@ Returns the current status of the checkout process. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const checkoutStatus = store.getCheckoutStatus(); ``` @@ -151,7 +153,7 @@ Returns true if the shopper has opted to create an account with their order. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const shouldCreateAccount = store.getShouldCreateAccount(); ``` @@ -166,7 +168,7 @@ Returns true if the shopper has opted to use their shipping address as their bil #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const useShippingAsBilling = store.getUseShippingAsBilling(); ``` @@ -181,7 +183,7 @@ Returns true if an error occurred, and false otherwise. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const hasError = store.hasError(); ``` @@ -196,7 +198,7 @@ Returns true if a draft order had been created, and false otherwise. #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const hasOrder = store.hasOrder(); ``` @@ -211,7 +213,7 @@ When the checkout status is `IDLE` this flag is true. Checkout will be this stat #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const isIdle = store.isIdle(); ``` @@ -226,7 +228,7 @@ When the checkout status is `BEFORE_PROCESSING` this flag is true. Checkout will #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const isBeforeProcessing = store.isBeforeProcessing(); ``` @@ -241,7 +243,7 @@ When the checkout status is `PROCESSING` this flag is true. Checkout will be thi #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const isProcessing = store.isProcessing(); ``` @@ -256,7 +258,7 @@ When the checkout status is `AFTER_PROCESSING` this flag is true. Checkout will #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const isAfterProcessing = store.isAfterProcessing(); ``` @@ -271,7 +273,7 @@ When the checkout status is `COMPLETE` this flag is true. Checkout will have thi #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const isComplete = store.isComplete(); ``` @@ -286,10 +288,27 @@ This is true when the total is being re-calculated for the order. There are nume #### _Example_ ```js -const store = select( 'wc/store/checkout' ); +const store = select( CHECKOUT_STORE_KEY ); const isCalculating = store.isCalculating(); ``` +## Actions + +### setPrefersCollection + +Sets the `prefersCollection` flag to true or false. + +#### _Parameters_ + +- _prefersCollection_ `boolean`: True if the shopper prefers to collect their order. + +#### _Example_ + +```js +const store = dispatch( CHECKOUT_STORE_KEY ); +store.setPrefersCollection( true ); +``` + --- diff --git a/docs/third-party-developers/extensibility/data-store/collections.md b/docs/third-party-developers/extensibility/data-store/collections.md index b75dcb50510..2917afedd0b 100644 --- a/docs/third-party-developers/extensibility/data-store/collections.md +++ b/docs/third-party-developers/extensibility/data-store/collections.md @@ -6,6 +6,8 @@ - [Usage](#usage) - [Actions](#actions) - [receiveCollection( namespace, resourceName, queryString, ids = \[\], items = \[\], replace = false )](#receivecollection-namespace-resourcename-querystring-ids---items---replace--false-) + - [receiveCollectionError](#receivecollectionerror) + - [receiveLastModified](#receivelastmodified) - [Selectors](#selectors) - [getCollection](#getcollection) - [getCollectionHeader](#getcollectionheader) @@ -39,6 +41,53 @@ This will return an action object for the given arguments used in dispatching th - _response_ `Object`: An object containing a `items` property with the collection items from the response (array), and a `headers` property that is matches the `window.Headers` interface containing the headers from the response. - _replace_ `boolean`: Whether or not to replace any existing items in the store for the given indexes (namespace, resourceName, queryString) if there are already values in the store. +#### _Example_ + +```js +const { dispatch } = useDispatch( COLLECTIONS_STORE_KEY ); +dispatch( receiveCollection( namespace, resourceName, queryString, ids, response ) ); +``` + +### receiveCollectionError + +This will return an action object for the given arguments used in dispatching an error to the store. + +#### _Parameters_ + +- _namespace_ `string`: The route namespace for the collection, eg. `/wc/blocks`. +- _resourceName_ `string`: The resource name for the collection, eg. `products/attributes`. +- _queryString_ `string`: An additional query string to add to the request for the collection. Note, collections are cached by the query string, eg. `?order=ASC`. +- _ids_ `array`: If the collection route has placeholders for ids, you provide them via this argument in the order of how the placeholders appear in the route. +- _error_ `object`: The error object with the following keys: + - _code_ `string`: The error code. + - _message_ `string`: The error message. + - _data_ `object`: The error data with the following keys: + - _status_ `number`: The HTTP status code. + - _params_ `object`: The parameters for the error. + - _headers_ `object`: The headers for the error. + +#### _Example_ + +```js +const { dispatch } = useDispatch( COLLECTIONS_STORE_KEY ); +dispatch( receiveCollectionError( namespace, resourceName, queryString, ids, error ) ); +``` + +### receiveLastModified + +This will return an action object for the given arguments used in dispatching the last modified date to the store. + +#### _Parameters_ + +- _timestamp_ `number`: The timestamp of the last modified date. + +#### _Example_ + +```js +const { dispatch } = useDispatch( COLLECTIONS_STORE_KEY ); +dispatch( receiveLastModified( timestamp ) ); +``` + ## Selectors ### getCollection diff --git a/docs/third-party-developers/extensibility/data-store/validation.md b/docs/third-party-developers/extensibility/data-store/validation.md index d9bea418666..b9ab4f0bd81 100644 --- a/docs/third-party-developers/extensibility/data-store/validation.md +++ b/docs/third-party-developers/extensibility/data-store/validation.md @@ -12,6 +12,7 @@ - [hideValidationError( errorId )](#hidevalidationerror-errorid-) - [showValidationError( errorId )](#showvalidationerror-errorid-) - [showAllValidationErrors](#showallvalidationerrors) + - [clearAllValidationErrors](#clearallvalidationerrors) - [Selectors](#selectors) - [getValidationError( errorId )](#getvalidationerror-errorid-) - [getValidationErrorId( errorId )](#getvalidationerrorid-errorid-) @@ -42,7 +43,7 @@ When the checkout process begins, it will check if this data store has any entri ## Usage -To utilize this store you will import the `CART_STORE_KEY` in any module referencing it. Assuming `@woocommerce/block-data` is registered as an external pointing to `wc.wcBlocksData` you can import the key via: +To utilize this store you will import the `VALIDATION_STORE_KEY` in any module referencing it. Assuming `@woocommerce/block-data` is registered as an external pointing to `wc.wcBlocksData` you can import the key via: ```js const { VALIDATION_STORE_KEY } = window.wc.wcBlocksData; @@ -168,7 +169,7 @@ Clears a validation error. #### _Example_ ```js -const store = dispatch( 'wc/store/validation' ); +const store = dispatch( VALIDATION_STORE_KEY ); store.clearValidationError( 'billing-first-name' ); ``` @@ -185,7 +186,7 @@ Clears multiple validation errors at once. If no error IDs are passed, all valid 1. This will clear only the validation errors passed in the array. ```js -const store = dispatch( 'wc/store/validation' ); +const store = dispatch( VALIDATION_STORE_KEY ); store.clearValidationErrors( [ 'billing-first-name', 'billing-last-name', @@ -196,7 +197,7 @@ store.clearValidationErrors( [ 2. This will clear all validation errors. ```js -const store = dispatch( 'wc/store/validation' ); +const store = dispatch( VALIDATION_STORE_KEY ); store.clearValidationErrors(); ``` @@ -212,7 +213,7 @@ Sets the validation errors. The entries in _errors_ will be _added_ to the list ```js const { dispatch } = wp.data; -const { setValidationErrors } = dispatch( 'wc/store/validation' ); +const { setValidationErrors } = dispatch( VALIDATION_STORE_KEY ); setValidationErrors( { 'billing-first-name': { @@ -238,7 +239,7 @@ Hides a validation error by setting the `hidden` property to `true`. This will _ ```js const { dispatch } = wp.data; -const { hideValidationError } = dispatch( 'wc/store/validation' ); +const { hideValidationError } = dispatch( VALIDATION_STORE_KEY ); hideValidationError( 'billing-first-name' ); ``` @@ -255,7 +256,7 @@ Shows a validation error by setting the `hidden` property to `false`. ```js const { dispatch } = wp.data; -const { showValidationError } = dispatch( 'wc/store/validation' ); +const { showValidationError } = dispatch( VALIDATION_STORE_KEY ); showValidationError( 'billing-first-name' ); ``` @@ -268,11 +269,22 @@ Shows all validation errors by setting the `hidden` property to `false`. ```js const { dispatch } = wp.data; -const { showAllValidationErrors } = dispatch( 'wc/store/validation' ); +const { showAllValidationErrors } = dispatch( VALIDATION_STORE_KEY ); showAllValidationErrors(); ``` +### clearAllValidationErrors + +Clears all validation errors by removing them from the store. + +#### _Example_ + +```js +const { clearAllValidationErrors } = dispatch( VALIDATION_STORE_KEY ); +clearAllValidationErrors(); +``` + ## Selectors ### getValidationError( errorId ) @@ -290,7 +302,7 @@ Returns the validation error. #### _Example_ ```js -const store = select( 'wc/store/validation' ); +const store = select( VALIDATION_STORE_KEY ); const billingFirstNameError = store.getValidationError( 'billing-first-name' ); ``` @@ -309,7 +321,7 @@ Gets a validation error ID for use in HTML which can be used as a CSS selector, #### _Example_ ```js -const store = select( 'wc/store/validation' ); +const store = select( VALIDATION_STORE_KEY ); const billingFirstNameErrorId = store.getValidationErrorId( 'billing-first-name' ); ``` @@ -325,7 +337,7 @@ Returns true if validation errors occurred, and false otherwise. #### _Example_ ```js -const store = select( 'wc/store/validation' ); +const store = select( VALIDATION_STORE_KEY ); const hasValidationErrors = store.hasValidationErrors(); ``` From c7a84b1ac50f75c640c5be4584493254b37860e3 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Dec 2023 12:31:37 +0000 Subject: [PATCH 41/48] Adjust nesting so page wraps the main group block (#12050) --- templates/templates/blockified/page-cart.html | 11 ++++------- templates/templates/blockified/page-checkout.html | 8 +++----- templates/templates/page-cart.html | 11 ++++------- templates/templates/page-checkout.html | 8 +++----- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/templates/templates/blockified/page-cart.html b/templates/templates/blockified/page-cart.html index b78e5739f41..90c4d11a450 100644 --- a/templates/templates/blockified/page-cart.html +++ b/templates/templates/blockified/page-cart.html @@ -1,13 +1,10 @@ - - + +
- - -
- - + + \ No newline at end of file diff --git a/templates/templates/blockified/page-checkout.html b/templates/templates/blockified/page-checkout.html index 9cef5328aa1..5261d1aecb8 100644 --- a/templates/templates/blockified/page-checkout.html +++ b/templates/templates/blockified/page-checkout.html @@ -1,11 +1,9 @@ - - + +
- - -
+ \ No newline at end of file diff --git a/templates/templates/page-cart.html b/templates/templates/page-cart.html index b78e5739f41..90c4d11a450 100644 --- a/templates/templates/page-cart.html +++ b/templates/templates/page-cart.html @@ -1,13 +1,10 @@ - - + +
- - -
- - + + \ No newline at end of file diff --git a/templates/templates/page-checkout.html b/templates/templates/page-checkout.html index 9cef5328aa1..5261d1aecb8 100644 --- a/templates/templates/page-checkout.html +++ b/templates/templates/page-checkout.html @@ -1,11 +1,9 @@ - - + +
- - -
+ \ No newline at end of file From ee21c687018b7f547c04311f1f311410836f9b37 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Dec 2023 12:43:07 +0000 Subject: [PATCH 42/48] Use get_plugins because it uses cache (#12031) --- src/BlockTypes/Checkout.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/BlockTypes/Checkout.php b/src/BlockTypes/Checkout.php index 38133e31b14..b07f947d2b6 100644 --- a/src/BlockTypes/Checkout.php +++ b/src/BlockTypes/Checkout.php @@ -343,22 +343,21 @@ function( $acc, $method ) { } if ( $is_block_editor && ! $this->asset_data_registry->exists( 'incompatibleExtensions' ) ) { - if ( ! class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { + if ( ! class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) || ! function_exists( 'get_plugins' ) ) { return; } - if ( ! function_exists( 'get_plugin_data' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - } - $declared_extensions = \Automattic\WooCommerce\Utilities\FeaturesUtil::get_compatible_plugins_for_feature( 'cart_checkout_blocks' ); + $all_plugins = \get_plugins(); // Note that `get_compatible_plugins_for_feature` calls `get_plugins` internally, so this is already in cache. $incompatible_extensions = array_reduce( $declared_extensions['incompatible'], - function( $acc, $item ) { - $plugin = get_plugin_data( WP_PLUGIN_DIR . '/' . $item ); - $acc[] = [ - 'id' => $plugin['TextDomain'], - 'title' => $plugin['Name'], + function( $acc, $item ) use ( $all_plugins ) { + $plugin = $all_plugins[ $item ] ?? null; + $plugin_id = $plugin['TextDomain'] ?? dirname( $item ); + $plugin_name = $plugin['Name'] ?? $plugin_id; + $acc[] = [ + 'id' => $plugin_id, + 'title' => $plugin_name, ]; return $acc; }, From 3e4079b92f1e85ce0c5d8d7ee90b7f8a733f57d9 Mon Sep 17 00:00:00 2001 From: Karol Manijak <20098064+kmanijak@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:28:48 +0100 Subject: [PATCH 43/48] Unify the @typescript-eslint/parser version with eslint-plugin and what's in Core (#12066) --- package-lock.json | 332 ++++++---------------------------------------- package.json | 2 +- 2 files changed, 41 insertions(+), 293 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7ff5aea4eb4..bfadde7cfc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,7 @@ "@types/wordpress__notices": "^3.5.0", "@types/wordpress__wordcount": "^2.4.2", "@typescript-eslint/eslint-plugin": "5.56.0", - "@typescript-eslint/parser": "5.35.1", + "@typescript-eslint/parser": "5.56.0", "@woocommerce/api": "0.2.0", "@woocommerce/data": "4.1.0", "@woocommerce/e2e-utils": "0.2.0", @@ -14194,13 +14194,14 @@ "license": "ISC" }, "node_modules/@typescript-eslint/parser": { - "version": "5.35.1", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz", + "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "5.35.1", - "@typescript-eslint/types": "5.35.1", - "@typescript-eslint/typescript-estree": "5.35.1", + "@typescript-eslint/scope-manager": "5.56.0", + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/typescript-estree": "5.56.0", "debug": "^4.3.4" }, "engines": { @@ -14219,38 +14220,6 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "5.35.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.35.1", - "@typescript-eslint/visitor-keys": "5.35.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.35.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.35.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.56.0", "dev": true, @@ -14267,18 +14236,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { - "version": "5.56.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/type-utils": { "version": "5.56.0", "dev": true, @@ -14305,10 +14262,11 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/types": { "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", + "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -14317,10 +14275,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", + "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "5.56.0", "@typescript-eslint/visitor-keys": "5.56.0", @@ -14343,94 +14302,11 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/@typescript-eslint/types": { - "version": "5.35.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.35.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "5.35.1", - "@typescript-eslint/visitor-keys": "5.35.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.35.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.35.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -14440,8 +14316,9 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -14454,8 +14331,9 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { "version": "4.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/@typescript-eslint/utils": { "version": "5.56.0", @@ -14482,44 +14360,6 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "5.56.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { "version": "6.0.0", "dev": true, @@ -14566,18 +14406,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": { - "version": "5.56.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@use-gesture/core": { "version": "10.2.27", "license": "MIT" @@ -57622,31 +57450,15 @@ } }, "@typescript-eslint/parser": { - "version": "5.35.1", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz", + "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.35.1", - "@typescript-eslint/types": "5.35.1", - "@typescript-eslint/typescript-estree": "5.35.1", + "@typescript-eslint/scope-manager": "5.56.0", + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/typescript-estree": "5.56.0", "debug": "^4.3.4" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.35.1", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.35.1", - "@typescript-eslint/visitor-keys": "5.35.1" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.35.1", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.35.1", - "eslint-visitor-keys": "^3.3.0" - } - } } }, "@typescript-eslint/scope-manager": { @@ -57655,12 +57467,6 @@ "requires": { "@typescript-eslint/types": "5.56.0", "@typescript-eslint/visitor-keys": "5.56.0" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "5.56.0", - "dev": true - } } }, "@typescript-eslint/type-utils": { @@ -57671,55 +57477,22 @@ "@typescript-eslint/utils": "5.56.0", "debug": "^4.3.4", "tsutils": "^3.21.0" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "5.56.0", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "dev": true - } } }, "@typescript-eslint/types": { - "version": "5.35.1", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", + "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.35.1", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", + "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.35.1", - "@typescript-eslint/visitor-keys": "5.35.1", + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/visitor-keys": "5.56.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -57727,16 +57500,10 @@ "tsutils": "^3.21.0" }, "dependencies": { - "@typescript-eslint/visitor-keys": { - "version": "5.35.1", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.35.1", - "eslint-visitor-keys": "^3.3.0" - } - }, "lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -57744,6 +57511,8 @@ }, "semver": { "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -57751,6 +57520,8 @@ }, "yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } @@ -57769,23 +57540,6 @@ "semver": "^7.3.7" }, "dependencies": { - "@typescript-eslint/types": { - "version": "5.56.0", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, "lru-cache": { "version": "6.0.0", "dev": true, @@ -57812,12 +57566,6 @@ "requires": { "@typescript-eslint/types": "5.56.0", "eslint-visitor-keys": "^3.3.0" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "5.56.0", - "dev": true - } } }, "@use-gesture/core": { diff --git a/package.json b/package.json index 286d4107c3e..0911a9fb103 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "@types/wordpress__notices": "^3.5.0", "@types/wordpress__wordcount": "^2.4.2", "@typescript-eslint/eslint-plugin": "5.56.0", - "@typescript-eslint/parser": "5.35.1", + "@typescript-eslint/parser": "5.56.0", "@woocommerce/api": "0.2.0", "@woocommerce/data": "4.1.0", "@woocommerce/e2e-utils": "0.2.0", From 37f9d75d7a5ec6c801dd1c8caa5cc16419111155 Mon Sep 17 00:00:00 2001 From: Karol Manijak <20098064+kmanijak@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:50:24 +0100 Subject: [PATCH 44/48] Restructure Product Collection editor files (#11981) * Move Product Collection editor experience components into /edit directory * Move Product Collection Inner Blocks structure to constants * Improve the Product Collection's variations names --- .../js/blocks/product-collection/constants.ts | 91 +++++++++++++++++++ .../product-collection/{ => edit}/editor.scss | 0 .../{edit.tsx => edit/index.tsx} | 67 +------------- .../inspector-controls/attributes-control.tsx | 2 +- .../inspector-controls/columns-control.tsx | 4 +- .../inspector-controls/created-control.tsx | 2 +- .../featured-products-control.tsx | 2 +- .../hand-picked-products-control.tsx | 2 +- .../{ => edit}/inspector-controls/index.tsx | 8 +- .../inherit-query-control.tsx | 6 +- .../inspector-controls/keyword-control.tsx | 2 +- .../layout-options-control.tsx | 2 +- .../inspector-controls/on-sale-control.tsx | 2 +- .../inspector-controls/order-by-control.tsx | 4 +- .../stock-status-control.tsx | 4 +- .../taxonomy-controls/index.tsx | 2 +- .../taxonomy-controls/taxonomy-item.tsx | 0 .../inspector-controls/upgrade-notice.tsx | 0 .../display-layout-toolbar.tsx | 2 +- .../display-settings-toolbar.tsx | 2 +- .../{ => edit}/toolbar-controls/index.tsx | 4 +- .../pattern-chooser-toolbar.tsx | 0 .../pattern-selection-modal.tsx | 2 +- .../variations/elements/product-summary.tsx | 3 +- .../variations/elements/product-title.tsx | 3 +- 25 files changed, 125 insertions(+), 91 deletions(-) rename assets/js/blocks/product-collection/{ => edit}/editor.scss (100%) rename assets/js/blocks/product-collection/{edit.tsx => edit/index.tsx} (62%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/attributes-control.tsx (97%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/columns-control.tsx (94%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/created-control.tsx (97%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/featured-products-control.tsx (95%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/hand-picked-products-control.tsx (98%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/index.tsx (96%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/inherit-query-control.tsx (93%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/keyword-control.tsx (96%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/layout-options-control.tsx (97%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/on-sale-control.tsx (95%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/order-by-control.tsx (96%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/stock-status-control.tsx (97%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/taxonomy-controls/index.tsx (97%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/taxonomy-controls/taxonomy-item.tsx (100%) rename assets/js/blocks/product-collection/{ => edit}/inspector-controls/upgrade-notice.tsx (100%) rename assets/js/blocks/product-collection/{ => edit}/toolbar-controls/display-layout-toolbar.tsx (98%) rename assets/js/blocks/product-collection/{ => edit}/toolbar-controls/display-settings-toolbar.tsx (98%) rename assets/js/blocks/product-collection/{ => edit}/toolbar-controls/index.tsx (93%) rename assets/js/blocks/product-collection/{ => edit}/toolbar-controls/pattern-chooser-toolbar.tsx (100%) rename assets/js/blocks/product-collection/{ => edit}/toolbar-controls/pattern-selection-modal.tsx (97%) diff --git a/assets/js/blocks/product-collection/constants.ts b/assets/js/blocks/product-collection/constants.ts index a88fd5d3754..565b0133010 100644 --- a/assets/js/blocks/product-collection/constants.ts +++ b/assets/js/blocks/product-collection/constants.ts @@ -3,6 +3,12 @@ */ import { getSetting } from '@woocommerce/settings'; import { objectOmit } from '@woocommerce/utils'; +import { + type InnerBlockTemplate, + createBlock, + // @ts-expect-error Missing types in Gutenberg + createBlocksFromInnerBlocksTemplate, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -15,7 +21,10 @@ import { ProductCollectionDisplayLayout, LayoutOptions, } from './types'; +import { ImageSizing } from '../../atomic/blocks/product-elements/image/types'; +import { VARIATION_NAME as PRODUCT_TITLE_ID } from './variations/elements/product-title'; import { getDefaultValueOfInheritQueryFromTemplate } from './utils'; +import blockJson from './block.json'; export const STOCK_STATUS_OPTIONS = getSetting< Record< string, string > >( 'stockStatusOptions', @@ -91,3 +100,85 @@ export const DEFAULT_FILTERS: Partial< ProductCollectionQuery > = { featured: DEFAULT_QUERY.featured, timeFrame: undefined, }; + +/** + * Default inner block templates for the product collection block. + * Exported for use in different collections, e.g., 'New Arrivals' collection. + */ +export const INNER_BLOCKS_PRODUCT_TEMPLATE: InnerBlockTemplate = [ + 'woocommerce/product-template', + {}, + [ + [ + 'woocommerce/product-image', + { + imageSizing: ImageSizing.THUMBNAIL, + }, + ], + [ + 'core/post-title', + { + textAlign: 'center', + level: 3, + fontSize: 'medium', + style: { + spacing: { + margin: { + bottom: '0.75rem', + top: '0', + }, + }, + }, + isLink: true, + __woocommerceNamespace: PRODUCT_TITLE_ID, + }, + ], + [ + 'woocommerce/product-price', + { + textAlign: 'center', + fontSize: 'small', + }, + ], + [ + 'woocommerce/product-button', + { + textAlign: 'center', + fontSize: 'small', + }, + ], + ], +]; + +export const INNER_BLOCKS_PAGINATION_TEMPLATE: InnerBlockTemplate = [ + 'core/query-pagination', + { + layout: { + type: 'flex', + justifyContent: 'center', + }, + }, +]; + +export const INNER_BLOCKS_NO_RESULTS_TEMPLATE: InnerBlockTemplate = [ + 'woocommerce/product-collection-no-results', +]; + +export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [ + INNER_BLOCKS_PRODUCT_TEMPLATE, + INNER_BLOCKS_PAGINATION_TEMPLATE, + INNER_BLOCKS_NO_RESULTS_TEMPLATE, +]; + +export const getDefaultProductCollection = () => + createBlock( + blockJson.name, + { + ...DEFAULT_ATTRIBUTES, + query: { + ...DEFAULT_ATTRIBUTES.query, + inherit: getDefaultValueOfInheritQueryFromTemplate(), + }, + }, + createBlocksFromInnerBlocksTemplate( INNER_BLOCKS_TEMPLATE ) + ); diff --git a/assets/js/blocks/product-collection/editor.scss b/assets/js/blocks/product-collection/edit/editor.scss similarity index 100% rename from assets/js/blocks/product-collection/editor.scss rename to assets/js/blocks/product-collection/edit/editor.scss diff --git a/assets/js/blocks/product-collection/edit.tsx b/assets/js/blocks/product-collection/edit/index.tsx similarity index 62% rename from assets/js/blocks/product-collection/edit.tsx rename to assets/js/blocks/product-collection/edit/index.tsx index 9da8bbc60a9..9dae873a53c 100644 --- a/assets/js/blocks/product-collection/edit.tsx +++ b/assets/js/blocks/product-collection/edit/index.tsx @@ -2,82 +2,23 @@ * External dependencies */ import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; -import { BlockEditProps, InnerBlockTemplate } from '@wordpress/blocks'; +import { BlockEditProps } from '@wordpress/blocks'; import { useInstanceId } from '@wordpress/compose'; import { useEffect } from '@wordpress/element'; /** * Internal dependencies */ -import { ImageSizing } from '../../atomic/blocks/product-elements/image/types'; import type { ProductCollectionAttributes, ProductCollectionQuery, -} from './types'; -import { VARIATION_NAME as PRODUCT_TITLE_ID } from './variations/elements/product-title'; +} from '../types'; import InspectorControls from './inspector-controls'; -import { DEFAULT_ATTRIBUTES } from './constants'; +import { DEFAULT_ATTRIBUTES, INNER_BLOCKS_TEMPLATE } from '../constants'; import './editor.scss'; -import { getDefaultValueOfInheritQueryFromTemplate } from './utils'; +import { getDefaultValueOfInheritQueryFromTemplate } from '../utils'; import ToolbarControls from './toolbar-controls'; -export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [ - [ - 'woocommerce/product-template', - {}, - [ - [ - 'woocommerce/product-image', - { - imageSizing: ImageSizing.THUMBNAIL, - }, - ], - [ - 'core/post-title', - { - textAlign: 'center', - level: 3, - fontSize: 'medium', - style: { - spacing: { - margin: { - bottom: '0.75rem', - top: '0', - }, - }, - }, - isLink: true, - __woocommerceNamespace: PRODUCT_TITLE_ID, - }, - ], - [ - 'woocommerce/product-price', - { - textAlign: 'center', - fontSize: 'small', - }, - ], - [ - 'woocommerce/product-button', - { - textAlign: 'center', - fontSize: 'small', - }, - ], - ], - ], - [ - 'core/query-pagination', - { - layout: { - type: 'flex', - justifyContent: 'center', - }, - }, - ], - [ 'woocommerce/product-collection-no-results' ], -]; - const Edit = ( props: BlockEditProps< ProductCollectionAttributes > ) => { const { attributes, setAttributes } = props; const { queryId } = attributes; diff --git a/assets/js/blocks/product-collection/inspector-controls/attributes-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/attributes-control.tsx similarity index 97% rename from assets/js/blocks/product-collection/inspector-controls/attributes-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/attributes-control.tsx index f8f08f7aa76..a401aaeb484 100644 --- a/assets/js/blocks/product-collection/inspector-controls/attributes-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/attributes-control.tsx @@ -15,7 +15,7 @@ import { /** * Internal dependencies */ -import { QueryControlProps } from '../types'; +import { QueryControlProps } from '../../types'; const EDIT_ATTRIBUTES_URL = `${ ADMIN_URL }edit.php?post_type=product&page=product_attributes`; diff --git a/assets/js/blocks/product-collection/inspector-controls/columns-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/columns-control.tsx similarity index 94% rename from assets/js/blocks/product-collection/inspector-controls/columns-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/columns-control.tsx index 0bdcbe2a31f..9c74f4a135c 100644 --- a/assets/js/blocks/product-collection/inspector-controls/columns-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/columns-control.tsx @@ -13,8 +13,8 @@ import { /** * Internal dependencies */ -import { DisplayLayoutToolbarProps } from '../types'; -import { getDefaultDisplayLayout } from '../constants'; +import { DisplayLayoutToolbarProps } from '../../types'; +import { getDefaultDisplayLayout } from '../../constants'; const columnsLabel = __( 'Columns', 'woo-gutenberg-products-block' ); const toggleLabel = __( 'Responsive', 'woo-gutenberg-products-block' ); diff --git a/assets/js/blocks/product-collection/inspector-controls/created-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/created-control.tsx similarity index 97% rename from assets/js/blocks/product-collection/inspector-controls/created-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/created-control.tsx index 2c485da7346..4cbb3760088 100644 --- a/assets/js/blocks/product-collection/inspector-controls/created-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/created-control.tsx @@ -22,7 +22,7 @@ import { /** * Internal dependencies */ -import { ETimeFrameOperator, QueryControlProps } from '../types'; +import { ETimeFrameOperator, QueryControlProps } from '../../types'; const CreatedControl = ( props: QueryControlProps ) => { const { query, setQueryAttribute } = props; diff --git a/assets/js/blocks/product-collection/inspector-controls/featured-products-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/featured-products-control.tsx similarity index 95% rename from assets/js/blocks/product-collection/inspector-controls/featured-products-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/featured-products-control.tsx index dec43da0da5..f912328ee0c 100644 --- a/assets/js/blocks/product-collection/inspector-controls/featured-products-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/featured-products-control.tsx @@ -13,7 +13,7 @@ import { /** * Internal dependencies */ -import { QueryControlProps } from '../types'; +import { QueryControlProps } from '../../types'; const FeaturedProductsControl = ( props: QueryControlProps ) => { const { query, setQueryAttribute } = props; diff --git a/assets/js/blocks/product-collection/inspector-controls/hand-picked-products-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/hand-picked-products-control.tsx similarity index 98% rename from assets/js/blocks/product-collection/inspector-controls/hand-picked-products-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/hand-picked-products-control.tsx index b0d14e6b774..fe3c966a7c3 100644 --- a/assets/js/blocks/product-collection/inspector-controls/hand-picked-products-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/hand-picked-products-control.tsx @@ -16,7 +16,7 @@ import { /** * Internal dependencies */ -import { QueryControlProps } from '../types'; +import { QueryControlProps } from '../../types'; /** * Returns: diff --git a/assets/js/blocks/product-collection/inspector-controls/index.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx similarity index 96% rename from assets/js/blocks/product-collection/inspector-controls/index.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/index.tsx index 63116f6c703..2109dc03977 100644 --- a/assets/js/blocks/product-collection/inspector-controls/index.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx @@ -24,10 +24,10 @@ import { /** * Internal dependencies */ -import metadata from '../block.json'; -import { ProductCollectionAttributes } from '../types'; -import { setQueryAttribute } from '../utils'; -import { DEFAULT_FILTERS, getDefaultSettings } from '../constants'; +import metadata from '../../block.json'; +import { ProductCollectionAttributes } from '../../types'; +import { setQueryAttribute } from '../../utils'; +import { DEFAULT_FILTERS, getDefaultSettings } from '../../constants'; import UpgradeNotice from './upgrade-notice'; import ColumnsControl from './columns-control'; import InheritQueryControl from './inherit-query-control'; diff --git a/assets/js/blocks/product-collection/inspector-controls/inherit-query-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/inherit-query-control.tsx similarity index 93% rename from assets/js/blocks/product-collection/inspector-controls/inherit-query-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/inherit-query-control.tsx index 5b337329a24..82123ee9815 100644 --- a/assets/js/blocks/product-collection/inspector-controls/inherit-query-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/inherit-query-control.tsx @@ -16,9 +16,9 @@ import { /** * Internal dependencies */ -import { ProductCollectionQuery } from '../types'; -import { DEFAULT_QUERY } from '../constants'; -import { getDefaultValueOfInheritQueryFromTemplate } from '../utils'; +import { ProductCollectionQuery } from '../../types'; +import { DEFAULT_QUERY } from '../../constants'; +import { getDefaultValueOfInheritQueryFromTemplate } from '../../utils'; const label = __( 'Inherit query from template', diff --git a/assets/js/blocks/product-collection/inspector-controls/keyword-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/keyword-control.tsx similarity index 96% rename from assets/js/blocks/product-collection/inspector-controls/keyword-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/keyword-control.tsx index f1ceccdfe88..7709bd8c042 100644 --- a/assets/js/blocks/product-collection/inspector-controls/keyword-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/keyword-control.tsx @@ -14,7 +14,7 @@ import { /** * Internal dependencies */ -import { QueryControlProps } from '../types'; +import { QueryControlProps } from '../../types'; const KeywordControl = ( props: QueryControlProps ) => { const { query, setQueryAttribute } = props; diff --git a/assets/js/blocks/product-collection/inspector-controls/layout-options-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/layout-options-control.tsx similarity index 97% rename from assets/js/blocks/product-collection/inspector-controls/layout-options-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/layout-options-control.tsx index 2b3eb61901d..0377176e34c 100644 --- a/assets/js/blocks/product-collection/inspector-controls/layout-options-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/layout-options-control.tsx @@ -19,7 +19,7 @@ import { /** * Internal dependencies */ -import { DisplayLayoutToolbarProps, LayoutOptions } from '../types'; +import { DisplayLayoutToolbarProps, LayoutOptions } from '../../types'; const getHelpText = ( layoutOptions: LayoutOptions ) => { switch ( layoutOptions ) { diff --git a/assets/js/blocks/product-collection/inspector-controls/on-sale-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/on-sale-control.tsx similarity index 95% rename from assets/js/blocks/product-collection/inspector-controls/on-sale-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/on-sale-control.tsx index d12bf2e00cd..16d4cb2506b 100644 --- a/assets/js/blocks/product-collection/inspector-controls/on-sale-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/on-sale-control.tsx @@ -12,7 +12,7 @@ import { /** * Internal dependencies */ -import { QueryControlProps } from '../types'; +import { QueryControlProps } from '../../types'; const OnSaleControl = ( props: QueryControlProps ) => { const { query, setQueryAttribute } = props; diff --git a/assets/js/blocks/product-collection/inspector-controls/order-by-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx similarity index 96% rename from assets/js/blocks/product-collection/inspector-controls/order-by-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx index 2c57ecbf9e0..23fa538e0d5 100644 --- a/assets/js/blocks/product-collection/inspector-controls/order-by-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/order-by-control.tsx @@ -16,8 +16,8 @@ import { TProductCollectionOrder, TProductCollectionOrderBy, QueryControlProps, -} from '../types'; -import { getDefaultQuery } from '../constants'; +} from '../../types'; +import { getDefaultQuery } from '../../constants'; const orderOptions = [ { diff --git a/assets/js/blocks/product-collection/inspector-controls/stock-status-control.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/stock-status-control.tsx similarity index 97% rename from assets/js/blocks/product-collection/inspector-controls/stock-status-control.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/stock-status-control.tsx index a1ee7dce2b2..615708047cc 100644 --- a/assets/js/blocks/product-collection/inspector-controls/stock-status-control.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/stock-status-control.tsx @@ -13,8 +13,8 @@ import { /** * Internal dependencies */ -import { QueryControlProps } from '../types'; -import { STOCK_STATUS_OPTIONS, getDefaultStockStatuses } from '../constants'; +import { QueryControlProps } from '../../types'; +import { STOCK_STATUS_OPTIONS, getDefaultStockStatuses } from '../../constants'; /** * Gets the id of a specific stock status from its text label diff --git a/assets/js/blocks/product-collection/inspector-controls/taxonomy-controls/index.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/index.tsx similarity index 97% rename from assets/js/blocks/product-collection/inspector-controls/taxonomy-controls/index.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/index.tsx index 454b59e97bb..0754a9ca181 100644 --- a/assets/js/blocks/product-collection/inspector-controls/taxonomy-controls/index.tsx +++ b/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/index.tsx @@ -15,7 +15,7 @@ import { * Internal dependencies */ import TaxonomyItem from './taxonomy-item'; -import { ProductCollectionQuery } from '../../types'; +import { ProductCollectionQuery } from '../../../types'; interface TaxonomyControlProps { query: ProductCollectionQuery; diff --git a/assets/js/blocks/product-collection/inspector-controls/taxonomy-controls/taxonomy-item.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/taxonomy-item.tsx similarity index 100% rename from assets/js/blocks/product-collection/inspector-controls/taxonomy-controls/taxonomy-item.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/taxonomy-controls/taxonomy-item.tsx diff --git a/assets/js/blocks/product-collection/inspector-controls/upgrade-notice.tsx b/assets/js/blocks/product-collection/edit/inspector-controls/upgrade-notice.tsx similarity index 100% rename from assets/js/blocks/product-collection/inspector-controls/upgrade-notice.tsx rename to assets/js/blocks/product-collection/edit/inspector-controls/upgrade-notice.tsx diff --git a/assets/js/blocks/product-collection/toolbar-controls/display-layout-toolbar.tsx b/assets/js/blocks/product-collection/edit/toolbar-controls/display-layout-toolbar.tsx similarity index 98% rename from assets/js/blocks/product-collection/toolbar-controls/display-layout-toolbar.tsx rename to assets/js/blocks/product-collection/edit/toolbar-controls/display-layout-toolbar.tsx index d617bda1832..d2445a04ec8 100644 --- a/assets/js/blocks/product-collection/toolbar-controls/display-layout-toolbar.tsx +++ b/assets/js/blocks/product-collection/edit/toolbar-controls/display-layout-toolbar.tsx @@ -12,7 +12,7 @@ import { DisplayLayoutToolbarProps, ProductCollectionDisplayLayout, LayoutOptions, -} from '../types'; +} from '../../types'; const DisplayLayoutToolbar = ( props: DisplayLayoutToolbarProps ) => { const { type, columns, shrinkColumns } = props.displayLayout; diff --git a/assets/js/blocks/product-collection/toolbar-controls/display-settings-toolbar.tsx b/assets/js/blocks/product-collection/edit/toolbar-controls/display-settings-toolbar.tsx similarity index 98% rename from assets/js/blocks/product-collection/toolbar-controls/display-settings-toolbar.tsx rename to assets/js/blocks/product-collection/edit/toolbar-controls/display-settings-toolbar.tsx index e9c554efede..fbd3402edc0 100644 --- a/assets/js/blocks/product-collection/toolbar-controls/display-settings-toolbar.tsx +++ b/assets/js/blocks/product-collection/edit/toolbar-controls/display-settings-toolbar.tsx @@ -16,7 +16,7 @@ import { /** * Internal dependencies */ -import { ProductCollectionQuery } from '../types'; +import { ProductCollectionQuery } from '../../types'; interface DisplaySettingsToolbarProps { query: ProductCollectionQuery; diff --git a/assets/js/blocks/product-collection/toolbar-controls/index.tsx b/assets/js/blocks/product-collection/edit/toolbar-controls/index.tsx similarity index 93% rename from assets/js/blocks/product-collection/toolbar-controls/index.tsx rename to assets/js/blocks/product-collection/edit/toolbar-controls/index.tsx index bb0ab643dc6..0d87fb53ec5 100644 --- a/assets/js/blocks/product-collection/toolbar-controls/index.tsx +++ b/assets/js/blocks/product-collection/edit/toolbar-controls/index.tsx @@ -8,8 +8,8 @@ import { BlockControls } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { setQueryAttribute } from '../utils'; -import { ProductCollectionAttributes } from '../types'; +import { setQueryAttribute } from '../../utils'; +import { ProductCollectionAttributes } from '../../types'; import DisplaySettingsToolbar from './display-settings-toolbar'; import DisplayLayoutToolbar from './display-layout-toolbar'; import PatternChooserToolbar from './pattern-chooser-toolbar'; diff --git a/assets/js/blocks/product-collection/toolbar-controls/pattern-chooser-toolbar.tsx b/assets/js/blocks/product-collection/edit/toolbar-controls/pattern-chooser-toolbar.tsx similarity index 100% rename from assets/js/blocks/product-collection/toolbar-controls/pattern-chooser-toolbar.tsx rename to assets/js/blocks/product-collection/edit/toolbar-controls/pattern-chooser-toolbar.tsx diff --git a/assets/js/blocks/product-collection/toolbar-controls/pattern-selection-modal.tsx b/assets/js/blocks/product-collection/edit/toolbar-controls/pattern-selection-modal.tsx similarity index 97% rename from assets/js/blocks/product-collection/toolbar-controls/pattern-selection-modal.tsx rename to assets/js/blocks/product-collection/edit/toolbar-controls/pattern-selection-modal.tsx index 2601e7f2fb1..043af3a7a15 100644 --- a/assets/js/blocks/product-collection/toolbar-controls/pattern-selection-modal.tsx +++ b/assets/js/blocks/product-collection/edit/toolbar-controls/pattern-selection-modal.tsx @@ -13,7 +13,7 @@ import { type BlockInstance, cloneBlock } from '@wordpress/blocks'; /** * Internal dependencies */ -import { ProductCollectionQuery } from '../types'; +import { ProductCollectionQuery } from '../../types'; const blockName = 'woocommerce/product-collection'; diff --git a/assets/js/blocks/product-collection/variations/elements/product-summary.tsx b/assets/js/blocks/product-collection/variations/elements/product-summary.tsx index a4effd6b393..99e051cd296 100644 --- a/assets/js/blocks/product-collection/variations/elements/product-summary.tsx +++ b/assets/js/blocks/product-collection/variations/elements/product-summary.tsx @@ -12,9 +12,10 @@ import { BLOCK_DESCRIPTION, BLOCK_TITLE, } from '../../../../atomic/blocks/product-elements/summary/constants'; +import blockJson from '../../block.json'; export const CORE_NAME = 'core/post-excerpt'; -export const VARIATION_NAME = 'woocommerce/product-collection/product-summary'; +export const VARIATION_NAME = `${ blockJson.name }/product-summary`; const registerProductSummary = () => { registerElementVariation( CORE_NAME, { diff --git a/assets/js/blocks/product-collection/variations/elements/product-title.tsx b/assets/js/blocks/product-collection/variations/elements/product-title.tsx index e863cee1a3e..c15aef31fda 100644 --- a/assets/js/blocks/product-collection/variations/elements/product-title.tsx +++ b/assets/js/blocks/product-collection/variations/elements/product-title.tsx @@ -12,9 +12,10 @@ import { BLOCK_DESCRIPTION, BLOCK_TITLE, } from '../../../../atomic/blocks/product-elements/title/constants'; +import blockJson from '../../block.json'; export const CORE_NAME = 'core/post-title'; -export const VARIATION_NAME = 'woocommerce/product-collection/product-title'; +export const VARIATION_NAME = `${ blockJson.name }/product-title`; const registerProductTitle = () => { registerElementVariation( CORE_NAME, { From a2fc38120069c4f2a20e3ccee048fa7eb66dc26e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Dec 2023 16:33:15 +0000 Subject: [PATCH 45/48] Use locale in isAddressComplete (#11888) --- assets/js/base/utils/address.ts | 24 +++++++++++++++++++++--- assets/js/base/utils/test/address.ts | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/assets/js/base/utils/address.ts b/assets/js/base/utils/address.ts index e786f78b4ec..9a41a7e93eb 100644 --- a/assets/js/base/utils/address.ts +++ b/assets/js/base/utils/address.ts @@ -8,6 +8,7 @@ import type { CartResponseShippingAddress, } from '@woocommerce/types'; import { + AddressFields, defaultAddressFields, ShippingAddress, BillingAddress, @@ -93,7 +94,9 @@ export const emptyHiddenAddressFields = < >( address: T ): T => { - const fields = Object.keys( defaultAddressFields ); + const fields = Object.keys( + defaultAddressFields + ) as ( keyof AddressFields )[]; const addressFields = prepareAddressFields( fields, {}, address.country ); const newAddress = Object.assign( {}, address ) as T; @@ -149,10 +152,25 @@ export const formatShippingAddress = ( }; /** - * Returns true if the address has a city and country. + * Checks that all required fields in an address are completed based on the settings in countryLocale. */ export const isAddressComplete = ( address: ShippingAddress | BillingAddress ): boolean => { - return !! address.city && !! address.country; + if ( ! address.country ) { + return false; + } + const fields = Object.keys( + defaultAddressFields + ) as ( keyof AddressFields )[]; + const addressFields = prepareAddressFields( fields, {}, address.country ); + + return addressFields.every( + ( { key = '', hidden = false, required = false } ) => { + if ( hidden || ! required ) { + return true; + } + return isValidAddressKey( key, address ) && address[ key ] !== ''; + } + ); }; diff --git a/assets/js/base/utils/test/address.ts b/assets/js/base/utils/test/address.ts index 27d31eef5b0..6b23d5bfd96 100644 --- a/assets/js/base/utils/test/address.ts +++ b/assets/js/base/utils/test/address.ts @@ -85,6 +85,27 @@ describe( 'isAddressComplete', () => { }; expect( isAddressComplete( address ) ).toBe( true ); } ); + + it( 'correctly checks addresses against country locale', () => { + const address = { + first_name: 'John', + last_name: 'Doe', + company: 'Company', + address_1: '409 Main Street', + address_2: 'Apt 1', + city: 'California', + postcode: '90210', + country: 'US', + state: '', + email: 'john.doe@company', + phone: '+1234567890', + }; + // US address requires state. + expect( isAddressComplete( address ) ).toBe( false ); + + address.state = 'CA'; + expect( isAddressComplete( address ) ).toBe( true ); + } ); } ); describe( 'formatShippingAddress', () => { From 3fb2dfb941b8667f29b3689cbd16605d2079079e Mon Sep 17 00:00:00 2001 From: "Daniel W. Robert" Date: Wed, 6 Dec 2023 15:25:02 -0500 Subject: [PATCH 46/48] Remove `Approved Pull Requests` Workflow (#12017) * Remove ADD_LABEL step for 'status: ready to merge' This updates the workflow to remove the step for adding the 'status: ready to merge' label. Additionally, this adjusts the naming within the job to reflect the change to the workflow. * Remove entire workflow for Approved Pull Requests Once Woo Blocks is merged into the monorepo, this workflow will no longer be needed / relevant at all. --- .github/workflows/approved-with-labels.yml | 25 ---------------------- 1 file changed, 25 deletions(-) delete mode 100644 .github/workflows/approved-with-labels.yml diff --git a/.github/workflows/approved-with-labels.yml b/.github/workflows/approved-with-labels.yml deleted file mode 100644 index 22928c838d4..00000000000 --- a/.github/workflows/approved-with-labels.yml +++ /dev/null @@ -1,25 +0,0 @@ -on: pull_request_review -name: Approved Pull Requests -jobs: - labelWhenApproved: - name: Add Labels - runs-on: ubuntu-latest - steps: - - name: Ready to merge label - uses: pullreminders/label-when-approved-action@master - env: - APPROVALS: '1' - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ADD_LABEL: 'status: ready to merge' - REMOVE_LABEL: 'status:%20needs%20review' - milestoneWhenApproved: - name: Add Milestone - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Latest milestone - uses: woocommerce/automations@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - automations: assign-milestone - milestone_bump_strategy: none From 7ee3f872425295c8453988fde951cc37d886cbd9 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 7 Dec 2023 09:27:57 +0100 Subject: [PATCH 47/48] CYS: Add DELETE private/ai/products endpoint (#12076) * CYS: Add DELETE private/ai/products endpoint * Ensure the hash for the product is also updated on content reset. --------- Co-authored-by: Patricia Hillebrandt --- src/Patterns/ProductUpdater.php | 28 ++++++++++++++++++++++++++ src/StoreApi/Routes/V1/AI/Products.php | 17 ++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Patterns/ProductUpdater.php b/src/Patterns/ProductUpdater.php index 2806e76c12b..e504e160eea 100644 --- a/src/Patterns/ProductUpdater.php +++ b/src/Patterns/ProductUpdater.php @@ -477,4 +477,32 @@ public function assign_ai_generated_content_to_dummy_products( $ai_connection, $ 'product_content' => $products_information_list, ); } + + /** + * Reset the products content. + */ + public function reset_products_content() { + $dummy_products_to_update = $this->fetch_dummy_products_to_update(); + $i = 0; + foreach ( $dummy_products_to_update as $product ) { + $product->set_name( self::DUMMY_PRODUCTS[ $i ]['title'] ); + $product->set_description( self::DUMMY_PRODUCTS[ $i ]['description'] ); + $product->set_regular_price( self::DUMMY_PRODUCTS[ $i ]['price'] ); + $product->set_slug( sanitize_title( self::DUMMY_PRODUCTS[ $i ]['title'] ) ); + $product->save(); + + require_once ABSPATH . 'wp-admin/includes/media.php'; + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/image.php'; + + $product_image_id = media_sideload_image( plugins_url( self::DUMMY_PRODUCTS[ $i ]['image'], dirname( __DIR__ ) ), $product->get_id(), self::DUMMY_PRODUCTS[ $i ]['title'], 'id' ); + $product_image_id = $product->set_image_id( $product_image_id ); + + $product->save(); + + $this->create_hash_for_ai_modified_product( $product ); + + $i++; + } + } } diff --git a/src/StoreApi/Routes/V1/AI/Products.php b/src/StoreApi/Routes/V1/AI/Products.php index 55788115e5b..32048f7114f 100644 --- a/src/StoreApi/Routes/V1/AI/Products.php +++ b/src/StoreApi/Routes/V1/AI/Products.php @@ -57,6 +57,11 @@ public function get_args() { ], ], ], + [ + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => [ $this, 'get_response' ], + 'permission_callback' => [ Middleware::class, 'is_authorized' ], + ], 'schema' => [ $this->schema, 'get_public_item_schema' ], 'allow_batch' => [ 'v1' => true ], ]; @@ -124,4 +129,16 @@ protected function get_route_post_response( \WP_REST_Request $request ) { return rest_ensure_response( $item ); } + + /** + * Remove products generated by AI. + * + * @param \WP_REST_Request $request Request object. + * + * @return bool|string|\WP_Error|\WP_REST_Response + */ + protected function get_route_delete_response( \WP_REST_Request $request ) { + ( new ProductUpdater() )->reset_products_content(); + return rest_ensure_response( array( 'removed' => true ) ); + } } From 22793eb4ae5f8c2fbf19538e708d4b4489c725d2 Mon Sep 17 00:00:00 2001 From: Niels Lange Date: Thu, 7 Dec 2023 15:55:47 +0700 Subject: [PATCH 48/48] Fix font weight of cart totals title in site editor (#12051) * Fix font weight of cart totals title in site editor * Set explicit font-weight for th in cart line items --- .../cart-checkout/cart-line-items-table/style.scss | 3 +++ assets/js/blocks/cart/style.scss | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/assets/js/base/components/cart-checkout/cart-line-items-table/style.scss b/assets/js/base/components/cart-checkout/cart-line-items-table/style.scss index 0bf564b5171..296f3bb8f89 100644 --- a/assets/js/base/components/cart-checkout/cart-line-items-table/style.scss +++ b/assets/js/base/components/cart-checkout/cart-line-items-table/style.scss @@ -26,6 +26,9 @@ table.wc-block-cart-items { width: 100px; text-align: right; } + th { + font-weight: 700; + } } .wc-block-cart-items__row { .wc-block-cart-item__image img { diff --git a/assets/js/blocks/cart/style.scss b/assets/js/blocks/cart/style.scss index 125a7521447..e56d06e1a0d 100644 --- a/assets/js/blocks/cart/style.scss +++ b/assets/js/blocks/cart/style.scss @@ -125,10 +125,14 @@ @include text-heading(); @include font-size( smaller ); display: block; - font-weight: 600; + font-weight: 700; padding: 0.25rem 0; text-align: right; text-transform: uppercase; + + textarea { + font-weight: 700; // Ensure correct font-weight in site-editor. + } } .wc-block-components-sidebar {