Merge pull request #46 from woocommerce/update/allow-manual-experiment-input
Allow adding a new experiment manually
This commit is contained in:
commit
69f89dc11c
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function NewExperimentForm( { addExperiment } ) {
|
||||
const [ experimentName, setExperimentName ] = useState( null );
|
||||
const [ variation, setVariation ] = useState( 'treatment' );
|
||||
|
||||
const getInputValue = ( event ) => {
|
||||
setExperimentName( event.target.value );
|
||||
};
|
||||
|
||||
const getVariationInput = ( event ) => {
|
||||
setVariation( event.target.value );
|
||||
};
|
||||
|
||||
const AddNewExperiment = () => {
|
||||
addExperiment( experimentName, variation );
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="manual-input">
|
||||
<div className="description">
|
||||
Don't see an experiment you want to test? Add it manually.
|
||||
</div>
|
||||
<input type="text" onChange={ getInputValue } />
|
||||
<select value={ variation } onChange={ getVariationInput }>
|
||||
<option value="treatment">treatment</option>
|
||||
<option value="control">control</option>
|
||||
</select>
|
||||
|
||||
<Button isPrimary onClick={ AddNewExperiment }>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { addExperiment } = dispatch( STORE_KEY );
|
||||
return {
|
||||
addExperiment,
|
||||
};
|
||||
} )
|
||||
)( NewExperimentForm );
|
|
@ -1,6 +1,8 @@
|
|||
const TYPES = {
|
||||
TOGGLE_EXPERIMENT: 'TOGGLE_EXPERIMENT',
|
||||
SET_EXPERIMENTS: 'SET_EXPERIMENTS',
|
||||
ADD_EXPERIMENT: 'ADD_EXPERIMENT',
|
||||
DELETE_EXPERIMENT: 'DELETE_EXPERIMENT',
|
||||
};
|
||||
|
||||
export default TYPES;
|
||||
|
|
|
@ -14,10 +14,18 @@ import {
|
|||
} from './constants';
|
||||
|
||||
function toggleFrontendExperiment( experimentName, newVariation ) {
|
||||
const storageItem = JSON.parse(
|
||||
let storageItem = JSON.parse(
|
||||
window.localStorage.getItem( EXPERIMENT_NAME_PREFIX + experimentName )
|
||||
);
|
||||
|
||||
// If the experiment is not in localStorage, consider it as a new.
|
||||
if ( storageItem === null ) {
|
||||
storageItem = {
|
||||
experimentName,
|
||||
retrievedTimestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
storageItem.variationName = newVariation;
|
||||
storageItem.ttl = 3600;
|
||||
|
||||
|
@ -45,21 +53,17 @@ function* toggleBackendExperiment( experimentName, newVariation ) {
|
|||
}
|
||||
}
|
||||
|
||||
export function* toggleExperiment( experimentName, currentVariation, source ) {
|
||||
export function* toggleExperiment( experimentName, currentVariation ) {
|
||||
const newVariation =
|
||||
currentVariation === 'control' ? 'treatment' : 'control';
|
||||
|
||||
if ( source === 'frontend' ) {
|
||||
toggleFrontendExperiment( experimentName, newVariation );
|
||||
} else {
|
||||
yield toggleBackendExperiment( experimentName, newVariation );
|
||||
}
|
||||
toggleFrontendExperiment( experimentName, newVariation );
|
||||
yield toggleBackendExperiment( experimentName, newVariation );
|
||||
|
||||
return {
|
||||
type: TYPES.TOGGLE_EXPERIMENT,
|
||||
experimentName,
|
||||
newVariation,
|
||||
source,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -69,3 +73,33 @@ export function setExperiments( experiments ) {
|
|||
experiments,
|
||||
};
|
||||
}
|
||||
|
||||
export function* addExperiment( experimentName, variation ) {
|
||||
toggleFrontendExperiment( experimentName, variation );
|
||||
yield toggleBackendExperiment( experimentName, variation );
|
||||
|
||||
return {
|
||||
type: TYPES.ADD_EXPERIMENT,
|
||||
experimentName,
|
||||
variation,
|
||||
};
|
||||
}
|
||||
|
||||
export function* deleteExperiment( experimentName ) {
|
||||
window.localStorage.removeItem( EXPERIMENT_NAME_PREFIX + experimentName );
|
||||
|
||||
const optionNames = [
|
||||
TRANSIENT_NAME_PREFIX + experimentName,
|
||||
TRANSIENT_TIMEOUT_NAME_PREFIX + experimentName,
|
||||
];
|
||||
|
||||
yield apiFetch( {
|
||||
method: 'DELETE',
|
||||
path: '/wc-admin-test-helper/options/' + optionNames.join( ',' ),
|
||||
} );
|
||||
|
||||
return {
|
||||
type: TYPES.DELETE_EXPERIMENT,
|
||||
experimentName,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,14 +9,52 @@ const DEFAULT_STATE = {
|
|||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.DELETE_EXPERIMENT:
|
||||
return {
|
||||
...state,
|
||||
experiments: state.experiments.filter( ( experiment ) => {
|
||||
return experiment.name !== action.experimentName;
|
||||
} ),
|
||||
};
|
||||
case TYPES.ADD_EXPERIMENT:
|
||||
const existingExperimentIndex = state.experiments.findIndex(
|
||||
( element ) => {
|
||||
return element.name === action.experimentName;
|
||||
}
|
||||
);
|
||||
const newExperiment = {
|
||||
name: action.experimentName,
|
||||
variation: action.variation,
|
||||
};
|
||||
const newExperiments =
|
||||
existingExperimentIndex !== -1
|
||||
? state.experiments
|
||||
.slice( 0, existingExperimentIndex )
|
||||
.concat( newExperiment )
|
||||
.concat(
|
||||
state.experiments.slice(
|
||||
existingExperimentIndex + 1
|
||||
)
|
||||
)
|
||||
: [
|
||||
...state.experiments,
|
||||
{
|
||||
name: action.experimentName,
|
||||
variation: action.variation,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
...state,
|
||||
experiments: newExperiments,
|
||||
};
|
||||
case TYPES.TOGGLE_EXPERIMENT:
|
||||
return {
|
||||
...state,
|
||||
experiments: state.experiments.map( ( experiment ) => ( {
|
||||
...experiment,
|
||||
variation:
|
||||
experiment.name === action.experimentName &&
|
||||
experiment.source === action.source
|
||||
experiment.name === action.experimentName
|
||||
? action.newVariation
|
||||
: experiment.variation,
|
||||
} ) ),
|
||||
|
|
|
@ -26,7 +26,6 @@ function getExperimentsFromFrontend() {
|
|||
return {
|
||||
name: key.replace( EXPERIMENT_NAME_PREFIX, '' ),
|
||||
variation: objectValue.variationName || 'control',
|
||||
source: 'frontend',
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
@ -47,12 +46,22 @@ export function* getExperiments() {
|
|||
experiment.option_value === 'control'
|
||||
? 'control'
|
||||
: 'treatment',
|
||||
source: 'backend',
|
||||
};
|
||||
} );
|
||||
yield setExperiments(
|
||||
getExperimentsFromFrontend().concat( experimentsFromBackend )
|
||||
);
|
||||
|
||||
// Remove duplicate.
|
||||
const experiments = getExperimentsFromFrontend()
|
||||
.concat( experimentsFromBackend )
|
||||
.filter(
|
||||
( value, index, self ) =>
|
||||
index ===
|
||||
self.findIndex(
|
||||
( t ) =>
|
||||
t.place === value.place && t.name === value.name
|
||||
)
|
||||
);
|
||||
|
||||
yield setExperiments( experiments );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
|
|
|
@ -10,16 +10,19 @@ import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
|||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
import NewExperimentForm from './NewExperimentForm';
|
||||
|
||||
function Experiments( {
|
||||
experiments,
|
||||
toggleExperiment,
|
||||
deleteExperiment,
|
||||
isTrackingEnabled,
|
||||
isResolving,
|
||||
} ) {
|
||||
if ( isResolving ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-experiments">
|
||||
<h2>Experiments</h2>
|
||||
|
@ -43,43 +46,42 @@ function Experiments( {
|
|||
<b>Allow usage of WooCommerce to be tracked</b>.
|
||||
</p>
|
||||
) }
|
||||
<NewExperimentForm />
|
||||
<table className="experiments wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Experiment</th>
|
||||
<th>Variation</th>
|
||||
<th>Source</th>
|
||||
<th>Toggle</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ experiments.map(
|
||||
( { name, variation, source }, index ) => {
|
||||
return (
|
||||
<tr key={ index }>
|
||||
<td className="experiment-name">
|
||||
{ name }
|
||||
</td>
|
||||
<td align="center">{ variation }</td>
|
||||
<td align="center">{ source }</td>
|
||||
<td align="center">
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleExperiment(
|
||||
name,
|
||||
variation,
|
||||
source
|
||||
);
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
) }
|
||||
{ experiments.map( ( { name, variation }, index ) => {
|
||||
return (
|
||||
<tr key={ index }>
|
||||
<td className="experiment-name">{ name }</td>
|
||||
<td align="center">{ variation }</td>
|
||||
<td className="actions" align="center">
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleExperiment( name, variation );
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
deleteExperiment( name );
|
||||
} }
|
||||
className="btn btn-danger"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -98,10 +100,11 @@ export default compose(
|
|||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { toggleExperiment } = dispatch( STORE_KEY );
|
||||
const { toggleExperiment, deleteExperiment } = dispatch( STORE_KEY );
|
||||
|
||||
return {
|
||||
toggleExperiment,
|
||||
deleteExperiment,
|
||||
};
|
||||
} )
|
||||
)( Experiments );
|
||||
|
|
|
@ -92,6 +92,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
#wc-admin-test-helper-experiments {
|
||||
.actions {
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.manual-input {
|
||||
margin-bottom: 20px;
|
||||
float: right;
|
||||
.description {
|
||||
text-align: right;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
button {
|
||||
height: 34px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
input {
|
||||
height: 34px;
|
||||
width: 250px;
|
||||
}
|
||||
select {
|
||||
height: 34px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#wc-admin-test-helper-rest-api-filters {
|
||||
.btn-new {
|
||||
float: right;
|
||||
|
|
Loading…
Reference in New Issue