From 3136c9eca4044ef38cce89bfa126398b9f639233 Mon Sep 17 00:00:00 2001 From: Darren Ethier Date: Sat, 4 May 2019 22:03:35 -0400 Subject: [PATCH 1/2] expose `hasResolver` property on returned selectors --- packages/data/CHANGELOG.md | 4 ++++ packages/data/src/namespace-store/index.js | 5 ++++- packages/data/src/namespace-store/test/index.js | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 91362f4e1d5c4..79cce308253ee 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 73e6560baf8e9..82ee9023e093f 100644 --- a/packages/data/src/namespace-store/index.js +++ b/packages/data/src/namespace-store/index.js @@ -213,10 +213,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 +237,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 34a1092d44d1b..d9a806ea0f756 100644 --- a/packages/data/src/namespace-store/test/index.js +++ b/packages/data/src/namespace-store/test/index.js @@ -80,6 +80,22 @@ describe( 'controls', () => { registry.select( 'store' ).getItems(); } ); + it( 'selectors have expected value for the `hasResolver` property', () => { + 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 ); + } ); describe( 'various action types have expected response and resolve as ' + 'expected with controls middleware', () => { const actions = { From c0f655340185e39b708d4b93cdb087843b83f780 Mon Sep 17 00:00:00 2001 From: Darren Ethier Date: Thu, 16 May 2019 07:10:26 -0400 Subject: [PATCH 2/2] account for custom stores that may not have resolvers defined in the store config --- packages/data/src/namespace-store/index.js | 10 +++-- .../data/src/namespace-store/test/index.js | 37 ++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/data/src/namespace-store/index.js b/packages/data/src/namespace-store/index.js index 82ee9023e093f..6c8d7d94094eb 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 ); diff --git a/packages/data/src/namespace-store/test/index.js b/packages/data/src/namespace-store/test/index.js index d9a806ea0f756..2a9f14ae821f6 100644 --- a/packages/data/src/namespace-store/test/index.js +++ b/packages/data/src/namespace-store/test/index.js @@ -80,21 +80,32 @@ describe( 'controls', () => { registry.select( 'store' ).getItems(); } ); - it( 'selectors have expected value for the `hasResolver` property', () => { - registry.registerStore( 'store', { - reducer: jest.fn(), - selectors: { - getItems: ( state ) => state, - getItem: ( state ) => state, - }, - resolvers: { - * getItems() { - yield 'foo'; + 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 ); } ); - expect( registry.select( 'store' ).getItems.hasResolver ).toBe( true ); - expect( registry.select( 'store' ).getItem.hasResolver ).toBe( false ); } ); describe( 'various action types have expected response and resolve as ' + 'expected with controls middleware', () => {