diff --git a/src/app/app.js b/src/app/app.js index ca85e886d31..69d70987a8d 100644 --- a/src/app/app.js +++ b/src/app/app.js @@ -10,8 +10,9 @@ import { applyFilters } from '@wordpress/hooks'; import { AdminNotes } from '../admin-notes'; import { default as Tools } from '../tools'; import { default as Options } from '../options'; +import { default as Experiments } from '../experiments'; -const tabs = applyFilters( 'woocommerce_admin_test_helper_tabs', [ +const tabs = applyFilters('woocommerce_admin_test_helper_tabs', [ { name: 'options', title: 'Options', @@ -27,7 +28,12 @@ const tabs = applyFilters( 'woocommerce_admin_test_helper_tabs', [ title: 'Tools', content: , }, -] ); + { + name: 'experiments', + title: 'Experiments', + content: , + }, +]); export function App() { return ( @@ -36,18 +42,18 @@ export function App() { - { ( tab ) => ( + {(tab) => ( <> - { tab.content } - { applyFilters( - `woocommerce_admin_test_helper_tab_${ tab.name }`, + {tab.content} + {applyFilters( + `woocommerce_admin_test_helper_tab_${tab.name}`, [] - ) } + )} - ) } + )} ); diff --git a/src/experiments/data/action-types.js b/src/experiments/data/action-types.js new file mode 100644 index 00000000000..9e435812f39 --- /dev/null +++ b/src/experiments/data/action-types.js @@ -0,0 +1,6 @@ +const TYPES = { + TOGGLE_EXPERIMENT: 'TOGGLE_EXPERIMENT', + SET_EXPERIMENTS: 'SET_EXPERIMENTS', +}; + +export default TYPES; diff --git a/src/experiments/data/actions.js b/src/experiments/data/actions.js new file mode 100644 index 00000000000..12647ff5952 --- /dev/null +++ b/src/experiments/data/actions.js @@ -0,0 +1,34 @@ +/** + * Internal dependencies + */ +import TYPES from './action-types'; +import { EXPERIMENT_NAME_PREFIX } from './constants'; + +export function toggleExperiment(experimentName) { + const storageItem = JSON.parse( + window.localStorage.getItem(EXPERIMENT_NAME_PREFIX + experimentName) + ); + + const newVariation = + storageItem.variationName === 'control' ? 'treatment' : 'control'; + + storageItem.variationName = newVariation; + + window.localStorage.setItem( + EXPERIMENT_NAME_PREFIX + experimentName, + JSON.stringify(storageItem) + ); + + return { + type: TYPES.TOGGLE_EXPERIMENT, + experimentName, + newVariation, + }; +} + +export function setExperiments(experiments) { + return { + type: TYPES.SET_EXPERIMENTS, + experiments, + }; +} diff --git a/src/experiments/data/constants.js b/src/experiments/data/constants.js new file mode 100644 index 00000000000..9c918cb46e6 --- /dev/null +++ b/src/experiments/data/constants.js @@ -0,0 +1,2 @@ +export const STORE_KEY = 'wc-admin-helper/experiments'; +export const EXPERIMENT_NAME_PREFIX = 'explat-experiment--'; diff --git a/src/experiments/data/index.js b/src/experiments/data/index.js new file mode 100644 index 00000000000..969a750b7ad --- /dev/null +++ b/src/experiments/data/index.js @@ -0,0 +1,22 @@ +/** + * External dependencies + */ +import { registerStore } from '@wordpress/data'; +import { controls } from '@wordpress/data-controls'; + +/** + * Internal dependencies + */ +import * as actions from './actions'; +import * as resolvers from './resolvers'; +import * as selectors from './selectors'; +import reducer from './reducer'; +import { STORE_KEY } from './constants'; + +export default registerStore(STORE_KEY, { + actions, + selectors, + resolvers, + controls, + reducer, +}); diff --git a/src/experiments/data/reducer.js b/src/experiments/data/reducer.js new file mode 100644 index 00000000000..982ea63a256 --- /dev/null +++ b/src/experiments/data/reducer.js @@ -0,0 +1,34 @@ +/** + * Internal dependencies + */ +import TYPES from './action-types'; + +const DEFAULT_STATE = { + experiments: [], +}; + +const reducer = (state = DEFAULT_STATE, action) => { + switch (action.type) { + case TYPES.TOGGLE_EXPERIMENT: + let experiments = [...state.experiments]; + experiments = experiments.map((experiment) => { + if (experiment.name === action.experimentName) { + experiment.variation = action.newVariation; + } + return experiment; + }); + return { + ...state, + experiments, + }; + case TYPES.SET_EXPERIMENTS: + return { + ...state, + experiments: action.experiments, + }; + default: + return state; + } +}; + +export default reducer; diff --git a/src/experiments/data/resolvers.js b/src/experiments/data/resolvers.js new file mode 100644 index 00000000000..d2acdb5458c --- /dev/null +++ b/src/experiments/data/resolvers.js @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +import { setExperiments } from './actions'; +import { EXPERIMENT_NAME_PREFIX } from './constants'; + +export function* getExperiments() { + const storageItems = Object.entries({ ...window.localStorage }).filter( + (item) => { + if (item[0].indexOf(EXPERIMENT_NAME_PREFIX) === 0) { + return true; + } + return false; + } + ); + + const experiments = []; + storageItems.forEach((storageItem) => { + const [key, value] = storageItem; + const objectValue = JSON.parse(value); + + const experiment = { + name: key.replace(EXPERIMENT_NAME_PREFIX, ''), + variation: objectValue.variationName + ? objectValue.variationName + : 'control', + }; + experiments.push(experiment); + }); + + yield setExperiments(experiments); +} diff --git a/src/experiments/data/selectors.js b/src/experiments/data/selectors.js new file mode 100644 index 00000000000..83d271626cb --- /dev/null +++ b/src/experiments/data/selectors.js @@ -0,0 +1,3 @@ +export function getExperiments(state) { + return state.experiments; +} diff --git a/src/experiments/index.js b/src/experiments/index.js new file mode 100644 index 00000000000..c262bead923 --- /dev/null +++ b/src/experiments/index.js @@ -0,0 +1,65 @@ +/** + * External dependencies + */ +import { withDispatch, withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { STORE_KEY } from './data/constants'; +import './data'; + +function Experiments({ experiments, toggleExperiment }) { + return ( +
+

Experiments

+ + + + + + + + + + {experiments.map(({ name, variation }, index) => { + return ( + + + + + + ); + })} + +
ExperimentVariationToggle
{name}{variation} + +
+
+ ); +} + +export default compose( + withSelect((select) => { + const { getExperiments } = select(STORE_KEY); + return { + experiments: getExperiments(), + }; + }), + withDispatch((dispatch) => { + const { toggleExperiment } = dispatch(STORE_KEY); + + return { + toggleExperiment, + }; + }) +)(Experiments); diff --git a/src/index.scss b/src/index.scss index 7e82c41a608..28e61ed7cc5 100644 --- a/src/index.scss +++ b/src/index.scss @@ -51,8 +51,8 @@ float: right; } -#wc-admin-test-helper-tools { - table.tools { +#wc-admin-test-helper-tools, #wc-admin-test-helper-experiments { + table.tools, table.experiments { thead th { text-align: center; }