Commit c46edafe867 for woocommerce
commit c46edafe8673438f4684072a2284d6d10529b6b7
Author: Cem Ünalan <raicem@users.noreply.github.com>
Date: Mon Mar 2 16:18:30 2026 +0300
In-App Marketplace: improve Discover page events (#63465)
* In-App Marketplace: add utm_group information to wcadmin_marketplace_discover_viewed event
* In-App Marketplace: add wcadmin_marketplace_discover_group_viewed event
* In-App Marketplace: change group name
* In-App Marketplace: don't use ref for seenGroups
* In-App Marketplace: fix a bug where switching tabs stopping the observer
diff --git a/plugins/woocommerce/client/admin/client/marketplace/components/discover/discover.tsx b/plugins/woocommerce/client/admin/client/marketplace/components/discover/discover.tsx
index 138d99fba55..d8ef942ef62 100644
--- a/plugins/woocommerce/client/admin/client/marketplace/components/discover/discover.tsx
+++ b/plugins/woocommerce/client/admin/client/marketplace/components/discover/discover.tsx
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { useContext, useEffect, useState } from '@wordpress/element';
+import { useContext, useEffect, useRef, useState } from '@wordpress/element';
import { recordEvent } from '@woocommerce/tracks';
/**
@@ -19,20 +19,28 @@ export default function Discover(): JSX.Element | null {
const [ productGroups, setProductGroups ] = useState<
Array< ProductGroup >
>( [] );
+ const groupElements = useRef< Record< string, HTMLDivElement | null > >(
+ {}
+ );
const marketplaceContextValue = useContext( MarketplaceContext );
const { isLoading, setIsLoading } = marketplaceContextValue;
function recordTracksEvent( products: ProductGroup[] ) {
const product_ids = products
.flatMap( ( group ) => group.items )
- .map( ( product ) => {
- return product.id;
- } );
+ .map( ( product ) => product.id );
+ const groups = Object.fromEntries(
+ products.map( ( group ) => [
+ group.id,
+ group.items.map( ( product ) => product.id ),
+ ] )
+ );
// This is a new event specific to the Discover tab, added with Woo 8.4.
recordEvent( 'marketplace_discover_viewed', {
view: 'discover',
product_ids,
+ groups,
} );
// This is the new page view event added with Woo 8.3. It's improved with the marketplace_discover_viewed event
@@ -42,6 +50,14 @@ export default function Discover(): JSX.Element | null {
} );
}
+ function recordGroupViewedTrackEvent( group: ProductGroup ) {
+ recordEvent( 'marketplace_discover_group_viewed', {
+ view: 'discover',
+ group_id: group.id,
+ product_ids: group.items.map( ( product ) => product.id ),
+ } );
+ }
+
// Get the content for this screen
useEffect( () => {
setIsLoading( true );
@@ -62,7 +78,65 @@ export default function Discover(): JSX.Element | null {
.finally( () => {
setIsLoading( false );
} );
- }, [] );
+ }, [ setIsLoading ] );
+
+ useEffect( () => {
+ if (
+ isLoading ||
+ ! productGroups.length ||
+ ! ( 'IntersectionObserver' in window )
+ ) {
+ return;
+ }
+
+ const productGroupsById = new Map(
+ productGroups.map( ( productGroup ) => [
+ productGroup.id,
+ productGroup,
+ ] )
+ );
+ const seenGroups = new Set< string >();
+
+ const observer = new IntersectionObserver(
+ ( entries ) => {
+ entries.forEach( ( entry ) => {
+ if ( ! entry.isIntersecting ) {
+ return;
+ }
+
+ const groupId = ( entry.target as HTMLDivElement ).dataset
+ .groupId;
+
+ if ( ! groupId || seenGroups.has( groupId ) ) {
+ return;
+ }
+
+ const group = productGroupsById.get( groupId );
+
+ if ( ! group ) {
+ return;
+ }
+
+ recordGroupViewedTrackEvent( group );
+ seenGroups.add( groupId );
+ observer.unobserve( entry.target );
+ } );
+ },
+ { threshold: 0.25 }
+ );
+
+ productGroups.forEach( ( group ) => {
+ const groupElement = groupElements.current[ group.id ];
+
+ if ( groupElement ) {
+ observer.observe( groupElement );
+ }
+ } );
+
+ return () => {
+ observer.disconnect();
+ };
+ }, [ isLoading, productGroups ] );
if ( isLoading ) {
return (
@@ -90,6 +164,10 @@ export default function Discover(): JSX.Element | null {
groupURLType={ groups.url_type }
type={ groups.itemType }
cardType={ groups.cardType ?? ProductCardType.regular }
+ groupId={ groups.id }
+ containerRef={ ( element ) => {
+ groupElements.current[ groups.id ] = element;
+ } }
/>
) ) }
</div>
diff --git a/plugins/woocommerce/client/admin/client/marketplace/components/product-list/product-list.tsx b/plugins/woocommerce/client/admin/client/marketplace/components/product-list/product-list.tsx
index 8184c6e88f2..1f836eb3a63 100644
--- a/plugins/woocommerce/client/admin/client/marketplace/components/product-list/product-list.tsx
+++ b/plugins/woocommerce/client/admin/client/marketplace/components/product-list/product-list.tsx
@@ -15,6 +15,8 @@ interface ProductListProps {
cardType?: ProductCardType;
groupURLText: string | null;
groupURLType: 'wc-admin' | 'wp-admin' | 'external' | undefined; // types defined by Link component
+ groupId?: string;
+ containerRef?: ( element: HTMLDivElement | null ) => void;
}
export default function ProductList( props: ProductListProps ): JSX.Element {
@@ -28,10 +30,16 @@ export default function ProductList( props: ProductListProps ): JSX.Element {
groupURLText,
groupURLType,
cardType,
+ groupId,
+ containerRef,
} = props;
return (
- <div className="woocommerce-marketplace__product-list">
+ <div
+ className="woocommerce-marketplace__product-list"
+ data-group-id={ groupId }
+ ref={ containerRef }
+ >
<ProductListHeader
title={ title }
groupURL={ groupURL }