/** * WordPress dependencies */ import { Button } from '@safe-wordpress/components'; import { useSelect } from '@safe-wordpress/data'; import { useState } from '@safe-wordpress/element'; import { addQueryArgs } from '@safe-wordpress/url'; import { _x, sprintf } from '@safe-wordpress/i18n'; /** * External dependencies */ import clsx from 'clsx'; import { Preview, Tooltip } from '@nab/components'; import { store as NAB_DATA } from '@nab/data'; import { store as NAB_EXPERIMENTS } from '@nab/experiments'; import { formatNumber } from '@nab/i18n'; import type { Experiment, ExperimentId, ExperimentTypeName, GoalId, Maybe, ResultStatus, } from '@nab/types'; /** * Internal dependencies */ import './style.scss'; import { SummaryChart } from './summary-chart'; export type AlternativeExperimentProps = { readonly id: ExperimentId; readonly type: ExperimentTypeName; readonly name: Experiment[ 'name' ]; }; export const AlternativeExperiment = ( { id, name, type, }: AlternativeExperimentProps ): JSX.Element => { const [ goalIndex, setGoalIndex ] = useState( 0 ); const minSampleSize = useMinSampleSize(); const { experiment, preview, ecommerce, goalCount, result: { areViewsUnique, status: originalStatus, views = 0, daysRunning = 0, conversionRates = [], values = [], } = {}, } = useResults( id, goalIndex ); const nextGoal = () => setGoalIndex( ( goalIndex + 1 ) % goalCount ); const prevGoal = () => setGoalIndex( ( goalCount + goalIndex - 1 ) % goalCount ); const experimentType = useExperimentType( type ); const Icon = experimentType?.icon ?? ( () => <> ); const areResultsLoaded = !! conversionRates.length; const isThereEnoughData = minSampleSize <= views; let status = originalStatus ?? 'testing'; let message = getStatusMessage( status ); if ( ! areResultsLoaded ) { message = _x( 'Loading…', 'text', 'nelio-ab-testing' ); } else if ( ! isThereEnoughData ) { message = _x( 'There isn’t enough data to find a winning variant', 'text', 'nelio-ab-testing' ); status = 'testing'; } return (

{ Icon && ( ) } { name }

{ _x( 'Collected data about this test:', 'text', 'nelio-ab-testing' ) }

    { goalCount > 1 && (
  • { getShortGoalName( experiment, goalIndex ) }
  • ) }
  • { areViewsUnique ? _x( 'Unique Page Views', 'text (overview)', 'nelio-ab-testing' ) : _x( 'Page Views', 'text (overview)', 'nelio-ab-testing' ) } : { formatNumber( views ) }
  • { _x( 'Days running', 'text', 'nelio-ab-testing' ) } : { formatNumber( daysRunning ) }
{ _x( 'View', 'command', 'nelio-ab-testing' ) }
); }; // ===== // HOOKS // ===== const useExperimentType = ( type: ExperimentTypeName ) => useSelect( ( select ) => select( NAB_EXPERIMENTS ).getExperimentType( type ), [ type ] ); const useMinSampleSize = () => useSelect( ( select ) => select( NAB_DATA ).getPluginSetting( 'minSampleSize' ), [] ); const useResults = ( id: ExperimentId, goalIndex: number ) => { const { experiment, preview, ecommerce, goalCount, ...result } = useSelect( ( select ) => { const { areUniqueResultsVisible, getConversionRatesOfAlternativesInGoal, getDaysRunning, getECommercePlugin, getExperiment, getExperimentResultsStatus, getRevenuesOfAlternativesInGoal, getPageViews, } = select( NAB_DATA ); const exp = getExperiment( id ); const goals = exp?.goals; const goalId: Maybe< GoalId > = goals?.[ goalIndex ]?.id; return { experiment: exp, preview: exp?.links.preview ?? '', ecommerce: getECommercePlugin( id, goalId ), goalCount: goals?.length || 0, areViewsUnique: areUniqueResultsVisible( id ), status: getExperimentResultsStatus( id, goalId ), views: getPageViews( id ), daysRunning: getDaysRunning( id ), conversionRates: getConversionRatesOfAlternativesInGoal( id, goalId ), values: getRevenuesOfAlternativesInGoal( id, goalId ), }; }, [ id, goalIndex ] ); return { experiment, preview, ecommerce, goalCount, result }; }; // ======= // HELPERS // ======= function getStatusMessage( status?: ResultStatus ) { switch ( status ) { case 'winner': return _x( 'There is a winner!', 'text', 'nelio-ab-testing' ); case 'possible-winner': return _x( 'There is a winner with low confidence', 'text', 'nelio-ab-testing' ); case 'testing': default: return _x( 'No winner found', 'text', 'nelio-ab-testing' ); } } function getTooltipGoalName( experiment: Maybe< Experiment >, goalIndex: number ): Maybe< string > { const actualName = getActualGoalName( experiment, goalIndex ); const shortName = getShortGoalName( experiment, goalIndex ); return actualName !== shortName ? actualName : undefined; } function getShortGoalName( experiment: Maybe< Experiment >, goalIndex: number ): string { const name = getActualGoalName( experiment, goalIndex ) ?? ''; const shortName = name.substring( 0, 20 ) + ( name.length > 20 ? '…' : '' ); const defaultName = 0 !== goalIndex ? sprintf( /* translators: %d: Goal index. */ _x( 'Goal %d', 'text (short format)', 'nelio-ab-testing' ), goalIndex ) : _x( 'Default Goal', 'text (short format)', 'nelio-ab-testing' ); return shortName || defaultName; } function getActualGoalName( experiment: Maybe< Experiment >, goalIndex: number ): Maybe< string > { return experiment?.goals[ goalIndex ]?.attributes?.name; }