diff --git a/assets/js/atomic/blocks/index.js b/assets/js/atomic/blocks/index.js
index 5a1ff791cbd..6745263834d 100644
--- a/assets/js/atomic/blocks/index.js
+++ b/assets/js/atomic/blocks/index.js
@@ -13,3 +13,4 @@ import './product-elements/category-list';
import './product-elements/tag-list';
import './product-elements/stock-indicator';
import './product-elements/add-to-cart';
+import './product-elements/product-image-gallery';
diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json
new file mode 100644
index 00000000000..0a1247cbc34
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/block.json
@@ -0,0 +1,17 @@
+{
+ "name": "woocommerce/product-image-gallery",
+ "version": "1.0.0",
+ "title": "Product Image Gallery",
+ "icon": "gallery",
+ "description": "Display a product's images.",
+ "category": "woocommerce",
+ "supports": {
+ "align": true,
+ "reusable": false
+ },
+ "keywords": [ "WooCommerce" ],
+ "usesContext": [ "postId", "postType", "queryId" ],
+ "textdomain": "woo-gutenberg-products-block",
+ "apiVersion": 2,
+ "$schema": "https://schemas.wp.org/trunk/block.json"
+}
diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx
new file mode 100644
index 00000000000..df6fb8ed95e
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/edit.tsx
@@ -0,0 +1,64 @@
+/**
+ * External dependencies
+ */
+import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings';
+import { isEmptyObject } from '@woocommerce/types';
+import { useBlockProps } from '@wordpress/block-editor';
+import { BlockAttributes } from '@wordpress/blocks';
+import { Disabled } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import './editor.scss';
+
+const Placeholder = () => {
+ return (
+
+
+
+ { [ ...Array( 4 ).keys() ].map( ( index ) => {
+ return (
+
+ );
+ } ) }
+
+
+ );
+};
+
+type Context = {
+ postId: string;
+ postType: string;
+ queryId: string;
+};
+
+interface Props {
+ attributes: BlockAttributes;
+ context: Context;
+}
+
+const Edit = ( { context }: Props ) => {
+ const blockProps = useBlockProps();
+
+ if ( isEmptyObject( context ) ) {
+ return (
+
+ );
+ }
+ // We have work on this case when we will work on the Single Product block.
+ return '';
+};
+
+export default Edit;
diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss
new file mode 100644
index 00000000000..40696afdbba
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/editor.scss
@@ -0,0 +1,13 @@
+.wc-block-editor-product-gallery {
+ img {
+ width: 500px;
+ height: 500px;
+ }
+ .wc-block-editor-product-gallery__other-images {
+ img {
+ width: 100px;
+ height: 100px;
+ margin: 5px;
+ }
+ }
+}
diff --git a/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts
new file mode 100644
index 00000000000..ca81e18eee9
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/product-image-gallery/index.ts
@@ -0,0 +1,26 @@
+/**
+ * External dependencies
+ */
+import { gallery as icon } from '@wordpress/icons';
+import { registerBlockType, unregisterBlockType } from '@wordpress/blocks';
+import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils';
+
+/**
+ * Internal dependencies
+ */
+import edit from './edit';
+import metadata from './block.json';
+
+registerBlockSingleProductTemplate( {
+ registerBlockFn: () => {
+ // @ts-expect-error: `registerBlockType` is a function that is typed in WordPress core.
+ registerBlockType( metadata, {
+ icon,
+ edit,
+ } );
+ },
+ unregisterBlockFn: () => {
+ unregisterBlockType( metadata.name );
+ },
+ blockName: metadata.name,
+} );
diff --git a/assets/js/atomic/utils/index.js b/assets/js/atomic/utils/index.js
index f10a14b6858..06280bb433f 100644
--- a/assets/js/atomic/utils/index.js
+++ b/assets/js/atomic/utils/index.js
@@ -2,3 +2,4 @@ export * from './get-block-map';
export * from './create-blocks-from-template';
export * from './render-parent-block';
export * from './render-standalone-blocks';
+export * from './register-block-single-product-template';
diff --git a/assets/js/atomic/utils/register-block-single-product-template.ts b/assets/js/atomic/utils/register-block-single-product-template.ts
new file mode 100644
index 00000000000..675e335b06d
--- /dev/null
+++ b/assets/js/atomic/utils/register-block-single-product-template.ts
@@ -0,0 +1,46 @@
+/**
+ * External dependencies
+ */
+import { getBlockType } from '@wordpress/blocks';
+import { subscribe, select } from '@wordpress/data';
+
+export const registerBlockSingleProductTemplate = ( {
+ registerBlockFn,
+ unregisterBlockFn,
+ blockName,
+}: {
+ registerBlockFn: () => void;
+ unregisterBlockFn: () => void;
+ blockName: string;
+} ) => {
+ let currentTemplateId: string | undefined;
+
+ subscribe( () => {
+ const previousTemplateId = currentTemplateId;
+ const store = select( 'core/edit-site' );
+ currentTemplateId = store?.getEditedPostId() as string | undefined;
+
+ if ( previousTemplateId === currentTemplateId ) {
+ return;
+ }
+
+ const parsedTemplate = currentTemplateId?.split( '//' )[ 1 ];
+
+ if ( parsedTemplate === null || parsedTemplate === undefined ) {
+ return;
+ }
+
+ const block = getBlockType( blockName );
+
+ if (
+ block === undefined &&
+ parsedTemplate.includes( 'single-product' )
+ ) {
+ registerBlockFn();
+ }
+
+ if ( block !== undefined ) {
+ unregisterBlockFn();
+ }
+ }, 'core/edit-site' );
+};
diff --git a/assets/js/base/components/cart-checkout/address-form/address-form.tsx b/assets/js/base/components/cart-checkout/address-form/address-form.tsx
index 364fd819d9f..3a211ee37fe 100644
--- a/assets/js/base/components/cart-checkout/address-form/address-form.tsx
+++ b/assets/js/base/components/cart-checkout/address-form/address-form.tsx
@@ -22,10 +22,8 @@ import {
ShippingAddress,
} from '@woocommerce/settings';
import { useSelect, useDispatch } from '@wordpress/data';
-import {
- VALIDATION_STORE_KEY,
- FieldValidationStatus,
-} from '@woocommerce/block-data';
+import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
+import { FieldValidationStatus } from '@woocommerce/types';
/**
* Internal dependencies
diff --git a/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx b/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx
index c11a16eb2a4..968192ed6b6 100644
--- a/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx
+++ b/assets/js/base/components/cart-checkout/order-summary/order-summary-item.tsx
@@ -10,10 +10,7 @@ import {
getCurrencyFromPriceResponse,
formatPrice,
} from '@woocommerce/price-format';
-import {
- __experimentalApplyCheckoutFilter,
- mustContain,
-} from '@woocommerce/blocks-checkout';
+import { applyCheckoutFilter, mustContain } from '@woocommerce/blocks-checkout';
import Dinero from 'dinero.js';
import { getSetting } from '@woocommerce/settings';
import { useMemo } from '@wordpress/element';
@@ -52,7 +49,7 @@ const OrderSummaryItem = ( { cartItem }: OrderSummaryProps ): JSX.Element => {
extensions,
} = cartItem;
- // Prepare props to pass to the __experimentalApplyCheckoutFilter filter.
+ // Prepare props to pass to the applyCheckoutFilter filter.
// We need to pluck out receiveCart.
// eslint-disable-next-line no-unused-vars
const { receiveCart, ...cart } = useStoreCart();
@@ -68,7 +65,7 @@ const OrderSummaryItem = ( { cartItem }: OrderSummaryProps ): JSX.Element => {
const priceCurrency = getCurrencyFromPriceResponse( prices );
- const name = __experimentalApplyCheckoutFilter( {
+ const name = applyCheckoutFilter( {
filterName: 'itemName',
defaultValue: initialName,
extensions,
@@ -101,7 +98,7 @@ const OrderSummaryItem = ( { cartItem }: OrderSummaryProps ): JSX.Element => {
amount: lineSubtotal,
precision: totalsCurrency.minorUnit,
} ).getAmount();
- const subtotalPriceFormat = __experimentalApplyCheckoutFilter( {
+ const subtotalPriceFormat = applyCheckoutFilter( {
filterName: 'subtotalPriceFormat',
defaultValue: '',
extensions,
@@ -110,7 +107,7 @@ const OrderSummaryItem = ( { cartItem }: OrderSummaryProps ): JSX.Element => {
} );
// Allow extensions to filter how the price is displayed. Ie: prepending or appending some values.
- const productPriceFormat = __experimentalApplyCheckoutFilter( {
+ const productPriceFormat = applyCheckoutFilter( {
filterName: 'cartItemPrice',
defaultValue: '',
extensions,
@@ -118,7 +115,7 @@ const OrderSummaryItem = ( { cartItem }: OrderSummaryProps ): JSX.Element => {
validation: productPriceValidation,
} );
- const cartItemClassNameFilter = __experimentalApplyCheckoutFilter( {
+ const cartItemClassNameFilter = applyCheckoutFilter( {
filterName: 'cartItemClass',
defaultValue: '',
extensions,
diff --git a/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx b/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx
index 108fb33f25a..204596bffd0 100644
--- a/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx
+++ b/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx
@@ -30,10 +30,9 @@ const ShippingCalculator = ( {
addressFields = [ 'country', 'state', 'city', 'postcode' ],
}: ShippingCalculatorProps ): JSX.Element => {
const { shippingAddress } = useCustomerData();
- const noticeContext = 'wc/cart/shipping-calculator';
return (
-
+
- { __(
- 'Apply a coupon code',
- 'woo-gutenberg-products-block'
- ) }
+ { __( 'Add a coupon', 'woo-gutenberg-products-block' ) }
) }
> => {
- const observerResponses = [];
+): Promise< ObserverResponse[] > => {
+ const observerResponses: ObserverResponse[] = [];
const observersByType = getObserversByPriority( observers, eventType );
for ( const observer of observersByType ) {
try {
const response = await Promise.resolve( observer.callback( data ) );
- if ( typeof response !== 'object' || response === null ) {
+ if ( ! isObserverResponse( response ) ) {
continue;
}
if ( ! response.hasOwnProperty( 'type' ) ) {
@@ -90,7 +93,7 @@ export const emitEventWithAbort = async (
// We don't handle thrown errors but just console.log for troubleshooting.
// eslint-disable-next-line no-console
console.error( e );
- observerResponses.push( { type: 'error' } );
+ observerResponses.push( { type: responseTypes.ERROR } );
return observerResponses;
}
}
diff --git a/assets/js/base/context/event-emit/utils.ts b/assets/js/base/context/event-emit/utils.ts
index 7cb4a5c036e..f9749e2d018 100644
--- a/assets/js/base/context/event-emit/utils.ts
+++ b/assets/js/base/context/event-emit/utils.ts
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { isObject } from '@woocommerce/types';
+import { FieldValidationStatus, isObject } from '@woocommerce/types';
/**
* Internal dependencies
@@ -42,6 +42,16 @@ export interface ResponseType extends Record< string, unknown > {
retry?: boolean;
}
+/**
+ * Observers of checkout/cart events can return a response object to indicate success/error/failure. They may also
+ * optionally pass metadata.
+ */
+export interface ObserverResponse {
+ type: responseTypes;
+ meta?: Record< string, unknown > | undefined;
+ validationErrors?: Record< string, FieldValidationStatus > | undefined;
+}
+
const isResponseOf = (
response: unknown,
type: string
@@ -51,19 +61,27 @@ const isResponseOf = (
export const isSuccessResponse = (
response: unknown
-): response is ResponseType => {
+): response is ObserverFailResponse => {
return isResponseOf( response, responseTypes.SUCCESS );
};
-
+interface ObserverSuccessResponse extends ObserverResponse {
+ type: responseTypes.SUCCESS;
+}
export const isErrorResponse = (
response: unknown
-): response is ResponseType => {
+): response is ObserverSuccessResponse => {
return isResponseOf( response, responseTypes.ERROR );
};
+interface ObserverErrorResponse extends ObserverResponse {
+ type: responseTypes.ERROR;
+}
+interface ObserverFailResponse extends ObserverResponse {
+ type: responseTypes.FAIL;
+}
export const isFailResponse = (
response: unknown
-): response is ResponseType => {
+): response is ObserverErrorResponse => {
return isResponseOf( response, responseTypes.FAIL );
};
diff --git a/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts b/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts
index 2923fb7e031..81a25490dc4 100644
--- a/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts
+++ b/assets/js/base/context/hooks/cart/use-store-cart-coupons.ts
@@ -6,7 +6,7 @@ import { useDispatch, useSelect } from '@wordpress/data';
import { CART_STORE_KEY, VALIDATION_STORE_KEY } from '@woocommerce/block-data';
import { decodeEntities } from '@wordpress/html-entities';
import type { StoreCartCoupon } from '@woocommerce/types';
-import { __experimentalApplyCheckoutFilter } from '@woocommerce/blocks-checkout';
+import { applyCheckoutFilter } from '@woocommerce/blocks-checkout';
/**
* Internal dependencies
@@ -46,7 +46,7 @@ export const useStoreCartCoupons = ( context = '' ): StoreCartCoupon => {
return applyCoupon( couponCode )
.then( () => {
if (
- __experimentalApplyCheckoutFilter( {
+ applyCheckoutFilter( {
filterName: 'showApplyCouponNotice',
defaultValue: true,
arg: { couponCode, context },
@@ -86,7 +86,7 @@ export const useStoreCartCoupons = ( context = '' ): StoreCartCoupon => {
return removeCoupon( couponCode )
.then( () => {
if (
- __experimentalApplyCheckoutFilter( {
+ applyCheckoutFilter( {
filterName: 'showRemoveCouponNotice',
defaultValue: true,
arg: { couponCode, context },
diff --git a/assets/js/base/context/hooks/payment-methods/use-payment-method-interface.ts b/assets/js/base/context/hooks/payment-methods/use-payment-method-interface.ts
index e8e2b720eb2..0a8ae83c3a8 100644
--- a/assets/js/base/context/hooks/payment-methods/use-payment-method-interface.ts
+++ b/assets/js/base/context/hooks/payment-methods/use-payment-method-interface.ts
@@ -60,13 +60,46 @@ export const usePaymentMethodInterface = (): PaymentMethodInterface => {
return {
// The paymentStatus is exposed to third parties via the payment method interface so the API must not be changed
paymentStatus: {
- isPristine: store.isPaymentPristine(),
- isStarted: store.isPaymentStarted(),
+ get isPristine() {
+ deprecated( 'isPristine', {
+ since: '9.6.0',
+ alternative: 'isIdle',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return store.isPaymentIdle();
+ }, // isPristine is the same as isIdle
+ isIdle: store.isPaymentIdle(),
+ isStarted: store.isExpressPaymentStarted(),
isProcessing: store.isPaymentProcessing(),
- isFinished: store.isPaymentFinished(),
+ get isFinished() {
+ deprecated( 'isFinished', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return (
+ store.hasPaymentError() || store.isPaymentReady()
+ );
+ },
hasError: store.hasPaymentError(),
- hasFailed: store.isPaymentFailed(),
- isSuccessful: store.isPaymentSuccess(),
+ get hasFailed() {
+ deprecated( 'hasFailed', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return store.hasPaymentError();
+ },
+ get isSuccessful() {
+ deprecated( 'isSuccessful', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return store.isPaymentReady();
+ },
+ isReady: store.isPaymentReady(),
isDoingExpressPayment: store.isExpressPaymentMethodActive(),
},
activePaymentMethod: store.getActivePaymentMethod(),
diff --git a/assets/js/base/context/providers/cart-checkout/checkout-processor.ts b/assets/js/base/context/providers/cart-checkout/checkout-processor.ts
index 7024107f250..0428c215b5b 100644
--- a/assets/js/base/context/providers/cart-checkout/checkout-processor.ts
+++ b/assets/js/base/context/providers/cart-checkout/checkout-processor.ts
@@ -92,7 +92,7 @@ const CheckoutProcessor = () => {
paymentMethodData,
isExpressPaymentMethodActive,
hasPaymentError,
- isPaymentSuccess,
+ isPaymentReady,
shouldSavePayment,
} = useSelect( ( select ) => {
const store = select( PAYMENT_STORE_KEY );
@@ -102,7 +102,7 @@ const CheckoutProcessor = () => {
paymentMethodData: store.getPaymentMethodData(),
isExpressPaymentMethodActive: store.isExpressPaymentMethodActive(),
hasPaymentError: store.hasPaymentError(),
- isPaymentSuccess: store.isPaymentSuccess(),
+ isPaymentReady: store.isPaymentReady(),
shouldSavePayment: store.getShouldSavePaymentMethod(),
};
}, [] );
@@ -130,7 +130,7 @@ const CheckoutProcessor = () => {
const paidAndWithoutErrors =
! checkoutHasError &&
! checkoutWillHaveError &&
- ( isPaymentSuccess || ! cartNeedsPayment ) &&
+ ( isPaymentReady || ! cartNeedsPayment ) &&
checkoutIsProcessing;
// Determine if checkout has an error.
diff --git a/assets/js/base/context/providers/cart-checkout/payment-events/index.tsx b/assets/js/base/context/providers/cart-checkout/payment-events/index.tsx
index 10a38b2cea2..d9b61713597 100644
--- a/assets/js/base/context/providers/cart-checkout/payment-events/index.tsx
+++ b/assets/js/base/context/providers/cart-checkout/payment-events/index.tsx
@@ -61,16 +61,18 @@ export const PaymentEventsProvider = ( {
isCalculating: store.isCalculating(),
};
} );
- const { isPaymentSuccess, isPaymentFinished, isPaymentProcessing } =
- useSelect( ( select ) => {
- const store = select( PAYMENT_STORE_KEY );
+ const { isPaymentReady } = useSelect( ( select ) => {
+ const store = select( PAYMENT_STORE_KEY );
- return {
- isPaymentSuccess: store.isPaymentSuccess(),
- isPaymentFinished: store.isPaymentFinished(),
- isPaymentProcessing: store.isPaymentProcessing(),
- };
- } );
+ return {
+ // The PROCESSING status represents befor the checkout runs the observers
+ // registered for the payment_setup event.
+ isPaymentProcessing: store.isPaymentProcessing(),
+ // the READY status represents when the observers have finished processing and payment data
+ // synced with the payment store, ready to be sent to the StoreApi
+ isPaymentReady: store.isPaymentReady(),
+ };
+ } );
const { setValidationErrors } = useDispatch( VALIDATION_STORE_KEY );
const [ observers, observerDispatch ] = useReducer( emitReducer, {} );
@@ -84,59 +86,50 @@ export const PaymentEventsProvider = ( {
const {
__internalSetPaymentProcessing,
- __internalSetPaymentPristine,
+ __internalSetPaymentIdle,
__internalEmitPaymentProcessingEvent,
} = useDispatch( PAYMENT_STORE_KEY );
- // flip payment to processing if checkout processing is complete, there are no errors, and payment status is started.
+ // flip payment to processing if checkout processing is complete and there are no errors
useEffect( () => {
if (
checkoutIsProcessing &&
! checkoutHasError &&
- ! checkoutIsCalculating &&
- ! isPaymentFinished
+ ! checkoutIsCalculating
) {
__internalSetPaymentProcessing();
+
+ // Note: the nature of this event emitter is that it will bail on any
+ // observer that returns a response that !== true. However, this still
+ // allows for other observers that return true for continuing through
+ // to the next observer (or bailing if there's a problem).
+ __internalEmitPaymentProcessingEvent(
+ currentObservers.current,
+ setValidationErrors
+ );
}
}, [
checkoutIsProcessing,
checkoutHasError,
checkoutIsCalculating,
- isPaymentFinished,
__internalSetPaymentProcessing,
+ __internalEmitPaymentProcessingEvent,
+ setValidationErrors,
] );
- // When checkout is returned to idle, set payment status to pristine but only if payment status is already not finished.
+ // When checkout is returned to idle, and the payment setup has not completed, set payment status to idle
useEffect( () => {
- if ( checkoutIsIdle && ! isPaymentSuccess ) {
- __internalSetPaymentPristine();
+ if ( checkoutIsIdle && ! isPaymentReady ) {
+ __internalSetPaymentIdle();
}
- }, [ checkoutIsIdle, isPaymentSuccess, __internalSetPaymentPristine ] );
+ }, [ checkoutIsIdle, isPaymentReady, __internalSetPaymentIdle ] );
- // if checkout has an error sync payment status back to pristine.
+ // if checkout has an error sync payment status back to idle.
useEffect( () => {
- if ( checkoutHasError && isPaymentSuccess ) {
- __internalSetPaymentPristine();
+ if ( checkoutHasError && isPaymentReady ) {
+ __internalSetPaymentIdle();
}
- }, [ checkoutHasError, isPaymentSuccess, __internalSetPaymentPristine ] );
-
- // Emit the payment processing event
- useEffect( () => {
- // Note: the nature of this event emitter is that it will bail on any
- // observer that returns a response that !== true. However, this still
- // allows for other observers that return true for continuing through
- // to the next observer (or bailing if there's a problem).
- if ( isPaymentProcessing ) {
- __internalEmitPaymentProcessingEvent(
- currentObservers.current,
- setValidationErrors
- );
- }
- }, [
- isPaymentProcessing,
- setValidationErrors,
- __internalEmitPaymentProcessingEvent,
- ] );
+ }, [ checkoutHasError, isPaymentReady, __internalSetPaymentIdle ] );
const paymentContextData = {
onPaymentProcessing,
diff --git a/assets/js/base/context/tsconfig.json b/assets/js/base/context/tsconfig.json
deleted file mode 100644
index 78b3b4d4549..00000000000
--- a/assets/js/base/context/tsconfig.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "extends": "../../../../tsconfig.base.json",
- "compilerOptions": {},
- "include": [
- ".",
- "../../blocks-registry/index.js",
- "../../settings/shared/index.ts",
- "../../settings/blocks/index.ts",
- "../../base/hooks/index.js",
- "../../base/utils/",
- "../../utils",
- "../../data/",
- "../../types/",
- "../components",
- "../../blocks/cart-checkout-shared/payment-methods",
- "../../settings/shared/default-address-fields.ts"
- ],
- "exclude": [ "**/test/**" ]
-}
diff --git a/assets/js/blocks/breadcrumbs/style.scss b/assets/js/blocks/breadcrumbs/style.scss
index a455e9008dc..bc01b5b8b10 100644
--- a/assets/js/blocks/breadcrumbs/style.scss
+++ b/assets/js/blocks/breadcrumbs/style.scss
@@ -1,3 +1,9 @@
.woocommerce.wc-block-breadcrumbs {
font-size: inherit;
}
+.woocommerce.woocommerce-shop .wc-block-breadcrumbs {
+ .woocommerce-breadcrumb {
+ margin: auto;
+ display: block;
+ }
+}
diff --git a/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js b/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js
index c8250967220..c30fa050be1 100644
--- a/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js
+++ b/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js
@@ -36,8 +36,8 @@ const ExpressPaymentMethods = () => {
);
const {
__internalSetActivePaymentMethod,
- __internalSetPaymentStarted,
- __internalSetPaymentPristine,
+ __internalSetExpressPaymentStarted,
+ __internalSetPaymentIdle,
__internalSetPaymentError,
__internalSetPaymentMethodData,
__internalSetExpressPaymentError,
@@ -58,14 +58,14 @@ const ExpressPaymentMethods = () => {
( paymentMethodId ) => () => {
previousActivePaymentMethod.current = activePaymentMethod;
previousPaymentMethodData.current = paymentMethodData;
- __internalSetPaymentStarted();
+ __internalSetExpressPaymentStarted();
__internalSetActivePaymentMethod( paymentMethodId );
},
[
activePaymentMethod,
paymentMethodData,
__internalSetActivePaymentMethod,
- __internalSetPaymentStarted,
+ __internalSetExpressPaymentStarted,
]
);
@@ -75,12 +75,12 @@ const ExpressPaymentMethods = () => {
* This restores the active method and returns the state to pristine.
*/
const onExpressPaymentClose = useCallback( () => {
- __internalSetPaymentPristine();
+ __internalSetPaymentIdle();
__internalSetActivePaymentMethod(
previousActivePaymentMethod.current,
previousPaymentMethodData.current
);
- }, [ __internalSetActivePaymentMethod, __internalSetPaymentPristine ] );
+ }, [ __internalSetActivePaymentMethod, __internalSetPaymentIdle ] );
/**
* onExpressPaymentError should be triggered when the express payment process errors.
diff --git a/assets/js/blocks/cart/cart-cross-sells-product-list/cart-cross-sells-product.tsx b/assets/js/blocks/cart/cart-cross-sells-product-list/cart-cross-sells-product.tsx
index 2ac0d9a33c6..ba67ed5d74e 100644
--- a/assets/js/blocks/cart/cart-cross-sells-product-list/cart-cross-sells-product.tsx
+++ b/assets/js/blocks/cart/cart-cross-sells-product-list/cart-cross-sells-product.tsx
@@ -49,7 +49,7 @@ const CartCrossSellsProduct = ( {
/>
diff --git a/assets/js/blocks/cart/cart-line-items-table/cart-line-item-row.tsx b/assets/js/blocks/cart/cart-line-items-table/cart-line-item-row.tsx
index 5ecfb80d399..fdf3976bdcf 100644
--- a/assets/js/blocks/cart/cart-line-items-table/cart-line-item-row.tsx
+++ b/assets/js/blocks/cart/cart-line-items-table/cart-line-item-row.tsx
@@ -20,10 +20,7 @@ import {
ProductSaleBadge,
} from '@woocommerce/base-components/cart-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
-import {
- __experimentalApplyCheckoutFilter,
- mustContain,
-} from '@woocommerce/blocks-checkout';
+import { applyCheckoutFilter, mustContain } from '@woocommerce/blocks-checkout';
import Dinero from 'dinero.js';
import { forwardRef, useMemo } from '@wordpress/element';
import type { CartItem } from '@woocommerce/types';
@@ -118,7 +115,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
useStoreCartItemQuantity( lineItem );
const { dispatchStoreEvent } = useStoreEvents();
- // Prepare props to pass to the __experimentalApplyCheckoutFilter filter.
+ // Prepare props to pass to the applyCheckoutFilter filter.
// We need to pluck out receiveCart.
// eslint-disable-next-line no-unused-vars
const { receiveCart, ...cart } = useStoreCart();
@@ -131,7 +128,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
[ lineItem, cart ]
);
const priceCurrency = getCurrencyFromPriceResponse( prices );
- const name = __experimentalApplyCheckoutFilter( {
+ const name = applyCheckoutFilter( {
filterName: 'itemName',
defaultValue: initialName,
extensions,
@@ -163,7 +160,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
const isProductHiddenFromCatalog =
catalogVisibility === 'hidden' || catalogVisibility === 'search';
- const cartItemClassNameFilter = __experimentalApplyCheckoutFilter( {
+ const cartItemClassNameFilter = applyCheckoutFilter( {
filterName: 'cartItemClass',
defaultValue: '',
extensions,
@@ -171,7 +168,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
} );
// Allow extensions to filter how the price is displayed. Ie: prepending or appending some values.
- const productPriceFormat = __experimentalApplyCheckoutFilter( {
+ const productPriceFormat = applyCheckoutFilter( {
filterName: 'cartItemPrice',
defaultValue: '',
extensions,
@@ -179,7 +176,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
validation: productPriceValidation,
} );
- const subtotalPriceFormat = __experimentalApplyCheckoutFilter( {
+ const subtotalPriceFormat = applyCheckoutFilter( {
filterName: 'subtotalPriceFormat',
defaultValue: '',
extensions,
@@ -187,7 +184,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
validation: productPriceValidation,
} );
- const saleBadgePriceFormat = __experimentalApplyCheckoutFilter( {
+ const saleBadgePriceFormat = applyCheckoutFilter( {
filterName: 'saleBadgePriceFormat',
defaultValue: '',
extensions,
@@ -195,7 +192,7 @@ const CartLineItemRow: React.ForwardRefExoticComponent<
validation: productPriceValidation,
} );
- const showRemoveItemLink = __experimentalApplyCheckoutFilter( {
+ const showRemoveItemLink = applyCheckoutFilter( {
filterName: 'showRemoveItemLink',
defaultValue: true,
extensions,
diff --git a/assets/js/blocks/cart/inner-blocks/cart-cross-sells-block/edit.tsx b/assets/js/blocks/cart/inner-blocks/cart-cross-sells-block/edit.tsx
index 25d8a8a9b20..67c15660241 100644
--- a/assets/js/blocks/cart/inner-blocks/cart-cross-sells-block/edit.tsx
+++ b/assets/js/blocks/cart/inner-blocks/cart-cross-sells-block/edit.tsx
@@ -17,9 +17,9 @@ export const Edit = (): JSX.Element => {
'You may be interested in…',
'woo-gutenberg-products-block'
),
- level: 3,
+ level: 2,
+ fontSize: 'large',
},
- ,
[],
],
[ 'woocommerce/cart-cross-sells-products-block', {}, [] ],
diff --git a/assets/js/blocks/cart/inner-blocks/cart-order-summary-heading/edit.tsx b/assets/js/blocks/cart/inner-blocks/cart-order-summary-heading/edit.tsx
index 2b9c7e08b6a..ee105b65b6d 100644
--- a/assets/js/blocks/cart/inner-blocks/cart-order-summary-heading/edit.tsx
+++ b/assets/js/blocks/cart/inner-blocks/cart-order-summary-heading/edit.tsx
@@ -37,6 +37,7 @@ export const Edit = ( {
onChange={ ( value ) =>
setAttributes( { content: value } )
}
+ style={ { backgroundColor: 'transparent' } }
/>
diff --git a/assets/js/blocks/cart/inner-blocks/empty-cart-block/edit.tsx b/assets/js/blocks/cart/inner-blocks/empty-cart-block/edit.tsx
index 7b7a909b23c..d55c95784ea 100644
--- a/assets/js/blocks/cart/inner-blocks/empty-cart-block/edit.tsx
+++ b/assets/js/blocks/cart/inner-blocks/empty-cart-block/edit.tsx
@@ -15,7 +15,6 @@ import {
useForcedLayout,
getAllowedBlocks,
} from '../../../cart-checkout-shared';
-import iconDataUri from './icon-data-uri.js';
import './style.scss';
const browseStoreTemplate = SHOP_URL
@@ -26,7 +25,7 @@ const browseStoreTemplate = SHOP_URL
content: sprintf(
/* translators: %s is the link to the store product directory. */
__(
- 'Browse store.',
+ 'Browse store',
'woo-gutenberg-products-block'
),
SHOP_URL
@@ -37,14 +36,6 @@ const browseStoreTemplate = SHOP_URL
: null;
const defaultTemplate = [
- [
- 'core/image',
- {
- align: 'center',
- url: iconDataUri,
- sizeSlug: 'small',
- },
- ],
[
'core/heading',
{
@@ -54,7 +45,7 @@ const defaultTemplate = [
'woo-gutenberg-products-block'
),
level: 2,
- className: 'wc-block-cart__empty-cart__title',
+ className: 'with-empty-cart-icon wc-block-cart__empty-cart__title',
},
],
browseStoreTemplate,
diff --git a/assets/js/blocks/cart/inner-blocks/empty-cart-block/icon-data-uri.js b/assets/js/blocks/cart/inner-blocks/empty-cart-block/icon-data-uri.js
deleted file mode 100644
index d5082ec4a4e..00000000000
--- a/assets/js/blocks/cart/inner-blocks/empty-cart-block/icon-data-uri.js
+++ /dev/null
@@ -1 +0,0 @@
-export default 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzgiIGhlaWdodD0iMzgiIHZpZXdCb3g9IjAgMCAzOCAzOCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5IDBDOC41MDQwMyAwIDAgOC41MDQwMyAwIDE5QzAgMjkuNDk2IDguNTA0MDMgMzggMTkgMzhDMjkuNDk2IDM4IDM4IDI5LjQ5NiAzOCAxOUMzOCA4LjUwNDAzIDI5LjQ5NiAwIDE5IDBaTTI1LjEyOSAxMi44NzFDMjYuNDg1MSAxMi44NzEgMjcuNTgwNiAxMy45NjY1IDI3LjU4MDYgMTUuMzIyNkMyNy41ODA2IDE2LjY3ODYgMjYuNDg1MSAxNy43NzQyIDI1LjEyOSAxNy43NzQyQzIzLjc3MyAxNy43NzQyIDIyLjY3NzQgMTYuNjc4NiAyMi42Nzc0IDE1LjMyMjZDMjIuNjc3NCAxMy45NjY1IDIzLjc3MyAxMi44NzEgMjUuMTI5IDEyLjg3MVpNMTEuNjQ1MiAzMS4yNTgxQzkuNjE0OTIgMzEuMjU4MSA3Ljk2Nzc0IDI5LjY0OTIgNy45Njc3NCAyNy42NTczQzcuOTY3NzQgMjYuMTI1IDEwLjE1MTIgMjMuMDI5OCAxMS4xNTQ4IDIxLjY5NjhDMTEuNCAyMS4zNjczIDExLjg5MDMgMjEuMzY3MyAxMi4xMzU1IDIxLjY5NjhDMTMuMTM5MSAyMy4wMjk4IDE1LjMyMjYgMjYuMTI1IDE1LjMyMjYgMjcuNjU3M0MxNS4zMjI2IDI5LjY0OTIgMTMuNjc1NCAzMS4yNTgxIDExLjY0NTIgMzEuMjU4MVpNMTIuODcxIDE3Ljc3NDJDMTEuNTE0OSAxNy43NzQyIDEwLjQxOTQgMTYuNjc4NiAxMC40MTk0IDE1LjMyMjZDMTAuNDE5NCAxMy45NjY1IDExLjUxNDkgMTIuODcxIDEyLjg3MSAxMi44NzFDMTQuMjI3IDEyLjg3MSAxNS4zMjI2IDEzLjk2NjUgMTUuMzIyNiAxNS4zMjI2QzE1LjMyMjYgMTYuNjc4NiAxNC4yMjcgMTcuNzc0MiAxMi44NzEgMTcuNzc0MlpNMjUuOTEwNSAyOS41ODc5QzI0LjE5NDQgMjcuNTM0NyAyMS42NzM4IDI2LjM1NDggMTkgMjYuMzU0OEMxNy4zNzU4IDI2LjM1NDggMTcuMzc1OCAyMy45MDMyIDE5IDIzLjkwMzJDMjIuNDAxNiAyMy45MDMyIDI1LjYxMTcgMjUuNDA0OCAyNy43ODc1IDI4LjAyNUMyOC44NDQ4IDI5LjI4MTUgMjYuOTI5NCAzMC44MjE0IDI1LjkxMDUgMjkuNTg3OVoiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=';
diff --git a/assets/js/blocks/cart/inner-blocks/empty-cart-block/style.scss b/assets/js/blocks/cart/inner-blocks/empty-cart-block/style.scss
index e0c402f6ee1..1c96b67ef01 100644
--- a/assets/js/blocks/cart/inner-blocks/empty-cart-block/style.scss
+++ b/assets/js/blocks/cart/inner-blocks/empty-cart-block/style.scss
@@ -2,3 +2,21 @@
.editor-styles-wrapper .wc-block-cart__empty-cart__title {
font-size: inherit;
}
+.wc-block-cart__empty-cart__title.with-empty-cart-icon {
+ &::before {
+ content: "";
+ background-color: currentColor;
+ display: block;
+ margin: 0 auto 2em;
+ mask-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOCIgaGVpZ2h0PSIzOCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMTkgMEM4LjUwNCAwIDAgOC41MDQgMCAxOXM4LjUwNCAxOSAxOSAxOSAxOS04LjUwNCAxOS0xOVMyOS40OTYgMCAxOSAwWm02LjEyOSAxMi44NzFhMi40NDkgMi40NDkgMCAwIDEgMi40NTIgMi40NTIgMi40NDkgMi40NDkgMCAwIDEtMi40NTIgMi40NTEgMi40NDkgMi40NDkgMCAwIDEtMi40NTItMi40NTEgMi40NDkgMi40NDkgMCAwIDEgMi40NTItMi40NTJaTTExLjY0NSAzMS4yNThjLTIuMDMgMC0zLjY3Ny0xLjYwOS0zLjY3Ny0zLjYgMC0xLjUzMyAyLjE4My00LjYyOCAzLjE4Ny01Ljk2MWEuNjEuNjEgMCAwIDEgLjk4IDBjMS4wMDQgMS4zMzMgMy4xODggNC40MjggMy4xODggNS45NiAwIDEuOTkyLTEuNjQ4IDMuNjAxLTMuNjc4IDMuNjAxWm0xLjIyNi0xMy40ODRhMi40NDkgMi40NDkgMCAwIDEtMi40NTItMi40NTEgMi40NDkgMi40NDkgMCAwIDEgMi40NTItMi40NTIgMi40NDkgMi40NDkgMCAwIDEgMi40NTIgMi40NTIgMi40NDkgMi40NDkgMCAwIDEtMi40NTIgMi40NTFabTEzLjA0IDExLjgxNEE4Ljk4OSA4Ljk4OSAwIDAgMCAxOSAyNi4zNTVjLTEuNjI0IDAtMS42MjQtMi40NTIgMC0yLjQ1MiAzLjQwMiAwIDYuNjEyIDEuNTAyIDguNzg4IDQuMTIyIDEuMDU3IDEuMjU3LS44NTkgMi43OTYtMS44NzggMS41NjNaIi8+PC9zdmc+);
+ mask-position: center;
+ mask-repeat: no-repeat;
+ mask-size: 5em;
+ width: 5em;
+ height: 5em;
+ }
+}
+.wp-block-woocommerce-empty-cart-block > .aligncenter {
+ margin-left: auto !important;
+ margin-right: auto !important;
+}
diff --git a/assets/js/blocks/cart/test/block.js b/assets/js/blocks/cart/test/block.js
index 14ba47dc156..3eea7abf2fa 100644
--- a/assets/js/blocks/cart/test/block.js
+++ b/assets/js/blocks/cart/test/block.js
@@ -6,7 +6,7 @@ import { previewCart } from '@woocommerce/resource-previews';
import { dispatch } from '@wordpress/data';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
import { default as fetchMock } from 'jest-fetch-mock';
-import { __experimentalRegisterCheckoutFilters } from '@woocommerce/blocks-checkout';
+import { registerCheckoutFilters } from '@woocommerce/blocks-checkout';
/**
* Internal dependencies
@@ -240,7 +240,7 @@ describe( 'Testing cart', () => {
items: [ previewCart.items[ 0 ] ],
};
- __experimentalRegisterCheckoutFilters( 'woo-blocks-test-extension', {
+ registerCheckoutFilters( 'woo-blocks-test-extension', {
showRemoveItemLink: ( value, extensions, { cartItem } ) => {
return cartItem.id !== cart.items[ 0 ].id;
},
diff --git a/assets/js/blocks/catalog-sorting/style.scss b/assets/js/blocks/catalog-sorting/style.scss
index 85ecc5acf2f..5be096ed2ab 100644
--- a/assets/js/blocks/catalog-sorting/style.scss
+++ b/assets/js/blocks/catalog-sorting/style.scss
@@ -7,4 +7,8 @@
font-size: inherit;
color: inherit;
}
+
+ .woocommerce-ordering {
+ margin: auto;
+ }
}
diff --git a/assets/js/blocks/checkout/block.tsx b/assets/js/blocks/checkout/block.tsx
index 1cce8fc71d0..18d1a0ac174 100644
--- a/assets/js/blocks/checkout/block.tsx
+++ b/assets/js/blocks/checkout/block.tsx
@@ -183,8 +183,9 @@ const Block = ( {
) }
showErrorMessage={ CURRENT_USER_IS_ADMIN }
>
-
-
+
{ /* SlotFillProvider need to be defined before CheckoutProvider so fills have the SlotFill context ready when they mount. */ }
diff --git a/assets/js/blocks/checkout/form-step/form-step-block.tsx b/assets/js/blocks/checkout/form-step/form-step-block.tsx
index d4eaee87de3..d20d806df96 100644
--- a/assets/js/blocks/checkout/form-step/form-step-block.tsx
+++ b/assets/js/blocks/checkout/form-step/form-step-block.tsx
@@ -66,6 +66,7 @@ export const FormStepBlock = ( {
className={ '' }
value={ title }
onChange={ ( value ) => setAttributes( { title: value } ) }
+ style={ { backgroundColor: 'transparent' } }
/>
@@ -86,6 +87,7 @@ export const FormStepBlock = ( {
description: value,
} )
}
+ style={ { backgroundColor: 'transparent' } }
/>
diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx
index 4ad6356032a..b4c36a071fb 100644
--- a/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx
+++ b/assets/js/blocks/checkout/inner-blocks/checkout-actions-block/block.tsx
@@ -11,7 +11,7 @@ import { useCheckoutSubmit } from '@woocommerce/base-context/hooks';
import { noticeContexts } from '@woocommerce/base-context';
import {
StoreNoticesContainer,
- __experimentalApplyCheckoutFilter,
+ applyCheckoutFilter,
} from '@woocommerce/blocks-checkout';
/**
@@ -32,7 +32,7 @@ const Block = ( {
placeOrderButtonLabel: string;
} ): JSX.Element => {
const { paymentMethodButtonLabel } = useCheckoutSubmit();
- const label = __experimentalApplyCheckoutFilter( {
+ const label = applyCheckoutFilter( {
filterName: 'placeOrderButtonLabel',
defaultValue:
paymentMethodButtonLabel ||
diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx
index 3d42b8ebc6a..1d372046da0 100644
--- a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx
+++ b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx
@@ -88,10 +88,13 @@ const Block = ( {
] ) as Record< keyof AddressFields, Partial< AddressField > >;
const AddressFormWrapperComponent = isEditor ? Noninteractive : Fragment;
+ const noticeContext = useBillingAsShipping
+ ? [ noticeContexts.BILLING_ADDRESS, noticeContexts.SHIPPING_ADDRESS ]
+ : [ noticeContexts.BILLING_ADDRESS ];
return (
-
+
{
setUseStoreCartReturnValue();
const { container } = render( );
expect(
- await findByText( container, 'Apply a coupon code' )
+ await findByText( container, 'Add a coupon' )
).toBeInTheDocument();
} );
diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx
index 891e962ebc9..e1fdfa98f02 100644
--- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx
+++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx
@@ -97,13 +97,14 @@ const Block = ( {
] ) as Record< keyof AddressFields, Partial< AddressField > >;
const AddressFormWrapperComponent = isEditor ? Noninteractive : Fragment;
+ const noticeContext = useShippingAsBilling
+ ? [ noticeContexts.SHIPPING_ADDRESS, noticeContexts.BILLING_ADDRESS ]
+ : [ noticeContexts.SHIPPING_ADDRESS ];
return (
<>
-
+
{
) }
- {
- // Hacky temporary solution to display the feedback prompt
- // at the bottom of the inspector controls
- }
-
-
-
>
);
};
@@ -226,6 +219,9 @@ export const withProductQueryControls =
<>
+
+
+
>
) : (
diff --git a/assets/js/blocks/product-query/variations/product-query.tsx b/assets/js/blocks/product-query/variations/product-query.tsx
index 73362b8bf0d..b5804d87d17 100644
--- a/assets/js/blocks/product-query/variations/product-query.tsx
+++ b/assets/js/blocks/product-query/variations/product-query.tsx
@@ -1,11 +1,16 @@
/**
* External dependencies
*/
-import { registerBlockVariation } from '@wordpress/blocks';
+import {
+ registerBlockVariation,
+ unregisterBlockVariation,
+} from '@wordpress/blocks';
import { Icon } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { stacks } from '@woocommerce/icons';
import { isWpVersion } from '@woocommerce/settings';
+import { select, subscribe } from '@wordpress/data';
+import { QueryBlockAttributes } from '@woocommerce/blocks/product-query/types';
/**
* Internal dependencies
@@ -19,7 +24,15 @@ import {
const VARIATION_NAME = 'woocommerce/product-query';
-if ( isWpVersion( '6.1', '>=' ) ) {
+const ARCHIVE_PRODUCT_TEMPLATES = [
+ 'woocommerce/woocommerce//archive-product',
+ 'woocommerce/woocommerce//taxonomy-product_cat',
+ 'woocommerce/woocommerce//taxonomy-product_tag',
+ 'woocommerce/woocommerce//taxonomy-product_attribute',
+ 'woocommerce/woocommerce//product-search-results',
+];
+
+const registerProductsBlock = ( attributes: QueryBlockAttributes ) => {
registerBlockVariation( QUERY_LOOP_ID, {
description: __(
'A block that displays a selection of products in your store.',
@@ -37,7 +50,7 @@ if ( isWpVersion( '6.1', '>=' ) ) {
/>
),
attributes: {
- ...QUERY_DEFAULT_ATTRIBUTES,
+ ...attributes,
namespace: VARIATION_NAME,
},
// Gutenberg doesn't support this type yet, discussion here:
@@ -48,4 +61,37 @@ if ( isWpVersion( '6.1', '>=' ) ) {
innerBlocks: INNER_BLOCKS_TEMPLATE,
scope: [ 'inserter' ],
} );
+};
+
+if ( isWpVersion( '6.1', '>=' ) ) {
+ const store = select( 'core/edit-site' );
+
+ if ( store ) {
+ let currentTemplateId: string | undefined;
+
+ subscribe( () => {
+ const previousTemplateId = currentTemplateId;
+
+ currentTemplateId = store?.getEditedPostId();
+
+ if ( previousTemplateId === currentTemplateId ) {
+ return;
+ }
+
+ const queryAttributes = {
+ ...QUERY_DEFAULT_ATTRIBUTES,
+ query: {
+ ...QUERY_DEFAULT_ATTRIBUTES.query,
+ inherit:
+ ARCHIVE_PRODUCT_TEMPLATES.includes( currentTemplateId ),
+ },
+ };
+
+ unregisterBlockVariation( QUERY_LOOP_ID, VARIATION_NAME );
+
+ registerProductsBlock( queryAttributes );
+ } );
+ } else {
+ registerProductsBlock( QUERY_DEFAULT_ATTRIBUTES );
+ }
}
diff --git a/assets/js/blocks/product-results-count/style.scss b/assets/js/blocks/product-results-count/style.scss
index 5ed82602ca6..54c9cfe3de8 100644
--- a/assets/js/blocks/product-results-count/style.scss
+++ b/assets/js/blocks/product-results-count/style.scss
@@ -5,5 +5,8 @@
.woocommerce-result-count {
float: none;
font-size: inherit;
+ // reset for margin
+ margin: auto;
}
}
+
diff --git a/assets/js/blocks/product-search/edit.js b/assets/js/blocks/product-search/edit.js
index c881d261b50..74d0530e02a 100644
--- a/assets/js/blocks/product-search/edit.js
+++ b/assets/js/blocks/product-search/edit.js
@@ -86,6 +86,7 @@ const Edit = ( {
onChange={ ( value ) =>
setAttributes( { label: value } )
}
+ style={ { backgroundColor: 'transparent' } }
/>
>
) }
diff --git a/assets/js/data/cart/push-changes.ts b/assets/js/data/cart/push-changes.ts
index 4484b978e29..06228265a8e 100644
--- a/assets/js/data/cart/push-changes.ts
+++ b/assets/js/data/cart/push-changes.ts
@@ -3,11 +3,7 @@
*/
import { debounce, pick } from 'lodash';
import { select, dispatch } from '@wordpress/data';
-import {
- pluckAddress,
- pluckEmail,
- removeAllNotices,
-} from '@woocommerce/base-utils';
+import { pluckEmail, removeAllNotices } from '@woocommerce/base-utils';
import {
CartBillingAddress,
CartShippingAddress,
@@ -27,15 +23,33 @@ type CustomerData = {
shippingAddress: CartShippingAddress;
};
+type BillingOrShippingAddress = CartBillingAddress | CartShippingAddress;
+
/**
* Checks if a cart response contains an email property.
*/
const isBillingAddress = (
- address: CartBillingAddress | CartShippingAddress
+ address: BillingOrShippingAddress
): address is CartBillingAddress => {
return 'email' in address;
};
+export const trimAddress = ( address: BillingOrShippingAddress ) => {
+ const trimmedAddress = {
+ ...address,
+ };
+ Object.keys( address ).forEach( ( key ) => {
+ trimmedAddress[ key as keyof BillingOrShippingAddress ] =
+ address[ key as keyof BillingOrShippingAddress ].trim();
+ } );
+
+ trimmedAddress.postcode = trimmedAddress.postcode
+ ? trimmedAddress.postcode.replace( ' ', '' ).toUpperCase()
+ : '';
+
+ return trimmedAddress;
+};
+
/**
* Does a shallow compare of important address data to determine if the cart needs updating on the server. This takes
* the current and previous address into account, as well as the billing email field.
@@ -57,8 +71,8 @@ const isAddressDirty = < T extends CartBillingAddress | CartShippingAddress >(
return (
!! address.country &&
! isShallowEqual(
- pluckAddress( previousAddress ),
- pluckAddress( address )
+ trimAddress( previousAddress ),
+ trimAddress( address )
)
);
};
diff --git a/assets/js/data/checkout/constants.ts b/assets/js/data/checkout/constants.ts
index 31bd2b71981..a5b16a1ca82 100644
--- a/assets/js/data/checkout/constants.ts
+++ b/assets/js/data/checkout/constants.ts
@@ -12,8 +12,6 @@ import { CheckoutResponseSuccess } from '@woocommerce/types';
export const STORE_KEY = 'wc/store/checkout';
export enum STATUS {
- // Checkout is in its initialized state.
- PRISTINE = 'pristine',
// When checkout state has changed but there is no activity happening.
IDLE = 'idle',
// After the AFTER_PROCESSING event emitters have completed. This status triggers the checkout redirect.
diff --git a/assets/js/data/checkout/reducers.ts b/assets/js/data/checkout/reducers.ts
index 93cd3c02d6e..fa7845715a3 100644
--- a/assets/js/data/checkout/reducers.ts
+++ b/assets/js/data/checkout/reducers.ts
@@ -166,14 +166,6 @@ const reducer = ( state = defaultState, action: CheckoutAction ) => {
}
break;
}
-
- if (
- newState !== state &&
- action.type !== types.SET_PRISTINE &&
- newState?.status === STATUS.PRISTINE
- ) {
- newState.status = STATUS.IDLE;
- }
return newState;
};
diff --git a/assets/js/data/checkout/test/reducer.ts b/assets/js/data/checkout/test/reducer.ts
index 4df3e787ef7..f83b5985a99 100644
--- a/assets/js/data/checkout/test/reducer.ts
+++ b/assets/js/data/checkout/test/reducer.ts
@@ -26,7 +26,6 @@ describe.only( 'Checkout Store Reducer', () => {
const expectedState = {
...defaultState,
redirectUrl: 'https://example.com',
- status: STATUS.IDLE,
};
expect(
@@ -97,12 +96,15 @@ describe.only( 'Checkout Store Reducer', () => {
} );
it( 'should handle SET_HAS_ERROR when status is anything else', () => {
- const initialState = { ...defaultState, status: STATUS.PRISTINE };
+ const initialState = {
+ ...defaultState,
+ status: STATUS.AFTER_PROCESSING,
+ };
const expectedState = {
...defaultState,
hasError: false,
- status: STATUS.IDLE,
+ status: STATUS.AFTER_PROCESSING,
};
expect(
@@ -135,7 +137,6 @@ describe.only( 'Checkout Store Reducer', () => {
it( 'should handle INCREMENT_CALCULATING', () => {
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
calculatingCount: 1,
};
@@ -152,7 +153,6 @@ describe.only( 'Checkout Store Reducer', () => {
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
calculatingCount: 0,
};
@@ -164,7 +164,6 @@ describe.only( 'Checkout Store Reducer', () => {
it( 'should handle SET_CUSTOMER_ID', () => {
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
customerId: 1,
};
@@ -176,7 +175,6 @@ describe.only( 'Checkout Store Reducer', () => {
it( 'should handle SET_USE_SHIPPING_AS_BILLING', () => {
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
useShippingAsBilling: false,
};
@@ -191,7 +189,6 @@ describe.only( 'Checkout Store Reducer', () => {
it( 'should handle SET_SHOULD_CREATE_ACCOUNT', () => {
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
shouldCreateAccount: true,
};
@@ -206,7 +203,6 @@ describe.only( 'Checkout Store Reducer', () => {
it( 'should handle SET_ORDER_NOTES', () => {
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
orderNotes: 'test',
};
@@ -225,7 +221,6 @@ describe.only( 'Checkout Store Reducer', () => {
};
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
extensionData: mockExtensionData,
};
expect(
@@ -247,7 +242,6 @@ describe.only( 'Checkout Store Reducer', () => {
};
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
extensionData: mockExtensionData,
};
const firstState = reducer(
@@ -272,7 +266,6 @@ describe.only( 'Checkout Store Reducer', () => {
};
const expectedState = {
...defaultState,
- status: STATUS.IDLE,
extensionData: mockExtensionData,
};
const firstState = reducer(
diff --git a/assets/js/data/checkout/types.ts b/assets/js/data/checkout/types.ts
index 6057407e1da..acec22130c6 100644
--- a/assets/js/data/checkout/types.ts
+++ b/assets/js/data/checkout/types.ts
@@ -3,6 +3,7 @@
*/
import type { Notice } from '@wordpress/notices/';
import { DataRegistry } from '@wordpress/data';
+import { FieldValidationStatus } from '@woocommerce/types';
/**
* Internal dependencies
@@ -13,7 +14,6 @@ import type { PaymentState } from '../payment/default-state';
import type { DispatchFromMap, SelectFromMap } from '../mapped-types';
import * as selectors from './selectors';
import * as actions from './actions';
-import { FieldValidationStatus } from '../types';
export type CheckoutAfterProcessingWithErrorEventData = {
redirectUrl: CheckoutState[ 'redirectUrl' ];
diff --git a/assets/js/data/index.ts b/assets/js/data/index.ts
index 5367e06bb06..4cd685a5c09 100644
--- a/assets/js/data/index.ts
+++ b/assets/js/data/index.ts
@@ -15,5 +15,4 @@ export { VALIDATION_STORE_KEY } from './validation';
export { QUERY_STATE_STORE_KEY } from './query-state';
export { STORE_NOTICES_STORE_KEY } from './store-notices';
export * from './constants';
-export * from './types';
export * from './utils';
diff --git a/assets/js/data/payment/action-types.ts b/assets/js/data/payment/action-types.ts
index f73208e3e83..5e4a57613a0 100644
--- a/assets/js/data/payment/action-types.ts
+++ b/assets/js/data/payment/action-types.ts
@@ -1,10 +1,9 @@
export enum ACTION_TYPES {
- SET_PAYMENT_PRISTINE = 'SET_PAYMENT_PRISTINE',
- SET_PAYMENT_STARTED = 'SET_PAYMENT_STARTED',
+ SET_PAYMENT_IDLE = 'SET_PAYMENT_IDLE',
+ SET_EXPRESS_PAYMENT_STARTED = 'SET_EXPRESS_PAYMENT_STARTED',
+ SET_PAYMENT_READY = 'SET_PAYMENT_READY',
SET_PAYMENT_PROCESSING = 'SET_PAYMENT_PROCESSING',
- SET_PAYMENT_FAILED = 'SET_PAYMENT_FAILED',
SET_PAYMENT_ERROR = 'SET_PAYMENT_ERROR',
- SET_PAYMENT_SUCCESS = 'SET_PAYMENT_SUCCESS',
SET_PAYMENT_METHODS_INITIALIZED = 'SET_PAYMENT_METHODS_INITIALIZED',
SET_EXPRESS_PAYMENT_METHODS_INITIALIZED = 'SET_EXPRESS_PAYMENT_METHODS_INITIALIZED',
SET_ACTIVE_PAYMENT_METHOD = 'SET_ACTIVE_PAYMENT_METHOD',
diff --git a/assets/js/data/payment/actions.ts b/assets/js/data/payment/actions.ts
index 8ee3d412fee..f1c735111a5 100644
--- a/assets/js/data/payment/actions.ts
+++ b/assets/js/data/payment/actions.ts
@@ -17,28 +17,24 @@ import { setDefaultPaymentMethod } from './utils/set-default-payment-method';
// `Thunks are functions that can be dispatched, similar to actions creators
export * from './thunks';
-export const __internalSetPaymentPristine = () => ( {
- type: ACTION_TYPES.SET_PAYMENT_PRISTINE,
+export const __internalSetPaymentIdle = () => ( {
+ type: ACTION_TYPES.SET_PAYMENT_IDLE,
} );
-export const __internalSetPaymentStarted = () => ( {
- type: ACTION_TYPES.SET_PAYMENT_STARTED,
+export const __internalSetExpressPaymentStarted = () => ( {
+ type: ACTION_TYPES.SET_EXPRESS_PAYMENT_STARTED,
} );
export const __internalSetPaymentProcessing = () => ( {
type: ACTION_TYPES.SET_PAYMENT_PROCESSING,
} );
-export const __internalSetPaymentFailed = () => ( {
- type: ACTION_TYPES.SET_PAYMENT_FAILED,
-} );
-
export const __internalSetPaymentError = () => ( {
type: ACTION_TYPES.SET_PAYMENT_ERROR,
} );
-export const __internalSetPaymentSuccess = () => ( {
- type: ACTION_TYPES.SET_PAYMENT_SUCCESS,
+export const __internalSetPaymentReady = () => ( {
+ type: ACTION_TYPES.SET_PAYMENT_READY,
} );
/**
diff --git a/assets/js/data/payment/constants.ts b/assets/js/data/payment/constants.ts
index f77e56d5aee..9f4ccd0f72b 100644
--- a/assets/js/data/payment/constants.ts
+++ b/assets/js/data/payment/constants.ts
@@ -1,10 +1,9 @@
export const STORE_KEY = 'wc/store/payment';
export enum STATUS {
- PRISTINE = 'pristine',
- STARTED = 'started',
+ IDLE = 'idle',
+ EXPRESS_STARTED = 'express_started',
PROCESSING = 'processing',
+ READY = 'ready',
ERROR = 'has_error',
- FAILED = 'failed',
- SUCCESS = 'success',
}
diff --git a/assets/js/data/payment/default-state.ts b/assets/js/data/payment/default-state.ts
index 660951c0b04..21e121cb61a 100644
--- a/assets/js/data/payment/default-state.ts
+++ b/assets/js/data/payment/default-state.ts
@@ -32,7 +32,7 @@ export interface PaymentState {
}
export const defaultPaymentState: PaymentState = {
- status: PAYMENT_STATUS.PRISTINE,
+ status: PAYMENT_STATUS.IDLE,
activePaymentMethod: '',
activeSavedToken: '',
availablePaymentMethods: {},
diff --git a/assets/js/data/payment/reducers.ts b/assets/js/data/payment/reducers.ts
index 18723fb3785..800db3f45fb 100644
--- a/assets/js/data/payment/reducers.ts
+++ b/assets/js/data/payment/reducers.ts
@@ -17,17 +17,17 @@ const reducer: Reducer< PaymentState > = (
) => {
let newState = state;
switch ( action.type ) {
- case ACTION_TYPES.SET_PAYMENT_PRISTINE:
+ case ACTION_TYPES.SET_PAYMENT_IDLE:
newState = {
...state,
- status: STATUS.PRISTINE,
+ status: STATUS.IDLE,
};
break;
- case ACTION_TYPES.SET_PAYMENT_STARTED:
+ case ACTION_TYPES.SET_EXPRESS_PAYMENT_STARTED:
newState = {
...state,
- status: STATUS.STARTED,
+ status: STATUS.EXPRESS_STARTED,
};
break;
@@ -38,10 +38,10 @@ const reducer: Reducer< PaymentState > = (
};
break;
- case ACTION_TYPES.SET_PAYMENT_FAILED:
+ case ACTION_TYPES.SET_PAYMENT_READY:
newState = {
...state,
- status: STATUS.FAILED,
+ status: STATUS.READY,
};
break;
@@ -52,13 +52,6 @@ const reducer: Reducer< PaymentState > = (
};
break;
- case ACTION_TYPES.SET_PAYMENT_SUCCESS:
- newState = {
- ...state,
- status: STATUS.SUCCESS,
- };
- break;
-
case ACTION_TYPES.SET_SHOULD_SAVE_PAYMENT_METHOD:
newState = {
...state,
diff --git a/assets/js/data/payment/selectors.ts b/assets/js/data/payment/selectors.ts
index dd87cd4cdaf..afc815d85ce 100644
--- a/assets/js/data/payment/selectors.ts
+++ b/assets/js/data/payment/selectors.ts
@@ -14,6 +14,7 @@ import { filterActiveSavedPaymentMethods } from './utils/filter-active-saved-pay
import { STATUS as PAYMENT_STATUS } from './constants';
const globalPaymentMethods: Record< string, string > = {};
+
if ( getSetting( 'globalPaymentMethods' ) ) {
getSetting< GlobalPaymentMethod[] >( 'globalPaymentMethods' ).forEach(
( method ) => {
@@ -22,30 +23,62 @@ if ( getSetting( 'globalPaymentMethods' ) ) {
);
}
-export const isPaymentPristine = ( state: PaymentState ) =>
- state.status === PAYMENT_STATUS.PRISTINE;
+export const isPaymentPristine = ( state: PaymentState ) => {
+ deprecated( 'isPaymentPristine', {
+ since: '9.6.0',
+ alternative: 'isPaymentIdle',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+
+ return state.status === PAYMENT_STATUS.IDLE;
+};
+
+export const isPaymentIdle = ( state: PaymentState ) =>
+ state.status === PAYMENT_STATUS.IDLE;
-export const isPaymentStarted = ( state: PaymentState ) =>
- state.status === PAYMENT_STATUS.STARTED;
+export const isPaymentStarted = ( state: PaymentState ) => {
+ deprecated( 'isPaymentStarted', {
+ since: '9.6.0',
+ alternative: 'isExpressPaymentStarted',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return state.status === PAYMENT_STATUS.EXPRESS_STARTED;
+};
+
+export const isExpressPaymentStarted = ( state: PaymentState ) => {
+ return state.status === PAYMENT_STATUS.EXPRESS_STARTED;
+};
export const isPaymentProcessing = ( state: PaymentState ) =>
state.status === PAYMENT_STATUS.PROCESSING;
-export const isPaymentSuccess = ( state: PaymentState ) =>
- state.status === PAYMENT_STATUS.SUCCESS;
+export const isPaymentReady = ( state: PaymentState ) =>
+ state.status === PAYMENT_STATUS.READY;
+
+export const isPaymentSuccess = ( state: PaymentState ) => {
+ deprecated( 'isPaymentSuccess', {
+ since: '9.6.0',
+ alternative: 'isPaymentReady',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+
+ return state.status === PAYMENT_STATUS.READY;
+};
export const hasPaymentError = ( state: PaymentState ) =>
state.status === PAYMENT_STATUS.ERROR;
-export const isPaymentFailed = ( state: PaymentState ) =>
- state.status === PAYMENT_STATUS.FAILED;
+export const isPaymentFailed = ( state: PaymentState ) => {
+ deprecated( 'isPaymentFailed', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
-export const isPaymentFinished = ( state: PaymentState ) => {
- return (
- state.status === PAYMENT_STATUS.SUCCESS ||
- state.status === PAYMENT_STATUS.ERROR ||
- state.status === PAYMENT_STATUS.FAILED
- );
+ return state.status === PAYMENT_STATUS.ERROR;
};
export const isExpressPaymentMethodActive = ( state: PaymentState ) => {
@@ -119,26 +152,54 @@ export const expressPaymentMethodsInitialized = ( state: PaymentState ) => {
};
/**
- * @deprecated - use these selectors instead: isPaymentPristine, isPaymentStarted, isPaymentProcessing,
- * isPaymentFinished, hasPaymentError, isPaymentSuccess, isPaymentFailed
+ * @deprecated - Use these selectors instead: isPaymentIdle, isPaymentProcessing,
+ * hasPaymentError
*/
export const getCurrentStatus = ( state: PaymentState ) => {
deprecated( 'getCurrentStatus', {
since: '8.9.0',
- alternative:
- 'isPaymentPristine, isPaymentStarted, isPaymentProcessing, isPaymentFinished, hasPaymentError, isPaymentSuccess, isPaymentFailed',
+ alternative: 'isPaymentIdle, isPaymentProcessing, hasPaymentError',
plugin: 'WooCommerce Blocks',
link: 'https://github.com/woocommerce/woocommerce-blocks/pull/7666',
} );
return {
- isPristine: isPaymentPristine( state ),
- isStarted: isPaymentStarted( state ),
+ get isPristine() {
+ deprecated( 'isPristine', {
+ since: '9.6.0',
+ alternative: 'isIdle',
+ plugin: 'WooCommerce Blocks',
+ } );
+ return isPaymentIdle( state );
+ }, // isPristine is the same as isIdle.
+ isIdle: isPaymentIdle( state ),
+ isStarted: isExpressPaymentStarted( state ),
isProcessing: isPaymentProcessing( state ),
- isFinished: isPaymentFinished( state ),
+ get isFinished() {
+ deprecated( 'isFinished', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return hasPaymentError( state ) || isPaymentReady( state );
+ },
hasError: hasPaymentError( state ),
- hasFailed: isPaymentFailed( state ),
- isSuccessful: isPaymentSuccess( state ),
+ get hasFailed() {
+ deprecated( 'hasFailed', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return hasPaymentError( state );
+ },
+ get isSuccessful() {
+ deprecated( 'isSuccessful', {
+ since: '9.6.0',
+ plugin: 'WooCommerce Blocks',
+ link: 'https://github.com/woocommerce/woocommerce-blocks/pull/8110',
+ } );
+ return isPaymentReady( state );
+ },
isDoingExpressPayment: isExpressPaymentMethodActive( state ),
};
};
diff --git a/assets/js/data/payment/test/set-default-payment-method.ts b/assets/js/data/payment/test/set-default-payment-method.ts
index c0ca7ae989c..aa1a21e82c2 100644
--- a/assets/js/data/payment/test/set-default-payment-method.ts
+++ b/assets/js/data/payment/test/set-default-payment-method.ts
@@ -126,11 +126,10 @@ describe( 'setDefaultPaymentMethod', () => {
__internalSetActivePaymentMethod:
setActivePaymentMethodMock,
__internalSetPaymentError: () => void 0,
- __internalSetPaymentFailed: () => void 0,
- __internalSetPaymentSuccess: () => void 0,
- __internalSetPaymentPristine: () => void 0,
- __internalSetPaymentStarted: () => void 0,
+ __internalSetPaymentIdle: () => void 0,
+ __internalSetExpressPaymentStarted: () => void 0,
__internalSetPaymentProcessing: () => void 0,
+ __internalSetPaymentReady: () => void 0,
};
}
return originalStore;
diff --git a/assets/js/data/payment/test/thunks.tsx b/assets/js/data/payment/test/thunks.tsx
index dfe1a755ec8..d3377df4303 100644
--- a/assets/js/data/payment/test/thunks.tsx
+++ b/assets/js/data/payment/test/thunks.tsx
@@ -191,7 +191,7 @@ describe( 'wc/store/payment thunks', () => {
} );
const setPaymentErrorMock = jest.fn();
- const setPaymentSuccessMock = jest.fn();
+ const setPaymentReadyMock = jest.fn();
const registryMock = {
dispatch: jest
.fn()
@@ -211,14 +211,14 @@ describe( 'wc/store/payment thunks', () => {
dispatch: {
...wpDataFunctions.dispatch( PAYMENT_STORE_KEY ),
__internalSetPaymentError: setPaymentErrorMock,
- __internalSetPaymentSuccess: setPaymentSuccessMock,
+ __internalSetPaymentReady: setPaymentReadyMock,
},
} );
// The observer throwing will cause this.
//expect( console ).toHaveErroredWith( new Error( 'test error' ) );
expect( setPaymentErrorMock ).toHaveBeenCalled();
- expect( setPaymentSuccessMock ).not.toHaveBeenCalled();
+ expect( setPaymentReadyMock ).not.toHaveBeenCalled();
} );
} );
} );
diff --git a/assets/js/data/payment/thunks.ts b/assets/js/data/payment/thunks.ts
index 1ba23513727..d3fb85ab359 100644
--- a/assets/js/data/payment/thunks.ts
+++ b/assets/js/data/payment/thunks.ts
@@ -4,6 +4,7 @@
import { store as noticesStore } from '@wordpress/notices';
import deprecated from '@wordpress/deprecated';
import type { BillingAddress, ShippingAddress } from '@woocommerce/settings';
+import { isObject, isString, objectHasProp } from '@woocommerce/types';
/**
* Internal dependencies
@@ -14,6 +15,7 @@ import {
isFailResponse,
isSuccessResponse,
noticeContexts,
+ ObserverResponse,
} from '../../base/context/event-emit';
import { EMIT_TYPES } from '../../base/context/providers/cart-checkout/payment-events/event-emit';
import type { emitProcessingEventType } from './types';
@@ -22,6 +24,8 @@ import {
isBillingAddress,
isShippingAddress,
} from '../../types/type-guards/address';
+import { isObserverResponse } from '../../types/type-guards/observers';
+import { isValidValidationErrorsObject } from '../../types/type-guards/validation';
export const __internalSetExpressPaymentError = ( message?: string ) => {
return ( { registry } ) => {
@@ -57,15 +61,17 @@ export const __internalEmitPaymentProcessingEvent: emitProcessingEventType = (
EMIT_TYPES.PAYMENT_PROCESSING,
{}
).then( ( observerResponses ) => {
- let successResponse,
- errorResponse,
+ let successResponse: ObserverResponse | undefined,
+ errorResponse: ObserverResponse | undefined,
billingAddress: BillingAddress | undefined,
shippingAddress: ShippingAddress | undefined;
observerResponses.forEach( ( response ) => {
if ( isSuccessResponse( response ) ) {
- // the last observer response always "wins" for success.
+ // The last observer response always "wins" for success.
successResponse = response;
}
+
+ // We consider both failed and error responses as an error.
if (
isErrorResponse( response ) ||
isFailResponse( response )
@@ -86,12 +92,13 @@ export const __internalEmitPaymentProcessingEvent: emitProcessingEventType = (
shippingData: shippingDataFromResponse,
} = response?.meta || {};
- billingAddress = billingAddressFromResponse;
- shippingAddress = shippingAddressFromResponse;
+ billingAddress = billingAddressFromResponse as BillingAddress;
+ shippingAddress =
+ shippingAddressFromResponse as ShippingAddress;
if ( billingDataFromResponse ) {
// Set this here so that old extensions still using billingData can set the billingAddress.
- billingAddress = billingDataFromResponse;
+ billingAddress = billingDataFromResponse as BillingAddress;
deprecated(
'returning billingData from an onPaymentProcessing observer in WooCommerce Blocks',
{
@@ -104,7 +111,8 @@ export const __internalEmitPaymentProcessingEvent: emitProcessingEventType = (
if ( shippingDataFromResponse ) {
// Set this here so that old extensions still using shippingData can set the shippingAddress.
- shippingAddress = shippingDataFromResponse;
+ shippingAddress =
+ shippingDataFromResponse as ShippingAddress;
deprecated(
'returning shippingData from an onPaymentProcessing observer in WooCommerce Blocks',
{
@@ -119,54 +127,85 @@ export const __internalEmitPaymentProcessingEvent: emitProcessingEventType = (
const { setBillingAddress, setShippingAddress } =
registry.dispatch( CART_STORE_KEY );
- if ( successResponse && ! errorResponse ) {
+ // Observer returned success, we sync the payment method data and billing address.
+ if ( isObserverResponse( successResponse ) && ! errorResponse ) {
const { paymentMethodData } = successResponse?.meta || {};
- if ( billingAddress && isBillingAddress( billingAddress ) ) {
+ if ( isBillingAddress( billingAddress ) ) {
setBillingAddress( billingAddress );
}
- if (
- typeof shippingAddress !== 'undefined' &&
- isShippingAddress( shippingAddress )
- ) {
+ if ( isShippingAddress( shippingAddress ) ) {
setShippingAddress( shippingAddress );
}
- dispatch.__internalSetPaymentMethodData( paymentMethodData );
- dispatch.__internalSetPaymentSuccess();
- } else if ( errorResponse && isFailResponse( errorResponse ) ) {
- if ( errorResponse.message && errorResponse.message.length ) {
+
+ dispatch.__internalSetPaymentMethodData(
+ isObject( paymentMethodData ) ? paymentMethodData : {}
+ );
+ dispatch.__internalSetPaymentReady();
+ } else if ( isFailResponse( errorResponse ) ) {
+ const { paymentMethodData } = errorResponse?.meta || {};
+
+ if (
+ objectHasProp( errorResponse, 'message' ) &&
+ isString( errorResponse.message ) &&
+ errorResponse.message.length
+ ) {
+ let context: string = noticeContexts.PAYMENTS;
+ if (
+ objectHasProp( errorResponse, 'messageContext' ) &&
+ isString( errorResponse.messageContext ) &&
+ errorResponse.messageContext.length
+ ) {
+ context = errorResponse.messageContext;
+ }
createErrorNotice( errorResponse.message, {
id: 'wc-payment-error',
isDismissible: false,
- context:
- errorResponse?.messageContext ||
- noticeContexts.PAYMENTS,
+ context,
} );
}
- const { paymentMethodData } = errorResponse?.meta || {};
- if ( billingAddress && isBillingAddress( billingAddress ) ) {
+ if ( isBillingAddress( billingAddress ) ) {
setBillingAddress( billingAddress );
}
- dispatch.__internalSetPaymentFailed();
- dispatch.__internalSetPaymentMethodData( paymentMethodData );
- } else if ( errorResponse ) {
- if ( errorResponse.message && errorResponse.message.length ) {
+
+ dispatch.__internalSetPaymentMethodData(
+ isObject( paymentMethodData ) ? paymentMethodData : {}
+ );
+ dispatch.__internalSetPaymentError();
+ } else if ( isErrorResponse( errorResponse ) ) {
+ if (
+ objectHasProp( errorResponse, 'message' ) &&
+ isString( errorResponse.message ) &&
+ errorResponse.message.length
+ ) {
+ let context: string = noticeContexts.PAYMENTS;
+ if (
+ objectHasProp( errorResponse, 'messageContext' ) &&
+ isString( errorResponse.messageContext ) &&
+ errorResponse.messageContext.length
+ ) {
+ context = errorResponse.messageContext;
+ }
createErrorNotice( errorResponse.message, {
id: 'wc-payment-error',
isDismissible: false,
- context:
- errorResponse?.messageContext ||
- noticeContexts.PAYMENTS,
+ context,
} );
}
dispatch.__internalSetPaymentError();
- setValidationErrors( errorResponse?.validationErrors );
+
+ if (
+ isValidValidationErrorsObject(
+ errorResponse.validationErrors
+ )
+ ) {
+ setValidationErrors( errorResponse.validationErrors );
+ }
} else {
- // otherwise there are no payment methods doing anything so
- // just consider success
- dispatch.__internalSetPaymentSuccess();
+ // Otherwise there are no payment methods doing anything so just assume payment method is ready.
+ dispatch.__internalSetPaymentReady();
}
} );
};
diff --git a/assets/js/data/payment/types.ts b/assets/js/data/payment/types.ts
index d4073c7c272..1a831713627 100644
--- a/assets/js/data/payment/types.ts
+++ b/assets/js/data/payment/types.ts
@@ -5,7 +5,11 @@ import {
PlainPaymentMethods,
PlainExpressPaymentMethods,
} from '@woocommerce/types';
-import type { EmptyObjectType, ObjectType } from '@woocommerce/types';
+import type {
+ EmptyObjectType,
+ ObjectType,
+ FieldValidationStatus,
+} from '@woocommerce/types';
import { DataRegistry } from '@wordpress/data';
/**
@@ -14,7 +18,6 @@ import { DataRegistry } from '@wordpress/data';
import type { EventObserversType } from '../../base/context/event-emit';
import type { DispatchFromMap } from '../mapped-types';
import * as actions from './actions';
-import { FieldValidationStatus } from '../types';
export interface CustomerPaymentMethodConfiguration {
gateway: string;
diff --git a/assets/js/data/payment/utils/set-default-payment-method.ts b/assets/js/data/payment/utils/set-default-payment-method.ts
index 612284c6748..71fe94e7e76 100644
--- a/assets/js/data/payment/utils/set-default-payment-method.ts
+++ b/assets/js/data/payment/utils/set-default-payment-method.ts
@@ -60,7 +60,7 @@ export const setDefaultPaymentMethod = async (
return;
}
- dispatch( PAYMENT_STORE_KEY ).__internalSetPaymentPristine();
+ dispatch( PAYMENT_STORE_KEY ).__internalSetPaymentIdle();
dispatch( PAYMENT_STORE_KEY ).__internalSetActivePaymentMethod(
paymentMethodKeys[ 0 ]
diff --git a/assets/js/data/types.ts b/assets/js/data/types.ts
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/assets/js/data/validation/actions.ts b/assets/js/data/validation/actions.ts
index 0dd850a5474..2aef1ab66da 100644
--- a/assets/js/data/validation/actions.ts
+++ b/assets/js/data/validation/actions.ts
@@ -2,13 +2,13 @@
* External dependencies
*/
import deprecated from '@wordpress/deprecated';
+import { FieldValidationStatus } from '@woocommerce/types';
/**
* Internal dependencies
*/
import { ACTION_TYPES as types } from './action-types';
import { ReturnOrGeneratorYieldUnion } from '../mapped-types';
-import { FieldValidationStatus } from '../types';
export const setValidationErrors = (
errors: Record< string, FieldValidationStatus >
diff --git a/assets/js/data/validation/reducers.ts b/assets/js/data/validation/reducers.ts
index a16b8b83274..f3d1f07abac 100644
--- a/assets/js/data/validation/reducers.ts
+++ b/assets/js/data/validation/reducers.ts
@@ -4,14 +4,13 @@
import type { Reducer } from 'redux';
import { pickBy } from 'lodash';
import isShallowEqual from '@wordpress/is-shallow-equal';
-import { isString } from '@woocommerce/types';
+import { isString, FieldValidationStatus } from '@woocommerce/types';
/**
* Internal dependencies
*/
import { ValidationAction } from './actions';
import { ACTION_TYPES as types } from './action-types';
-import { FieldValidationStatus } from '../types';
const reducer: Reducer< Record< string, FieldValidationStatus > > = (
state: Record< string, FieldValidationStatus > = {},
diff --git a/assets/js/data/validation/test/reducers.ts b/assets/js/data/validation/test/reducers.ts
index b99530fcd1c..66ef3b8df71 100644
--- a/assets/js/data/validation/test/reducers.ts
+++ b/assets/js/data/validation/test/reducers.ts
@@ -1,8 +1,12 @@
+/**
+ * External dependencies
+ */
+import { FieldValidationStatus } from '@woocommerce/types';
+
/**
* Internal dependencies
*/
import reducer from '../reducers';
-import { FieldValidationStatus } from '../../types';
import { ACTION_TYPES as types } from '.././action-types';
import { ValidationAction } from '../actions';
diff --git a/assets/js/data/validation/test/selectors.ts b/assets/js/data/validation/test/selectors.ts
index cb06f3e7660..1703ef76d8c 100644
--- a/assets/js/data/validation/test/selectors.ts
+++ b/assets/js/data/validation/test/selectors.ts
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+import { FieldValidationStatus } from '@woocommerce/types';
+
/**
* Internal dependencies
*/
@@ -6,7 +11,6 @@ import {
getValidationError,
hasValidationErrors,
} from '../selectors';
-import { FieldValidationStatus } from '../../types';
describe( 'Validation selectors', () => {
it( 'Gets the validation error', () => {
diff --git a/assets/js/editor-components/block-title/index.js b/assets/js/editor-components/block-title/index.js
index 18ccd0f605d..0827dac567a 100644
--- a/assets/js/editor-components/block-title/index.js
+++ b/assets/js/editor-components/block-title/index.js
@@ -32,6 +32,7 @@ const BlockTitle = ( {
className="wc-block-editor-components-title"
value={ heading }
onChange={ onChange }
+ style={ { backgroundColor: 'transparent' } }
/>
);
diff --git a/assets/js/editor-components/incompatible-payment-gateways-notice/editor.scss b/assets/js/editor-components/incompatible-payment-gateways-notice/editor.scss
index db6f6c78b95..5a10598ea7f 100644
--- a/assets/js/editor-components/incompatible-payment-gateways-notice/editor.scss
+++ b/assets/js/editor-components/incompatible-payment-gateways-notice/editor.scss
@@ -24,6 +24,14 @@
.wc-blocks-incompatible-extensions-notice__element {
display: flex;
align-items: center;
+ position: relative;
+
+ &::before {
+ content: "•";
+ position: absolute;
+ left: -13px;
+ font-size: 1.2rem;
+ }
}
}
diff --git a/assets/js/editor-components/incompatible-payment-gateways-notice/index.tsx b/assets/js/editor-components/incompatible-payment-gateways-notice/index.tsx
index b4f8f6f9ec1..1c33b987a35 100644
--- a/assets/js/editor-components/incompatible-payment-gateways-notice/index.tsx
+++ b/assets/js/editor-components/incompatible-payment-gateways-notice/index.tsx
@@ -9,7 +9,7 @@ import {
createInterpolateElement,
useEffect,
} from '@wordpress/element';
-import { alert } from '@woocommerce/icons';
+import { Alert } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
/**
* Internal dependencies
@@ -74,7 +74,7 @@ export function IncompatiblePaymentGatewaysNotice( {
}
/>
{ noticeContent }
diff --git a/assets/js/icons/index.js b/assets/js/icons/index.js
index cf8687b350f..70acf6bdd56 100644
--- a/assets/js/icons/index.js
+++ b/assets/js/icons/index.js
@@ -13,7 +13,7 @@ export { default as woo } from './library/woo';
export { default as miniCart } from './library/mini-cart';
export { default as miniCartAlt } from './library/mini-cart-alt';
export { default as stacks } from './library/stacks';
-export { default as alert } from './library/alert';
+export { default as Alert } from './library/alert';
export { default as customerAccount } from './library/customer-account';
export { default as customerAccountStyle } from './library/customer-account-style';
export { default as customerAccountStyleAlt } from './library/customer-account-style-alt';
diff --git a/assets/js/icons/library/alert.tsx b/assets/js/icons/library/alert.tsx
index 096b1329c18..4cec8a9384a 100644
--- a/assets/js/icons/library/alert.tsx
+++ b/assets/js/icons/library/alert.tsx
@@ -1,18 +1,36 @@
/**
* External dependencies
*/
+import { IconProps } from '@wordpress/icons/build-types/icon';
import { SVG } from '@wordpress/primitives';
-const cart = (
-