Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add registry-aware controls #13722

Merged
merged 1 commit into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/core-data/src/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
15 changes: 14 additions & 1 deletion packages/data/src/factory.js
Original file line number Diff line number Diff line change
@@ -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.
*
Expand All @@ -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;
}
2 changes: 1 addition & 1 deletion packages/data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 17 additions & 15 deletions packages/data/src/namespace-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
6 changes: 5 additions & 1 deletion packages/data/src/plugins/controls/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import { applyMiddleware } from 'redux';
import { mapValues } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -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;

Expand Down
44 changes: 44 additions & 0 deletions packages/data/src/plugins/controls/test/index.js
Original file line number Diff line number Diff line change
@@ -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();
} );
} );
} );