/**
* 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' } ),
[]
);