Merge pull request #34 from woocommerce/add-features-helper
Add features tab
This commit is contained in:
commit
b1d0940db3
|
@ -36,3 +36,4 @@ require( 'tools/delete-all-products.php');
|
|||
require( 'tools/disable-wc-email.php' );
|
||||
require( 'tools/trigger-update-callbacks.php' );
|
||||
require( 'tracks/tracks-debug-log.php' );
|
||||
require( 'features/features.php' );
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
|
||||
const OPTION_NAME_PREFIX = 'wc_admin_helper_feature_values';
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/features/(?P<feature_name>[a-z0-9_\-]+)/toggle',
|
||||
'toggle_feature',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/features',
|
||||
'get_features',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/features/reset',
|
||||
'reset_features',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
function toggle_feature( $request ) {
|
||||
$features = get_features();
|
||||
$custom_feature_values = get_option( OPTION_NAME_PREFIX, array() );
|
||||
$feature_name = $request->get_param( 'feature_name' );
|
||||
|
||||
if ( ! isset( $features[$feature_name ]) ) {
|
||||
return new WP_REST_Response( $features, 204 );
|
||||
}
|
||||
|
||||
if ( isset( $custom_feature_values[$feature_name] ) ) {
|
||||
unset( $custom_feature_values[$feature_name] );
|
||||
} else {
|
||||
$custom_feature_values[$feature_name] = ! $features[ $feature_name ];
|
||||
}
|
||||
|
||||
update_option(OPTION_NAME_PREFIX, $custom_feature_values );
|
||||
return new WP_REST_Response( get_features(), 200 );
|
||||
}
|
||||
|
||||
function reset_features() {
|
||||
delete_option( OPTION_NAME_PREFIX );
|
||||
return new WP_REST_Response( get_features(), 200 );
|
||||
}
|
||||
|
||||
function get_features() {
|
||||
if ( function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
return apply_filters( 'woocommerce_admin_get_feature_config', wc_admin_get_feature_config() );
|
||||
}
|
||||
return array();
|
||||
}
|
10
plugin.php
10
plugin.php
|
@ -14,3 +14,13 @@ add_action( 'admin_menu', function() {
|
|||
add_action( 'wp_loaded', function() {
|
||||
require( 'api/api.php' );
|
||||
} );
|
||||
|
||||
add_filter( 'woocommerce_admin_get_feature_config', function( $feature_config ) {
|
||||
$custom_feature_values = get_option( 'wc_admin_helper_feature_values', array() );
|
||||
foreach ( $custom_feature_values as $feature => $value ) {
|
||||
if ( isset( $feature_config[$feature] ) ) {
|
||||
$feature_config[$feature] = $value;
|
||||
}
|
||||
}
|
||||
return $feature_config;
|
||||
} );
|
|
@ -11,6 +11,7 @@ import { AdminNotes } from '../admin-notes';
|
|||
import { default as Tools } from '../tools';
|
||||
import { default as Options } from '../options';
|
||||
import { default as Experiments } from '../experiments';
|
||||
import { default as Features } from '../features';
|
||||
|
||||
const tabs = applyFilters('woocommerce_admin_test_helper_tabs', [
|
||||
{
|
||||
|
@ -33,6 +34,11 @@ const tabs = applyFilters('woocommerce_admin_test_helper_tabs', [
|
|||
title: 'Experiments',
|
||||
content: <Experiments />,
|
||||
},
|
||||
{
|
||||
name: 'features',
|
||||
title: 'Features',
|
||||
content: <Features />,
|
||||
},
|
||||
]);
|
||||
|
||||
export function App() {
|
||||
|
|
|
@ -9,42 +9,42 @@ import { apiFetch } from '@wordpress/data-controls';
|
|||
import TYPES from './action-types';
|
||||
import { EXPERIMENT_NAME_PREFIX, TRANSIENT_NAME_PREFIX } from './constants';
|
||||
|
||||
function toggleFrontendExperiment( experimentName, newVariation ) {
|
||||
function toggleFrontendExperiment(experimentName, newVariation) {
|
||||
const storageItem = JSON.parse(
|
||||
window.localStorage.getItem( EXPERIMENT_NAME_PREFIX + experimentName )
|
||||
window.localStorage.getItem(EXPERIMENT_NAME_PREFIX + experimentName)
|
||||
);
|
||||
|
||||
storageItem.variationName = newVariation;
|
||||
|
||||
window.localStorage.setItem(
|
||||
EXPERIMENT_NAME_PREFIX + experimentName,
|
||||
JSON.stringify( storageItem )
|
||||
JSON.stringify(storageItem)
|
||||
);
|
||||
}
|
||||
|
||||
function* toggleBackendExperiment( experimentName, newVariation ) {
|
||||
function* toggleBackendExperiment(experimentName, newVariation) {
|
||||
try {
|
||||
const payload = {};
|
||||
payload[ TRANSIENT_NAME_PREFIX + experimentName ] = newVariation;
|
||||
yield apiFetch( {
|
||||
payload[TRANSIENT_NAME_PREFIX + experimentName] = newVariation;
|
||||
yield apiFetch({
|
||||
method: 'POST',
|
||||
path: '/wc-admin/options',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify( payload ),
|
||||
} );
|
||||
} catch ( error ) {
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* toggleExperiment( experimentName, currentVariation, source ) {
|
||||
export function* toggleExperiment(experimentName, currentVariation, source) {
|
||||
const newVariation =
|
||||
currentVariation === 'control' ? 'treatment' : 'control';
|
||||
|
||||
if ( source === 'frontend' ) {
|
||||
toggleFrontendExperiment( experimentName, newVariation );
|
||||
if (source === 'frontend') {
|
||||
toggleFrontendExperiment(experimentName, newVariation);
|
||||
} else {
|
||||
yield toggleBackendExperiment( experimentName, newVariation );
|
||||
yield toggleBackendExperiment(experimentName, newVariation);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -55,7 +55,7 @@ export function* toggleExperiment( experimentName, currentVariation, source ) {
|
|||
};
|
||||
}
|
||||
|
||||
export function setExperiments( experiments ) {
|
||||
export function setExperiments(experiments) {
|
||||
return {
|
||||
type: TYPES.SET_EXPERIMENTS,
|
||||
experiments,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
const TYPES = {
|
||||
TOGGLE_FEATURE: 'TOGGLE_FEATURE',
|
||||
SET_FEATURES: 'SET_FEATURES',
|
||||
SET_MODIFIED_FEATURES: 'SET_MODIFIED_FEATURES',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
import { controls } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { API_NAMESPACE, STORE_KEY } from './constants';
|
||||
|
||||
export function* resetModifiedFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/features/reset`,
|
||||
method: 'POST',
|
||||
} );
|
||||
|
||||
yield setModifiedFeatures( [] );
|
||||
yield setFeatures( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* toggleFeature( featureName ) {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: API_NAMESPACE + '/features/' + featureName + '/toggle',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
} );
|
||||
yield setFeatures( response );
|
||||
yield controls.dispatch(
|
||||
STORE_KEY,
|
||||
'invalidateResolutionForStoreSelector',
|
||||
'getModifiedFeatures'
|
||||
);
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function setFeatures( features ) {
|
||||
return {
|
||||
type: TYPES.SET_FEATURES,
|
||||
features,
|
||||
};
|
||||
}
|
||||
|
||||
export function setModifiedFeatures( modifiedFeatures ) {
|
||||
return {
|
||||
type: TYPES.SET_MODIFIED_FEATURES,
|
||||
modifiedFeatures,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/features';
|
||||
export const OPTION_NAME_PREFIX = 'wc_admin_helper_feature_values';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -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,
|
||||
} );
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
features: {},
|
||||
modifiedFeatures: [],
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.SET_MODIFIED_FEATURES:
|
||||
return {
|
||||
...state,
|
||||
modifiedFeatures: action.modifiedFeatures,
|
||||
};
|
||||
case TYPES.SET_FEATURES:
|
||||
return {
|
||||
...state,
|
||||
features: action.features,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { setFeatures, setModifiedFeatures } from './actions';
|
||||
import { API_NAMESPACE, OPTION_NAME_PREFIX } from './constants';
|
||||
|
||||
export function* getModifiedFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `wc-admin/options?options=` + OPTION_NAME_PREFIX,
|
||||
} );
|
||||
|
||||
yield setModifiedFeatures(
|
||||
response && response[ OPTION_NAME_PREFIX ]
|
||||
? Object.keys( response[ OPTION_NAME_PREFIX ] )
|
||||
: []
|
||||
);
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* getFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/features`,
|
||||
} );
|
||||
|
||||
yield setFeatures( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export function getFeatures( state ) {
|
||||
return state.features;
|
||||
}
|
||||
|
||||
export function getModifiedFeatures( state ) {
|
||||
return state.modifiedFeatures;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function Features() {
|
||||
const { features = {}, modifiedFeatures = [] } = useSelect( ( select ) => {
|
||||
const { getFeatures, getModifiedFeatures } = select( STORE_KEY );
|
||||
return {
|
||||
features: getFeatures(),
|
||||
modifiedFeatures: getModifiedFeatures(),
|
||||
};
|
||||
} );
|
||||
|
||||
const { toggleFeature, resetModifiedFeatures } = useDispatch( STORE_KEY );
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-features">
|
||||
<h2>
|
||||
Features
|
||||
<Button
|
||||
disabled={ modifiedFeatures.length === 0 }
|
||||
onClick={ () => resetModifiedFeatures() }
|
||||
isSecondary
|
||||
style={ { marginLeft: '24px' } }
|
||||
>
|
||||
Reset to defaults
|
||||
</Button>
|
||||
</h2>
|
||||
<table className="features wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Feature Name</th>
|
||||
<th>Enabled?</th>
|
||||
<th>Toggle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ Object.keys( features ).map( ( feature_name ) => {
|
||||
return (
|
||||
<tr key={ feature_name }>
|
||||
<td className="feature-name">
|
||||
{ feature_name }
|
||||
</td>
|
||||
<td>{ features[ feature_name ].toString() }</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleFeature( feature_name );
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Features;
|
Loading…
Reference in New Issue