diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 91362f4e1d5c4c..79cce308253eed 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -4,6 +4,10 @@ - Restore functionality of action-generators returning a Promise. Clarify intent and behaviour for `wp.data.dispatch` behaviour. Dispatch actions now always return a promise ([#14830](https://github.com/WordPress/gutenberg/pull/14830) + +### Enhancements + +- Expose `hasResolver` property on returned selectors indicating whether the selector has a corresponding resolver. ## 4.3.0 (2019-03-06) diff --git a/packages/data/src/namespace-store/index.js b/packages/data/src/namespace-store/index.js index 73e6560baf8e9a..6c8d7d94094eba 100644 --- a/packages/data/src/namespace-store/index.js +++ b/packages/data/src/namespace-store/index.js @@ -155,9 +155,11 @@ function createReduxStore( key, options, registry ) { */ function mapSelectors( selectors, store, registry ) { const createStateSelector = ( registeredSelector ) => { - const selector = registeredSelector.isRegistrySelector ? registeredSelector( registry.select ) : registeredSelector; + const registrySelector = registeredSelector.isRegistrySelector ? + registeredSelector( registry.select ) : + registeredSelector; - return function runSelector() { + const selector = function runSelector() { // This function is an optimized implementation of: // // selector( store.getState(), ...arguments ) @@ -172,8 +174,10 @@ function mapSelectors( selectors, store, registry ) { args[ i + 1 ] = arguments[ i ]; } - return selector( ...args ); + return registrySelector( ...args ); }; + selector.hasResolver = false; + return selector; }; return mapValues( selectors, createStateSelector ); @@ -213,10 +217,11 @@ function mapResolvers( resolvers, selectors, store ) { const mapSelector = ( selector, selectorName ) => { const resolver = resolvers[ selectorName ]; if ( ! resolver ) { + selector.hasResolver = false; return selector; } - return ( ...args ) => { + const selectorResolver = ( ...args ) => { async function fulfillSelector() { const state = store.getState(); if ( typeof resolver.isFulfilled === 'function' && resolver.isFulfilled( state, ...args ) ) { @@ -236,6 +241,8 @@ function mapResolvers( resolvers, selectors, store ) { fulfillSelector( ...args ); return selector( ...args ); }; + selectorResolver.hasResolver = true; + return selectorResolver; }; return { diff --git a/packages/data/src/namespace-store/test/index.js b/packages/data/src/namespace-store/test/index.js index 34a1092d44d1b4..2a9f14ae821f64 100644 --- a/packages/data/src/namespace-store/test/index.js +++ b/packages/data/src/namespace-store/test/index.js @@ -80,6 +80,33 @@ describe( 'controls', () => { registry.select( 'store' ).getItems(); } ); + describe( 'selectors have expected value for the `hasResolver` property', () => { + it( 'when custom store has resolvers defined', () => { + registry.registerStore( 'store', { + reducer: jest.fn(), + selectors: { + getItems: ( state ) => state, + getItem: ( state ) => state, + }, + resolvers: { + * getItems() { + yield 'foo'; + }, + }, + } ); + expect( registry.select( 'store' ).getItems.hasResolver ).toBe( true ); + expect( registry.select( 'store' ).getItem.hasResolver ).toBe( false ); + } ); + it( 'when custom store does not have resolvers defined', () => { + registry.registerStore( 'store', { + reducer: jest.fn(), + selectors: { + getItems: ( state ) => state, + }, + } ); + expect( registry.select( 'store' ).getItems.hasResolver ).toBe( false ); + } ); + } ); describe( 'various action types have expected response and resolve as ' + 'expected with controls middleware', () => { const actions = {