import { __ } from '@wordpress/i18n';
import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
import { Button } from '@wordpress/components';
import Table from '../shared/Table';
import ABConversionBar from '../shared/ABConversionBar';
import NoDataPlaceholder from '../shared/NoDataPlaceholder';
const getAnalyticsAbTestsAPI = () => {
if ( typeof window === 'undefined' ) {
return {} as Record< string, any >;
}
const growthstack = ( window as any ).growthstack ?? {};
return growthstack.analyticsAbTests ?? {};
};
const formatTrendValue = ( value: number, format: string ) => {
if ( format === 'compact' ) {
if ( value >= 1000000 ) {
return `${ ( value / 1000000 ).toFixed( 1 ) }M`;
}
if ( value >= 1000 ) {
return `${ ( value / 1000 ).toFixed( 1 ) }k`;
}
}
return value.toString();
};
const formatString = ( template: string, ...args: any[] ) => {
let i = 0;
return template.replace( /%s/g, () => args[ i++ ] );
};
const formatDate = ( publishedAt: string ) => {
if ( ! publishedAt ) {
return __( 'Draft', 'liana-with-growthstack' );
}
const date = new Date( publishedAt );
const formattedDate = date.toLocaleDateString( 'fi-FI', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
} );
return formatString( __( 'Since %s', 'liana-with-growthstack' ), formattedDate );
};
const getPlaceholderData = () => [
{
title: 'Ultimate Guide to Web Performance',
type: 'post',
days: 14,
publishedAt: new Date( Date.now() - 14 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 3420,
conversionsA: 145,
conversionsB: 178,
},
{
title: 'Getting Started with A/B Testing',
type: 'post',
days: 7,
publishedAt: new Date( Date.now() - 7 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 1850,
conversionsA: 92,
conversionsB: 87,
},
{
title: 'Product Features Overview',
type: 'page',
days: 21,
publishedAt: new Date( Date.now() - 21 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 5230,
conversionsA: 234,
conversionsB: 289,
},
{
title: 'Contact Us - Get in Touch',
type: 'page',
days: 3,
publishedAt: new Date( Date.now() - 3 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 890,
conversionsA: 45,
conversionsB: 52,
},
{
title: 'Best Practices for 2026',
type: 'post',
days: 10,
publishedAt: new Date( Date.now() - 10 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 2100,
conversionsA: 118,
conversionsB: 105,
},
{
title: 'How to Optimize Your Landing Pages',
type: 'page',
days: 5,
publishedAt: new Date( Date.now() - 5 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 1420,
conversionsA: 68,
conversionsB: 74,
},
{
title: 'Advanced Marketing Strategies',
type: 'post',
days: 28,
publishedAt: new Date( Date.now() - 28 * 24 * 60 * 60 * 1000 ).toISOString(),
views: 4780,
conversionsA: 198,
conversionsB: 215,
},
];
const DefaultABTestsContent = ( props: { upgradeUrl?: string } ) => {
const placeholderData = getPlaceholderData();
return (
<>
{ __( 'A/B Tests', 'liana-with-growthstack' ) }
{ __(
'Active A/B Tests from all posts and pages.',
'liana-with-growthstack'
) }
(
{ row.original.title }
{ row.original.type }
),
},
{
Header: __( 'Running', 'liana-with-growthstack' ),
accessor: 'days',
width: 120,
minWidth: 100,
maxWidth: 120,
Cell: ( { row } ) => (
{ row.original.days === 1
? formatString(
__(
'%s day',
'liana-with-growthstack'
),
row.original.days
)
: formatString(
__(
'%s days',
'liana-with-growthstack'
),
row.original.days
) }
{ formatDate(
row.original.publishedAt
) }
),
},
{
Header: __(
'Conversion Distribution',
'liana-with-growthstack'
),
accessor: 'conversionsA',
width: 140,
minWidth: 130,
maxWidth: 140,
Cell: ( { row } ) => (
),
},
{
Header: __( 'Views', 'liana-with-growthstack' ),
accessor: 'views',
width: 90,
minWidth: 80,
maxWidth: 100,
Cell: ( { row } ) => (
{ formatTrendValue(
row.original.views,
'compact'
) }
),
},
{
Header: ' ',
Cell: ( { row } ) => (
{ __(
'Complete test',
'liana-with-growthstack'
) }
),
},
] }
data={ placeholderData }
/>
{ __( 'Available for Pro version', 'liana-with-growthstack' ) }
{ __(
'Get Liana with GrowthStack Pro to run experiments and compare results between variants. A/B Testing is a powerful way to optimize your content and boost conversions across your site.',
'liana-with-growthstack'
) }
{ props.upgradeUrl && (
{ __( 'Upgrade to Pro', 'liana-with-growthstack' ) }
) }
>
);
};
export default function ABTests( props ) {
const { assetsFolder, upgradeUrl } = props;
const [ dataCache, setDataCache ] = useState( {} );
const [ registryVersion, setRegistryVersion ] = useState( 0 );
const getCachedData = useCallback(
( endpoint ) => {
return dataCache[ endpoint ] || null;
},
[ dataCache ]
);
const setCachedData = useCallback(
( endpoint, data, error = null ) => {
setDataCache( ( prevCache ) => ( {
...prevCache,
[ endpoint ]: { data, error, isFetched: true },
} ) );
},
[ setDataCache ]
);
useEffect( () => {
let unsubscribe: () => void = () => {};
let pollHandle: number | null = null;
let isSubscribed = false;
const attemptSubscribe = () => {
const { subscribeABTestsScreens } = getAnalyticsAbTestsAPI();
if ( typeof subscribeABTestsScreens === 'function' ) {
unsubscribe = subscribeABTestsScreens( () => {
setRegistryVersion( ( current ) => current + 1 );
} );
setRegistryVersion( ( current ) => current + 1 );
isSubscribed = true;
return true;
}
return false;
};
if ( attemptSubscribe() === false && typeof window !== 'undefined' ) {
pollHandle = window.setInterval( () => {
if ( attemptSubscribe() && pollHandle !== null ) {
window.clearInterval( pollHandle );
pollHandle = null;
}
}, 250 );
}
return () => {
if ( pollHandle !== null && typeof window !== 'undefined' ) {
window.clearInterval( pollHandle );
}
if ( isSubscribed && typeof unsubscribe === 'function' ) {
unsubscribe();
}
};
}, [] );
const fillProps = useMemo(
() => ( {
assetsFolder,
getCachedData,
setCachedData,
} ),
[ assetsFolder, getCachedData, setCachedData ]
);
const registeredScreens = useMemo( () => {
const { getRegisteredABTestsScreens } = getAnalyticsAbTestsAPI();
if ( typeof getRegisteredABTestsScreens === 'function' ) {
return getRegisteredABTestsScreens().filter(
( FillComponent: any ) => typeof FillComponent === 'function'
);
}
return [];
}, [ registryVersion ] );
const hasCustomScreen = registeredScreens.length > 0;
const { ABTestsSlot: SlotComponent } = getAnalyticsAbTestsAPI();
const canRenderSlot =
hasCustomScreen && typeof SlotComponent === 'function';
return (
<>
{ registeredScreens.map( ( FillComponent, index ) => (
) ) }
{ canRenderSlot ? (
) : (
) }
>
);
}