/** * WordPress dependencies */ import apiFetch from '@safe-wordpress/api-fetch'; import { Button, Popover } from '@safe-wordpress/components'; import { useState } from '@safe-wordpress/element'; import { useDispatch, useSelect } from '@safe-wordpress/data'; import { _x } from '@safe-wordpress/i18n'; import { store as NOTICES } from '@safe-wordpress/notices'; /** * External dependencies */ import clsx from 'clsx'; import { v4 as uuid } from 'uuid'; import { filter, groupBy, map, mapValues, values } from 'lodash'; import { store as NAB_DATA } from '@nab/data'; import { store as NAB_EXPERIMENTS } from '@nab/experiments'; import { useIsExperimentTypeDisabled } from '@nab/utils'; import type { Experiment, ExperimentCategory, ExperimentTypeName, } from '@nab/types'; /** * Internal dependencies */ import './style.scss'; const CATEGORY_LABELS: Record< ExperimentCategory, string > = { page: _x( 'Page', 'text', 'nelio-ab-testing' ), global: _x( 'Global', 'text', 'nelio-ab-testing' ), woocommerce: _x( 'WooCommerce', 'text', 'nelio-ab-testing' ), other: _x( 'Other', 'text', 'nelio-ab-testing' ), }; export const NewTestButton = (): JSX.Element => { const [ isVisible, setVisible ] = useState( false ); const experimentTypes = useExperimentTypes(); return ( { isVisible && ( setVisible( false ) } > { values( mapValues( experimentTypes, ( types: ReadonlyArray< ExperimentTypeName >, category: ExperimentCategory ) => !! types.length && (

{ CATEGORY_LABELS[ category ] }

    { types.map( ( type ) => ( setVisible( false ) } /> ) ) }
) ) ) }
) }
); }; // ============ // HELPER VIEWS // ============ type ExperimentTypeProps = { readonly type: ExperimentTypeName; readonly onCreationFail: () => void; }; const ExperimentType = ( { type, onCreationFail, }: ExperimentTypeProps ): JSX.Element | null => { const [ isCreatingOfType, createExperiment ] = useExperimentCreator( onCreationFail ); const experimentType = useExperimentType( type ); const postTypes = usePostTypes(); const isDisabled = useIsExperimentTypeDisabled( experimentType, postTypes ); if ( ! experimentType ) { return null; } const { title, shortTitle, description, icon: Icon } = experimentType; return (
  • ); }; // ===== // HOOKS // ===== const useExperimentCreator = ( onCreationFail: () => void ) => { const [ creatingType, setCreatingType ] = useState( '' ); const showError = useShowError(); const experimentTypes = useSelect( ( select ) => select( NAB_EXPERIMENTS ).getExperimentTypes(), [] ); const create = ( typeName: ExperimentTypeName ) => { setCreatingType( typeName ); const experimentType = experimentTypes[ typeName ]; const addTestedPostScopeRule = 'tested-post-with-consistency' === experimentType?.supports?.scope || 'tested-product-with-consistency' === experimentType?.supports?.scope; void apiFetch< Experiment >( { path: '/nab/v1/experiment', method: 'post', data: { type: typeName, addTestedPostScopeRule, }, } ) .then( ( experiment ) => { window.location.href = experiment.links.edit; } ) .catch( ( err: { message: string } ) => { setCreatingType( '' ); onCreationFail(); showError( err.message ); } ); }; return [ creatingType, create ] as const; }; const useShowError = () => { const { createErrorNotice, removeNotice } = useDispatch( NOTICES ); const defaultMessage = _x( 'Unknown error while creating test.', 'text', 'nelio-ab-testing' ); return ( message: string ) => { const id = uuid(); const msg = message || defaultMessage; void createErrorNotice( msg, { id, onDismiss: () => removeNotice( id ), } ); }; }; const useExperimentTypes = (): Record< ExperimentCategory, ReadonlyArray< ExperimentTypeName > > => { const experimentTypes = useSelect( ( select ) => select( NAB_EXPERIMENTS ).getExperimentTypes(), [] ); const types = mapValues( groupBy( values( experimentTypes ), 'category' ), ( ts ) => map( ts, 'name' ) ); return { page: [], global: [], woocommerce: [], other: [], ...types, }; }; const useExperimentType = ( typeName: ExperimentTypeName ) => useSelect( ( select ) => { const types = select( NAB_EXPERIMENTS ).getExperimentTypes(); const type = types[ typeName ]; if ( ! type ) { return; } return type; }, [ typeName ] ); const usePostTypes = () => useSelect( ( select ) => filter( select( NAB_DATA ).getKindEntities(), { kind: 'entity' } ), [] );