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
+
+
+
+ Experiment |
+ Variation |
+ Toggle |
+
+
+
+ {experiments.map(({ name, variation }, index) => {
+ return (
+
+ {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;
}