From 8b07508f30357d5ff25028c48bcd5383f1479d75 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 7 Feb 2019 10:35:25 +0100 Subject: [PATCH] Add registry-aware controls --- packages/core-data/src/controls.js | 8 ++-- packages/data/src/factory.js | 15 ++++++- packages/data/src/index.js | 2 +- packages/data/src/namespace-store.js | 32 +++++++------- packages/data/src/plugins/controls/index.js | 6 ++- .../data/src/plugins/controls/test/index.js | 44 +++++++++++++++++++ 6 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 packages/data/src/plugins/controls/test/index.js diff --git a/packages/core-data/src/controls.js b/packages/core-data/src/controls.js index 32adbc62aa346..d7544e5b30c9b 100644 --- a/packages/core-data/src/controls.js +++ b/packages/core-data/src/controls.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { default as triggerApiFetch } from '@wordpress/api-fetch'; -import { select as selectData } from '@wordpress/data'; +import { createRegistryControl } from '@wordpress/data'; /** * Trigger an API Fetch request. @@ -37,9 +37,9 @@ const controls = { return triggerApiFetch( request ); }, - SELECT( { selectorName, args } ) { - return selectData( 'core' )[ selectorName ]( ...args ); - }, + SELECT: createRegistryControl( ( registry ) => ( { selectorName, args } ) => { + return registry.select( 'core' )[ selectorName ]( ...args ); + } ), }; export default controls; diff --git a/packages/data/src/factory.js b/packages/data/src/factory.js index 866ce64052839..f7dbb7f269a86 100644 --- a/packages/data/src/factory.js +++ b/packages/data/src/factory.js @@ -1,5 +1,5 @@ /** - * Mark a function as a registry selector. + * Mark a selector as a registry selector. * * @param {function} registrySelector Function receiving a registry object and returning a state selector. * @@ -10,3 +10,16 @@ export function createRegistrySelector( registrySelector ) { return registrySelector; } + +/** + * Mark a control as a registry control. + * + * @param {function} registryControl Function receiving a registry object and returning a control. + * + * @return {function} marked registry control. + */ +export function createRegistryControl( registryControl ) { + registryControl.isRegistryControl = true; + + return registryControl; +} diff --git a/packages/data/src/index.js b/packages/data/src/index.js index a5d1a51214654..06f074bec1541 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -15,7 +15,7 @@ export { default as RegistryProvider, RegistryConsumer } from './components/regi export { default as __experimentalAsyncModeProvider } from './components/async-mode-provider'; export { createRegistry } from './registry'; export { plugins }; -export { createRegistrySelector } from './factory'; +export { createRegistrySelector, createRegistryControl } from './factory'; /** * The combineReducers helper function turns an object whose values are different diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js index 2e5f2b3022a3f..bb1259d3c52af 100644 --- a/packages/data/src/namespace-store.js +++ b/packages/data/src/namespace-store.js @@ -105,24 +105,26 @@ function createReduxStore( reducer, key, registry ) { * @return {Object} Selectors mapped to the redux store provided. */ function mapSelectors( selectors, store, registry ) { - const createStateSelector = ( registeredSelector ) => function runSelector() { + const createStateSelector = ( registeredSelector ) => { const selector = registeredSelector.isRegistrySelector ? registeredSelector( registry ) : registeredSelector; - // This function is an optimized implementation of: - // - // selector( store.getState(), ...arguments ) - // - // Where the above would incur an `Array#concat` in its application, - // the logic here instead efficiently constructs an arguments array via - // direct assignment. - const argsLength = arguments.length; - const args = new Array( argsLength + 1 ); - args[ 0 ] = store.getState(); - for ( let i = 0; i < argsLength; i++ ) { - args[ i + 1 ] = arguments[ i ]; - } + return function runSelector() { + // This function is an optimized implementation of: + // + // selector( store.getState(), ...arguments ) + // + // Where the above would incur an `Array#concat` in its application, + // the logic here instead efficiently constructs an arguments array via + // direct assignment. + const argsLength = arguments.length; + const args = new Array( argsLength + 1 ); + args[ 0 ] = store.getState(); + for ( let i = 0; i < argsLength; i++ ) { + args[ i + 1 ] = arguments[ i ]; + } - return selector( ...args ); + return selector( ...args ); + }; }; return mapValues( selectors, createStateSelector ); diff --git a/packages/data/src/plugins/controls/index.js b/packages/data/src/plugins/controls/index.js index bc19c9cfad897..a023dd6916b10 100644 --- a/packages/data/src/plugins/controls/index.js +++ b/packages/data/src/plugins/controls/index.js @@ -2,6 +2,7 @@ * External dependencies */ import { applyMiddleware } from 'redux'; +import { mapValues } from 'lodash'; /** * WordPress dependencies @@ -14,7 +15,10 @@ export default function( registry ) { const store = registry.registerStore( reducerKey, options ); if ( options.controls ) { - const middleware = createMiddleware( options.controls ); + const normalizedControls = mapValues( options.controls, ( control ) => { + return control.isRegistryControl ? control( registry ) : control; + } ); + const middleware = createMiddleware( normalizedControls ); const enhancer = applyMiddleware( middleware ); const createStore = () => store; diff --git a/packages/data/src/plugins/controls/test/index.js b/packages/data/src/plugins/controls/test/index.js new file mode 100644 index 0000000000000..d60cb9053211d --- /dev/null +++ b/packages/data/src/plugins/controls/test/index.js @@ -0,0 +1,44 @@ +/** + * Internal dependencies + */ +import { createRegistry } from '../../../registry'; +import { createRegistryControl } from '../../../factory'; +import controlsPlugin from '../'; + +describe( 'controls', () => { + let registry; + + beforeEach( () => { + registry = createRegistry(); + registry.use( controlsPlugin ); + } ); + + describe( 'should call registry-aware controls', () => { + it( 'registers multiple selectors to the public API', () => { + const action1 = jest.fn( () => ( { type: 'NOTHING' } ) ); + const action2 = function * () { + yield { type: 'DISPATCH', store: 'store1', action: 'action1' }; + }; + registry.registerStore( 'store1', { + reducer: () => 'state1', + actions: { + action1, + }, + } ); + registry.registerStore( 'store2', { + reducer: () => 'state2', + actions: { + action2, + }, + controls: { + DISPATCH: createRegistryControl( ( reg ) => ( { store, action } ) => { + return reg.dispatch( store )[ action ](); + } ), + }, + } ); + + registry.dispatch( 'store2' ).action2(); + expect( action1 ).toBeCalled(); + } ); + } ); +} );