Repository: google/site-kit-wp
Pull request #10746: Enhancement/10672 expand woo tracking client
Author: login: zutigrm
Created: 2025-05-09T11:10:41Z | Merged: 2025-05-20T10:27:48Z

## Summary

<!-- Please reference the issue this PR addresses in the following list. -->
Addresses issue:

- #10672 

## Relevant technical choices

<!-- Please describe your changes. -->

## PR Author Checklist

- [x] My code is tested and passes existing unit tests.
- [ ] My code has an appropriate set of unit tests which all pass.
- [ ] My code is backward-compatible with WordPress 5.2 and PHP 7.4.
- [ ] My code follows the [WordPress](https://make.wordpress.org/core/handbook/best-practices/coding-standards/) coding standards.
- [ ] My code has proper inline documentation.
- [x] I have added a QA Brief on the issue linked above.
- [x] I have signed the Contributor License Agreement (see <https://cla.developers.google.com/>).

---------------

_Do not alter or remove anything below. The following sections will be managed by moderators only._

## Code Reviewer Checklist

- [x] Run the code.
- [x] Ensure the acceptance criteria are satisfied.
- [x] Reassess the implementation with the IB.
- [x] Ensure no unrelated changes are included.
- [x] Ensure CI checks pass.
- [x] Check Storybook where applicable.
- [x] Ensure there is a QA Brief.
- [x] Ensure there are no unexpected significant changes to file sizes.

## Merge Reviewer Checklist

- [ ] Ensure the PR has the correct target branch.
- [ ] Double-check that the PR is okay to be merged.
- [ ] Ensure the corresponding issue has a ZenHub release assigned.
- [ ] Add a changelog message to the issue.



Diff:
diff --git a/assets/js/event-providers/woocommerce.js b/assets/js/event-providers/woocommerce.js
index 24dbd3fd91a..84fa404ea98 100644
--- a/assets/js/event-providers/woocommerce.js
+++ b/assets/js/event-providers/woocommerce.js
@@ -19,13 +19,168 @@
 		return;
 	}
 
-	const body = jQuery( 'body' );
+	const {
+		currency: globalCurrency,
+		products: globalProducts,
+		purchase,
+		add_to_cart: addToCart,
+	} = global._googlesitekit?.wcdata || {};
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
+	if ( addToCart ) {
+		const { price } = addToCart;
+
+		const eventData = formatEventData( price, globalCurrency, addToCart );
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( purchase ) {
+		const { id, totals, items } = purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
+	const $body = jQuery( 'body' );
+
+	$body.on( 'added_to_cart', ( event, fragments, cartHash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+
+		if ( ! productID ) {
+			return;
+		}
+
+		const productData =
+			globalProducts?.find( ( product ) => product?.id === productID ) ||
+			{};
+		const { price } = productData;
+
+		const eventData = formatEventData( price, globalCurrency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+	jQuery(
+		'.products-block-post-template .product, .wc-block-product-template .product'
+	).each( function () {
+		const $productCard = jQuery( this );
+		const productID = parseInt(
+			$productCard.find( '[data-product_id]' ).attr( 'data-product_id' ),
+			10
+		);
+
+		if ( ! productID ) {
+			return;
+		}
+
+		$productCard.on( 'click', function ( event ) {
+			const $target = jQuery( event.target );
+			const $button = $target.closest(
+				'.wc-block-components-product-button [data-product_id]'
+			);
+
+			const isAddToCartButton =
+				$button.length &&
+				$button.hasClass( 'add_to_cart_button' ) &&
+				! $button.hasClass( 'product_type_variable' );
+
+			if ( ! isAddToCartButton ) {
+				return;
+			}
+
+			const productData =
+				globalProducts?.find(
+					( product ) => product?.id === productID
+				) || {};
+			const { price } = productData;
+
+			const eventData = formatEventData(
+				price,
+				globalCurrency,
+				productData
+			);
+			global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+		} );
 	} );
+
+	function formatEventData(
+		value,
+		currency,
+		products,
+		transactionID = null,
+		shipping = null,
+		tax = null
+	) {
+		const formattedData = {
+			value,
+			currency,
+			items: [],
+		};
+
+		if ( transactionID ) {
+			formattedData.transaction_id = transactionID;
+		}
+
+		// Shipping can be 0, if only check if shipping is not empty value
+		// this will be omitted.
+		if ( typeof shipping === 'number' ) {
+			formattedData.shipping = shipping;
+		}
+
+		// Tax can be 0, if only check if shipping is not empty value
+		// this will be omitted.
+		if ( typeof tax === 'number' ) {
+			formattedData.tax = tax;
+		}
+
+		if ( products && products.length ) {
+			for ( const product of products ) {
+				formattedData.items.push( formatProductData( product ) );
+			}
+		} else if ( products && products.id ) {
+			formattedData.items = [ formatProductData( products ) ];
+		}
+
+		return formattedData;
+	}
+
+	function formatProductData( product ) {
+		const { id, name, price, variation, quantity, categories } = product;
+
+		const mappedItem = {
+			item_id: id,
+			item_name: name,
+			price,
+		};
+
+		if ( quantity ) {
+			mappedItem.quantity = quantity;
+		}
+
+		if ( variation ) {
+			mappedItem.item_variant = variation;
+		}
+
+		if ( categories && categories?.length ) {
+			let categoryIndex = 1;
+			for ( const category of categories ) {
+				mappedItem[
+					categoryIndex > 1
+						? `item_category${ categoryIndex }`
+						: 'item_category'
+				] = category.name;
+
+				categoryIndex++;
+			}
+		}
+
+		return mappedItem;
+	}
 } )( global.jQuery );
diff --git a/includes/Core/Conversion_Tracking/Conversion_Event_Providers/WooCommerce.php b/includes/Core/Conversion_Tracking/Conversion_Event_Providers/WooCommerce.php
index a3336275aad..f1f00ec9faf 100644
--- a/includes/Core/Conversion_Tracking/Conversion_Event_Providers/WooCommerce.php
+++ b/includes/Core/Conversion_Tracking/Conversion_Event_Providers/WooCommerce.php
@@ -155,10 +155,6 @@ function ( $order_id ) use ( $input ) {
 					),
 					'before'
 				);
-
-				// Output the script tag to track the purchase event in
-				// Analytics.
-				BC_Functions::wp_print_inline_script_tag( "window?._googlesitekit?.gtagEvent?.( 'purchase' );" );
 			},
 			10,
 			1


Reviews:
user: login: 10upsimon
body: Thanks @zutigrm , this LGTM, and I was able to verify all but the final non-block theme purchase, for some reason my local refuses to go to a cart or checkout page. I am approving this as the logic looks correct, so we'll wait for QA for the rest.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T13:10:31Z
user: login: eugene-manuilov
body: Thanks, @zutigrm. This is a good start. Left some feedback for you. Please, take a look.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T19:23:13Z
user: login: zutigrm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T06:44:33Z
user: login: zutigrm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T07:23:34Z
user: login: zutigrm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T07:31:19Z
user: login: eugene-manuilov
body: Thanks, @zutigrm. A few more minor comments from me.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T09:25:17Z
user: login: zutigrm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T10:43:10Z
user: login: eugene-manuilov
body: LGTM. Approved but will merge later.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-15T17:16:38Z

Review comments:
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
+			'.products-block-post-template .product, .wc-block-product-template .product'
+		)
+		?.forEach( ( productCard ) => {
+			// Get the Product ID from a child node containing the relevant attribute.
+			const productID = parseInt(
+				productCard
+					.querySelector( '[data-product_id]' )
+					?.getAttribute( 'data-product_id' ),
+				10
+			);
+
+			if ( ! productID ) {
+				return;
+			}
+
+			productCard.addEventListener( 'click', ( event ) => {
+				const target = event.target;
+				const button = target.closest(
+					'.wc-block-components-product-button [data-product_id]'
+				);
+
+				const isAddToCartButton =
+					button &&
+					button.classList.contains( 'add_to_cart_button' ) &&
+					! button.classList.contains( 'product_type_variable' );
+
+				if ( isAddToCartButton ) {
+					const productData =
+						global._googlesitekit.wcdata.products?.find(
+							( product ) => product?.id === productID
+						) || {};
+					const { price } = productData;
+
+					const eventData = formatEventData(
+						price,
+						currency,
+						productData
+					);
+					global._googlesitekit?.gtagEvent?.(
+						'add_to_cart',
+						eventData
+					);
+				}
+			} );
+		} );
 } )( global.jQuery );
+
+function formatEventData(
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: Please, move these functions into the anonymous function call to avoid polluting the global space. I that currently this is not gonna happen since we use webpack and it wraps everything with its own code, but in the future we may want to migrate to a more simple bunlder that won't wrap anything.
created_at: 2025-05-12T19:15:22Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
+			'.products-block-post-template .product, .wc-block-product-template .product'
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: I am afraid that these selectors will work reliably only on few themes. Can we add a hook to a filter that will add our own class name to product classes that we can use here? Something like `googlesitekit-product`?
created_at: 2025-05-12T19:17:30Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: Let's use `?.` for consistency because in some places in this script we use it like in `global._googlesitekit?.gtagEvent?.( ... )`, in other don't.
created_at: 2025-05-12T19:19:23Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: Let's use jquery for consistency.
created_at: 2025-05-12T19:20:55Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
+			'.products-block-post-template .product, .wc-block-product-template .product'
+		)
+		?.forEach( ( productCard ) => {
+			// Get the Product ID from a child node containing the relevant attribute.
+			const productID = parseInt(
+				productCard
+					.querySelector( '[data-product_id]' )
+					?.getAttribute( 'data-product_id' ),
+				10
+			);
+
+			if ( ! productID ) {
+				return;
+			}
+
+			productCard.addEventListener( 'click', ( event ) => {
+				const target = event.target;
+				const button = target.closest(
+					'.wc-block-components-product-button [data-product_id]'
+				);
+
+				const isAddToCartButton =
+					button &&
+					button.classList.contains( 'add_to_cart_button' ) &&
+					! button.classList.contains( 'product_type_variable' );
+
+				if ( isAddToCartButton ) {
+					const productData =
+						global._googlesitekit.wcdata.products?.find(
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: Let's destruct `global._googlesitekit.wcdata` at the beginning to avoid repeating it everywhere in this file.
created_at: 2025-05-12T19:22:41Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
+			'.products-block-post-template .product, .wc-block-product-template .product'
+		)
+		?.forEach( ( productCard ) => {
+			// Get the Product ID from a child node containing the relevant attribute.
+			const productID = parseInt(
+				productCard
+					.querySelector( '[data-product_id]' )
+					?.getAttribute( 'data-product_id' ),
+				10
+			);
+
+			if ( ! productID ) {
+				return;
+			}
+
+			productCard.addEventListener( 'click', ( event ) => {
+				const target = event.target;
+				const button = target.closest(
+					'.wc-block-components-product-button [data-product_id]'
+				);
+
+				const isAddToCartButton =
+					button &&
+					button.classList.contains( 'add_to_cart_button' ) &&
+					! button.classList.contains( 'product_type_variable' );
+
+				if ( isAddToCartButton ) {
+					const productData =
+						global._googlesitekit.wcdata.products?.find(
+							( product ) => product?.id === productID
+						) || {};
+					const { price } = productData;
+
+					const eventData = formatEventData(
+						price,
+						currency,
+						productData
+					);
+					global._googlesitekit?.gtagEvent?.(
+						'add_to_cart',
+						eventData
+					);
+				}
+			} );
+		} );
 } )( global.jQuery );
+
+function formatEventData(
path: assets/js/event-providers/woocommerce.js
user: login: zutigrm
body: Makes sense, thanks
created_at: 2025-05-14T06:44:33Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
+			'.products-block-post-template .product, .wc-block-product-template .product'
path: assets/js/event-providers/woocommerce.js
user: login: zutigrm
body: Since those are covering wide array of blocks, it will be a longer list of items, but the tricky part is that we can only hook into the `render_block` and then try to parse each block and append class on proper element, using `WP_HTML_Tag_Processor` instance, etc.

Although in this initial integration we are not concerned about supporting all themes - our original implementation currently has very minimal support, this issue expands it significally, especially around product single page support, product list pages, main collection of blocks, and bringing extra data, etc. This was the main goal, now we will try to support more blocks that we find are not covered with this initial implementation, but we don't need to cover that in this issue, it is planned that it will be filed as follow up after QA tests this and goes through all the blocks  
created_at: 2025-05-14T07:23:33Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,171 @@
 		return;
 	}
 
+	const currency = global._googlesitekit.wcdata.currency;
+
+	if ( global._googlesitekit.wcdata.add_to_cart ) {
+		const { price } = global._googlesitekit.wcdata.add_to_cart;
+
+		const eventData = formatEventData(
+			price,
+			currency,
+			global._googlesitekit.wcdata.add_to_cart
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( global._googlesitekit.wcdata.purchase ) {
+		const { id, totals, items } = global._googlesitekit.wcdata.purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
-	} );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			global._googlesitekit.wcdata.products?.find(
+				( product ) => product?.id === productID
+			) || {};
+		const { price } = productData;
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+		const eventData = formatEventData( price, currency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
+
+	document
+		.querySelectorAll(
+			'.products-block-post-template .product, .wc-block-product-template .product'
path: assets/js/event-providers/woocommerce.js
user: login: zutigrm
body: I opened #10765 , which will be used to research if we can reliably inject our classes to all blocks that can support this approach, after QA tests all the blocks, so it can also include any block not being supported 
created_at: 2025-05-14T07:31:19Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,164 @@
 		return;
 	}
 
+	const {
+		currency: globalCurrency,
+		products: globalProducts,
+		purchase,
+		add_to_cart: addToCart,
+	} = global._googlesitekit?.wcdata;
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: ```suggestion
	} = global._googlesitekit?.wcdata || {};
```
created_at: 2025-05-14T09:06:51Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,164 @@
 		return;
 	}
 
+	const {
+		currency: globalCurrency,
+		products: globalProducts,
+		purchase,
+		add_to_cart: addToCart,
+	} = global._googlesitekit?.wcdata;
+
+	if ( addToCart ) {
+		const { price } = addToCart;
+
+		const eventData = formatEventData( price, globalCurrency, addToCart );
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( purchase ) {
+		const { id, totals, items } = purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: I belive eslint blames `cart_hash` usage. Let's rename it to be `cartHash` instead and remove the eslint comment.
created_at: 2025-05-14T09:09:08Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,164 @@
 		return;
 	}
 
+	const {
+		currency: globalCurrency,
+		products: globalProducts,
+		purchase,
+		add_to_cart: addToCart,
+	} = global._googlesitekit?.wcdata;
+
+	if ( addToCart ) {
+		const { price } = addToCart;
+
+		const eventData = formatEventData( price, globalCurrency, addToCart );
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( purchase ) {
+		const { id, totals, items } = purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: If `productID` is `NaN`, then we should return early and not track this event.
created_at: 2025-05-14T09:11:07Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,164 @@
 		return;
 	}
 
+	const {
+		currency: globalCurrency,
+		products: globalProducts,
+		purchase,
+		add_to_cart: addToCart,
+	} = global._googlesitekit?.wcdata;
+
+	if ( addToCart ) {
+		const { price } = addToCart;
+
+		const eventData = formatEventData( price, globalCurrency, addToCart );
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( purchase ) {
+		const { id, totals, items } = purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
+		const productID = parseInt( $button.data( 'product_id' ), 10 );
+		const productData =
+			globalProducts?.find( ( product ) => product?.id === productID ) ||
+			{};
+		const { price } = productData;
+
+		const eventData = formatEventData( price, globalCurrency, productData );
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
 	} );
 
-	body.on( 'checkout_place_order_success', () => {
-		global._googlesitekit?.gtagEvent?.( 'purchase' );
+	jQuery(
+		'.products-block-post-template .product, .wc-block-product-template .product'
+	).each( function () {
+		const productCard = jQuery( this );
path: assets/js/event-providers/woocommerce.js
user: login: eugene-manuilov
body: Usually, when something is wrapped with jQuery, then the `$` prefix is used. So for this variable its name should be `$productCard`.
created_at: 2025-05-14T09:12:39Z
author_association: COLLABORATOR
diff_hunk: @@ -19,13 +19,164 @@
 		return;
 	}
 
+	const {
+		currency: globalCurrency,
+		products: globalProducts,
+		purchase,
+		add_to_cart: addToCart,
+	} = global._googlesitekit?.wcdata;
+
+	if ( addToCart ) {
+		const { price } = addToCart;
+
+		const eventData = formatEventData( price, globalCurrency, addToCart );
+
+		global._googlesitekit?.gtagEvent?.( 'add_to_cart', eventData );
+	}
+
+	if ( purchase ) {
+		const { id, totals, items } = purchase;
+
+		const eventData = formatEventData(
+			totals.total_price,
+			totals.currency_code,
+			items,
+			id,
+			totals.shipping_total,
+			totals.tax_total
+		);
+
+		global._googlesitekit?.gtagEvent?.( 'purchase', eventData );
+	}
+
 	const body = jQuery( 'body' );
 
-	body.on( 'added_to_cart', () => {
-		global._googlesitekit?.gtagEvent?.( 'add_to_cart' );
+	// eslint-disable-next-line camelcase
+	body.on( 'added_to_cart', ( event, fragments, cart_hash, $button ) => {
path: assets/js/event-providers/woocommerce.js
user: login: zutigrm
body: That's a good point, thanks
created_at: 2025-05-14T10:43:10Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-09T11:16:05Z
body: **Size Change:** +635 B (+0.03%) 

**Total Size:** 2.08 MB



<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size | Change |  |
| :--- | :---: | :---: | :---: |
| `./dist/assets/css/googlesitekit-admin-css-********************.min.css` | 65.9 kB | 0 B |  |
| `./dist/assets/css/googlesitekit-adminbar-css-********************.min.css` | 11.8 kB | 0 B |  |
| `./dist/assets/css/googlesitekit-authorize-application-css-********************.min.css` | 846 B | 0 B |  |
| `./dist/assets/css/googlesitekit-wp-dashboard-css-********************.min.css` | 8.46 kB | 0 B |  |
| `./dist/assets/js/32-********************.js` | 2.76 kB | 0 B |  |
| `./dist/assets/js/33-********************.js` | 2.25 kB | 0 B |  |
| `./dist/assets/js/34-********************.js` | 3.64 kB | 0 B |  |
| `./dist/assets/js/35-********************.js` | 936 B | 0 B |  |
| `./dist/assets/js/36-********************.js` | 892 B | 0 B |  |
| `./dist/assets/js/37-********************.js` | 1.61 kB | 0 B |  |
| `./dist/assets/js/38-********************.js` | 1.57 kB | 0 B |  |
| `./dist/assets/js/39-********************.js` | 1.61 kB | 0 B |  |
| `./dist/assets/js/40-********************.js` | 1.59 kB | 0 B |  |
| `./dist/assets/js/41-********************.js` | 1.82 kB | 0 B |  |
| `./dist/assets/js/42-********************.js` | 3.11 kB | 0 B |  |
| `./dist/assets/js/analytics-advanced-tracking-********************.js` | 903 B | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/block-editor-plugin/editor-styles.css` | 124 B | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/block-editor-plugin/editor-styles.js` | 491 B | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/block-editor-plugin/index.js` | 8.38 kB | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/common/editor-styles.css` | 307 B | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/common/editor-styles.js` | 492 B | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/contribute-with-google/index.js` | 9.4 kB | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/contribute-with-google/non-site-kit-user.js` | 8.9 kB | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/subscribe-with-google/index.js` | 9.41 kB | 0 B |  |
| `./dist/assets/js/blocks/reader-revenue-manager/subscribe-with-google/non-site-kit-user.js` | 8.91 kB | 0 B |  |
| `./dist/assets/js/blocks/sign-in-with-google/editor-styles.css` | 84 B | 0 B |  |
| `./dist/assets/js/blocks/sign-in-with-google/editor-styles.js` | 492 B | 0 B |  |
| `./dist/assets/js/blocks/sign-in-with-google/index.js` | 18.1 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-activation-********************.js` | 24.1 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-ad-blocking-recovery-********************.js` | 55.1 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-adminbar-********************.js` | 35.1 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-api-********************.js` | 10.4 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-components-********************.js` | 6.46 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-consent-mode-********************.js` | 25.6 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-data-********************.js` | 2.38 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-datastore-forms-********************.js` | 9.14 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-datastore-location-********************.js` | 2.08 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-datastore-site-********************.js` | 20.7 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-datastore-ui-********************.js` | 10.2 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-datastore-user-********************.js` | 27.4 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-entity-dashboard-********************.js` | 85.4 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-contact-form-7-********************.js` | 646 B | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-easy-digital-downloads-********************.js` | 977 B | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-mailchimp-********************.js` | 630 B | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-ninja-forms-********************.js` | 717 B | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-optin-monster-********************.js` | 675 B | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-popup-maker-********************.js` | 634 B | 0 B |  |
| `./dist/assets/js/googlesitekit-events-provider-woocommerce-********************.js` | 1.29 kB | +635 B (+96.65%) | 🆘 |
| `./dist/assets/js/googlesitekit-events-provider-wpforms-********************.js` | 633 B | 0 B |  |
| `./dist/assets/js/googlesitekit-i18n-********************.js` | 4.16 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-main-dashboard-********************.js` | 156 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-metric-selection-********************.js` | 52.6 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-********************.js` | 22.7 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-ads-********************.js` | 52.3 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-adsense-********************.js` | 119 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-analytics-4-********************.js` | 196 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-pagespeed-insights-********************.js` | 26 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-reader-revenue-manager-********************.js` | 49.8 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-search-console-********************.js` | 69.7 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-sign-in-with-google-********************.js` | 33.5 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-modules-tagmanager-********************.js` | 32.5 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-notifications-********************.js` | 48.6 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-polyfills-********************.js` | 378 B | 0 B |  |
| `./dist/assets/js/googlesitekit-settings-********************.js` | 130 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-splash-********************.js` | 70.2 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-user-input-********************.js` | 44.5 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-vendor-********************.js` | 313 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-widgets-********************.js` | 103 kB | 0 B |  |
| `./dist/assets/js/googlesitekit-wp-dashboard-********************.js` | 63.6 kB | 0 B |  |
| `./dist/assets/js/runtime-********************.js` | 1.4 kB | 0 B |  |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T11:16:29Z
body: Build files for 8cc15a2054666eeb6154b4761f671acfe5b647f8 have been deleted.
author_association: NONE
user: login: zutigrm
created_at: 2025-05-14T10:55:35Z
body: Thanks @eugene-manuilov , PR updated
author_association: COLLABORATOR