@wordpress/core-data 4.0.3 → 4.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +2 -4
  2. package/build/actions.js +321 -247
  3. package/build/actions.js.map +1 -1
  4. package/build/batch/create-batch.js +17 -9
  5. package/build/batch/create-batch.js.map +1 -1
  6. package/build/entities.js +58 -14
  7. package/build/entities.js.map +1 -1
  8. package/build/entity-provider.js +18 -13
  9. package/build/entity-provider.js.map +1 -1
  10. package/build/fetch/__experimental-fetch-link-suggestions.js +3 -1
  11. package/build/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
  12. package/build/fetch/__experimental-fetch-url-data.js +3 -2
  13. package/build/fetch/__experimental-fetch-url-data.js.map +1 -1
  14. package/build/index.js +25 -5
  15. package/build/index.js.map +1 -1
  16. package/build/locks/actions.js +4 -3
  17. package/build/locks/actions.js.map +1 -1
  18. package/build/locks/reducer.js +4 -1
  19. package/build/locks/reducer.js.map +1 -1
  20. package/build/locks/selectors.js +4 -3
  21. package/build/locks/selectors.js.map +1 -1
  22. package/build/locks/utils.js +7 -5
  23. package/build/locks/utils.js.map +1 -1
  24. package/build/queried-data/actions.js +6 -3
  25. package/build/queried-data/actions.js.map +1 -1
  26. package/build/queried-data/get-query-parts.js +1 -1
  27. package/build/queried-data/reducer.js +17 -6
  28. package/build/queried-data/reducer.js.map +1 -1
  29. package/build/queried-data/selectors.js +2 -1
  30. package/build/queried-data/selectors.js.map +1 -1
  31. package/build/reducer.js +90 -43
  32. package/build/reducer.js.map +1 -1
  33. package/build/resolvers.js +216 -163
  34. package/build/resolvers.js.map +1 -1
  35. package/build/selectors.js +75 -22
  36. package/build/selectors.js.map +1 -1
  37. package/build/utils/forward-resolver.js +30 -0
  38. package/build/utils/forward-resolver.js.map +1 -0
  39. package/build/utils/index.js +9 -9
  40. package/build/utils/index.js.map +1 -1
  41. package/build/utils/on-sub-key.js +4 -2
  42. package/build/utils/on-sub-key.js.map +1 -1
  43. package/build-module/actions.js +306 -240
  44. package/build-module/actions.js.map +1 -1
  45. package/build-module/batch/create-batch.js +17 -9
  46. package/build-module/batch/create-batch.js.map +1 -1
  47. package/build-module/entities.js +57 -13
  48. package/build-module/entities.js.map +1 -1
  49. package/build-module/entity-provider.js +17 -12
  50. package/build-module/entity-provider.js.map +1 -1
  51. package/build-module/fetch/__experimental-fetch-link-suggestions.js +3 -1
  52. package/build-module/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
  53. package/build-module/fetch/__experimental-fetch-url-data.js +3 -2
  54. package/build-module/fetch/__experimental-fetch-url-data.js.map +1 -1
  55. package/build-module/index.js +21 -3
  56. package/build-module/index.js.map +1 -1
  57. package/build-module/locks/actions.js +4 -3
  58. package/build-module/locks/actions.js.map +1 -1
  59. package/build-module/locks/reducer.js +4 -1
  60. package/build-module/locks/reducer.js.map +1 -1
  61. package/build-module/locks/selectors.js +4 -3
  62. package/build-module/locks/selectors.js.map +1 -1
  63. package/build-module/locks/utils.js +5 -3
  64. package/build-module/locks/utils.js.map +1 -1
  65. package/build-module/queried-data/actions.js +5 -2
  66. package/build-module/queried-data/actions.js.map +1 -1
  67. package/build-module/queried-data/reducer.js +15 -4
  68. package/build-module/queried-data/reducer.js.map +1 -1
  69. package/build-module/queried-data/selectors.js +2 -1
  70. package/build-module/queried-data/selectors.js.map +1 -1
  71. package/build-module/reducer.js +80 -34
  72. package/build-module/reducer.js.map +1 -1
  73. package/build-module/resolvers.js +211 -161
  74. package/build-module/resolvers.js.map +1 -1
  75. package/build-module/selectors.js +44 -3
  76. package/build-module/selectors.js.map +1 -1
  77. package/build-module/utils/forward-resolver.js +22 -0
  78. package/build-module/utils/forward-resolver.js.map +1 -0
  79. package/build-module/utils/index.js +1 -1
  80. package/build-module/utils/index.js.map +1 -1
  81. package/build-module/utils/on-sub-key.js +3 -1
  82. package/build-module/utils/on-sub-key.js.map +1 -1
  83. package/package.json +12 -12
  84. package/src/actions.js +51 -5
  85. package/src/entities.js +35 -6
  86. package/src/fetch/__experimental-fetch-url-data.js +1 -1
  87. package/src/reducer.js +14 -17
  88. package/src/resolvers.js +57 -20
  89. package/src/selectors.js +43 -4
  90. package/src/test/actions.js +89 -0
  91. package/src/test/selectors.js +56 -1
  92. package/src/utils/forward-resolver.js +14 -0
  93. package/src/utils/index.js +1 -1
  94. package/build/utils/if-not-resolved.js +0 -31
  95. package/build/utils/if-not-resolved.js.map +0 -1
  96. package/build-module/utils/if-not-resolved.js +0 -23
  97. package/build-module/utils/if-not-resolved.js.map +0 -1
  98. package/src/utils/if-not-resolved.js +0 -22
  99. package/src/utils/test/if-not-resolved.js +0 -76
package/src/reducer.js CHANGED
@@ -120,39 +120,36 @@ export function currentTheme( state = undefined, action ) {
120
120
  }
121
121
 
122
122
  /**
123
- * Reducer managing installed themes.
123
+ * Reducer managing the current global styles id.
124
124
  *
125
- * @param {Object} state Current state.
125
+ * @param {string} state Current state.
126
126
  * @param {Object} action Dispatched action.
127
127
  *
128
- * @return {Object} Updated state.
128
+ * @return {string} Updated state.
129
129
  */
130
- export function themes( state = {}, action ) {
130
+ export function currentGlobalStylesId( state = undefined, action ) {
131
131
  switch ( action.type ) {
132
- case 'RECEIVE_CURRENT_THEME':
133
- return {
134
- ...state,
135
- [ action.currentTheme.stylesheet ]: action.currentTheme,
136
- };
132
+ case 'RECEIVE_CURRENT_GLOBAL_STYLES_ID':
133
+ return action.id;
137
134
  }
138
135
 
139
136
  return state;
140
137
  }
141
138
 
142
139
  /**
143
- * Reducer managing theme supports data.
140
+ * Reducer managing the theme base global styles.
144
141
  *
145
- * @param {Object} state Current state.
142
+ * @param {string} state Current state.
146
143
  * @param {Object} action Dispatched action.
147
144
  *
148
- * @return {Object} Updated state.
145
+ * @return {string} Updated state.
149
146
  */
150
- export function themeSupports( state = {}, action ) {
147
+ export function themeBaseGlobalStyles( state = {}, action ) {
151
148
  switch ( action.type ) {
152
- case 'RECEIVE_THEME_SUPPORTS':
149
+ case 'RECEIVE_THEME_GLOBAL_STYLES':
153
150
  return {
154
151
  ...state,
155
- ...action.themeSupports,
152
+ [ action.stylesheet ]: action.globalStyles,
156
153
  };
157
154
  }
158
155
 
@@ -570,10 +567,10 @@ export default combineReducers( {
570
567
  terms,
571
568
  users,
572
569
  currentTheme,
570
+ currentGlobalStylesId,
573
571
  currentUser,
572
+ themeBaseGlobalStyles,
574
573
  taxonomies,
575
- themes,
576
- themeSupports,
577
574
  entities,
578
575
  undo,
579
576
  embedPreviews,
package/src/resolvers.js CHANGED
@@ -14,7 +14,7 @@ import apiFetch from '@wordpress/api-fetch';
14
14
  */
15
15
  import { STORE_NAME } from './name';
16
16
  import { getKindEntities, DEFAULT_ENTITY_KEY } from './entities';
17
- import { ifNotResolved, getNormalizedCommaSeparable } from './utils';
17
+ import { forwardResolver, getNormalizedCommaSeparable } from './utils';
18
18
 
19
19
  /**
20
20
  * Requests authors from the REST API.
@@ -85,7 +85,7 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
85
85
  // for how the request is made to the REST API.
86
86
 
87
87
  // eslint-disable-next-line @wordpress/no-unused-vars-before-return
88
- const path = addQueryArgs( entity.baseURL + '/' + key, {
88
+ const path = addQueryArgs( entity.baseURL + ( key ? '/' + key : '' ), {
89
89
  ...entity.baseURLParams,
90
90
  ...query,
91
91
  } );
@@ -107,6 +107,7 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
107
107
  } catch ( error ) {
108
108
  // We need a way to handle and access REST API errors in state
109
109
  // Until then, catching the error ensures the resolver is marked as resolved.
110
+ // See similar implementation in `getEntityRecords()`.
110
111
  } finally {
111
112
  dispatch.__unstableReleaseStoreLock( lock );
112
113
  }
@@ -115,18 +116,12 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
115
116
  /**
116
117
  * Requests an entity's record from the REST API.
117
118
  */
118
- export const getRawEntityRecord = ifNotResolved(
119
- getEntityRecord,
120
- 'getEntityRecord'
121
- );
119
+ export const getRawEntityRecord = forwardResolver( 'getEntityRecord' );
122
120
 
123
121
  /**
124
122
  * Requests an entity's record from the REST API.
125
123
  */
126
- export const getEditedEntityRecord = ifNotResolved(
127
- getRawEntityRecord,
128
- 'getRawEntityRecord'
129
- );
124
+ export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
130
125
 
131
126
  /**
132
127
  * Requests the entity's records from the REST API.
@@ -207,6 +202,10 @@ export const getEntityRecords = ( kind, name, query = {} ) => async ( {
207
202
  args: resolutionsArgs,
208
203
  } );
209
204
  }
205
+ } catch ( error ) {
206
+ // We need a way to handle and access REST API errors in state
207
+ // Until then, catching the error ensures the resolver is marked as resolved.
208
+ // See similar implementation in `getEntityRecord()`.
210
209
  } finally {
211
210
  dispatch.__unstableReleaseStoreLock( lock );
212
211
  }
@@ -224,22 +223,20 @@ getEntityRecords.shouldInvalidate = ( action, kind, name ) => {
224
223
  /**
225
224
  * Requests the current theme.
226
225
  */
227
- export const getCurrentTheme = () => async ( { dispatch } ) => {
228
- const activeThemes = await apiFetch( {
229
- path: '/wp/v2/themes?status=active',
230
- } );
226
+ export const getCurrentTheme = () => async ( { dispatch, resolveSelect } ) => {
227
+ const activeThemes = await resolveSelect.getEntityRecords(
228
+ 'root',
229
+ 'theme',
230
+ { status: 'active' }
231
+ );
232
+
231
233
  dispatch.receiveCurrentTheme( activeThemes[ 0 ] );
232
234
  };
233
235
 
234
236
  /**
235
237
  * Requests theme supports data from the index.
236
238
  */
237
- export const getThemeSupports = () => async ( { dispatch } ) => {
238
- const activeThemes = await apiFetch( {
239
- path: '/wp/v2/themes?status=active',
240
- } );
241
- dispatch.receiveThemeSupports( activeThemes[ 0 ].theme_supports );
242
- };
239
+ export const getThemeSupports = forwardResolver( 'getCurrentTheme' );
243
240
 
244
241
  /**
245
242
  * Requests a preview from the from the Embed API.
@@ -418,3 +415,43 @@ __experimentalGetTemplateForLink.shouldInvalidate = ( action ) => {
418
415
  action.name === 'wp_template'
419
416
  );
420
417
  };
418
+
419
+ export const __experimentalGetCurrentGlobalStylesId = () => async ( {
420
+ dispatch,
421
+ resolveSelect,
422
+ } ) => {
423
+ const activeThemes = await resolveSelect.getEntityRecords(
424
+ 'root',
425
+ 'theme',
426
+ { status: 'active' }
427
+ );
428
+ const globalStylesURL = get( activeThemes, [
429
+ 0,
430
+ '_links',
431
+ 'wp:user-global-styles',
432
+ 0,
433
+ 'href',
434
+ ] );
435
+ if ( globalStylesURL ) {
436
+ const globalStylesObject = await apiFetch( {
437
+ url: globalStylesURL,
438
+ } );
439
+ dispatch.__experimentalReceiveCurrentGlobalStylesId(
440
+ globalStylesObject.id
441
+ );
442
+ }
443
+ };
444
+
445
+ export const __experimentalGetCurrentThemeBaseGlobalStyles = () => async ( {
446
+ resolveSelect,
447
+ dispatch,
448
+ } ) => {
449
+ const currentTheme = await resolveSelect.getCurrentTheme();
450
+ const themeGlobalStyles = await apiFetch( {
451
+ path: `/wp/v2/global-styles/themes/${ currentTheme.stylesheet }`,
452
+ } );
453
+ await dispatch.__experimentalReceiveThemeBaseGlobalStyles(
454
+ currentTheme.stylesheet,
455
+ themeGlobalStyles
456
+ );
457
+ };
package/src/selectors.js CHANGED
@@ -19,6 +19,15 @@ import { getQueriedItems } from './queried-data';
19
19
  import { DEFAULT_ENTITY_KEY } from './entities';
20
20
  import { getNormalizedCommaSeparable, isRawAttribute } from './utils';
21
21
 
22
+ /**
23
+ * Shared reference to an empty object for cases where it is important to avoid
24
+ * returning a new object reference on every invocation, as in a connected or
25
+ * other pure component which performs `shouldComponentUpdate` check on props.
26
+ * This should be used as a last resort, since the normalized data should be
27
+ * maintained by the reducer result in state.
28
+ */
29
+ const EMPTY_OBJECT = {};
30
+
22
31
  /**
23
32
  * Shared reference to an empty array for cases where it is important to avoid
24
33
  * returning a new array reference on every invocation, as in a connected or
@@ -326,8 +335,12 @@ export const __experimentalGetDirtyEntityRecords = createSelector(
326
335
  Object.keys( data[ kind ] ).forEach( ( name ) => {
327
336
  const primaryKeys = Object.keys(
328
337
  data[ kind ][ name ].edits
329
- ).filter( ( primaryKey ) =>
330
- hasEditsForEntityRecord( state, kind, name, primaryKey )
338
+ ).filter(
339
+ ( primaryKey ) =>
340
+ // The entity record must exist (not be deleted),
341
+ // and it must have edits.
342
+ getEntityRecord( state, kind, name, primaryKey ) &&
343
+ hasEditsForEntityRecord( state, kind, name, primaryKey )
331
344
  );
332
345
 
333
346
  if ( primaryKeys.length ) {
@@ -684,7 +697,18 @@ export function hasRedo( state ) {
684
697
  * @return {Object} The current theme.
685
698
  */
686
699
  export function getCurrentTheme( state ) {
687
- return state.themes[ state.currentTheme ];
700
+ return getEntityRecord( state, 'root', 'theme', state.currentTheme );
701
+ }
702
+
703
+ /**
704
+ * Return the ID of the current global styles object.
705
+ *
706
+ * @param {Object} state Data state.
707
+ *
708
+ * @return {string} The current global styles ID.
709
+ */
710
+ export function __experimentalGetCurrentGlobalStylesId( state ) {
711
+ return state.currentGlobalStylesId;
688
712
  }
689
713
 
690
714
  /**
@@ -695,7 +719,7 @@ export function getCurrentTheme( state ) {
695
719
  * @return {*} Index data.
696
720
  */
697
721
  export function getThemeSupports( state ) {
698
- return state.themeSupports;
722
+ return getCurrentTheme( state )?.theme_supports ?? EMPTY_OBJECT;
699
723
  }
700
724
 
701
725
  /**
@@ -882,3 +906,18 @@ export function __experimentalGetTemplateForLink( state, link ) {
882
906
  }
883
907
  return template;
884
908
  }
909
+
910
+ /**
911
+ * Retrieve the current theme's base global styles
912
+ *
913
+ * @param {Object} state Editor state.
914
+ *
915
+ * @return {Object?} The Global Styles object.
916
+ */
917
+ export function __experimentalGetCurrentThemeBaseGlobalStyles( state ) {
918
+ const currentTheme = getCurrentTheme( state );
919
+ if ( ! currentTheme ) {
920
+ return null;
921
+ }
922
+ return state.themeBaseGlobalStyles[ currentTheme.stylesheet ];
923
+ }
@@ -11,6 +11,7 @@ jest.mock( '@wordpress/api-fetch' );
11
11
  import {
12
12
  editEntityRecord,
13
13
  saveEntityRecord,
14
+ saveEditedEntityRecord,
14
15
  deleteEntityRecord,
15
16
  receiveUserPermission,
16
17
  receiveAutosaves,
@@ -107,6 +108,94 @@ describe( 'deleteEntityRecord', () => {
107
108
  } );
108
109
  } );
109
110
 
111
+ describe( 'saveEditedEntityRecord', () => {
112
+ beforeEach( async () => {
113
+ apiFetch.mockReset();
114
+ jest.useFakeTimers();
115
+ } );
116
+
117
+ it( 'Uses "id" as a key when no entity key is provided', async () => {
118
+ const area = { id: 1, menu: 0 };
119
+ const entities = [
120
+ {
121
+ kind: 'root',
122
+ name: 'navigationArea',
123
+ baseURL: '/wp/v2/block-navigation-areas',
124
+ },
125
+ ];
126
+ const select = {
127
+ getEntityRecordNonTransientEdits: () => [],
128
+ hasEditsForEntityRecord: () => true,
129
+ };
130
+
131
+ const dispatch = Object.assign( jest.fn(), {
132
+ saveEntityRecord: jest.fn(),
133
+ } );
134
+ // Provide entities
135
+ dispatch.mockReturnValueOnce( entities );
136
+
137
+ // Provide response
138
+ const updatedRecord = { ...area, menu: 10 };
139
+ apiFetch.mockImplementation( () => {
140
+ return updatedRecord;
141
+ } );
142
+
143
+ await saveEditedEntityRecord(
144
+ 'root',
145
+ 'navigationArea',
146
+ 1
147
+ )( { dispatch, select } );
148
+
149
+ expect( dispatch.saveEntityRecord ).toHaveBeenCalledWith(
150
+ 'root',
151
+ 'navigationArea',
152
+ { id: 1 },
153
+ undefined
154
+ );
155
+ } );
156
+
157
+ it( 'Uses the entity key when provided', async () => {
158
+ const area = { area: 'primary', menu: 0 };
159
+ const entities = [
160
+ {
161
+ kind: 'root',
162
+ name: 'navigationArea',
163
+ baseURL: '/wp/v2/block-navigation-areas',
164
+ key: 'area',
165
+ },
166
+ ];
167
+ const select = {
168
+ getEntityRecordNonTransientEdits: () => [],
169
+ hasEditsForEntityRecord: () => true,
170
+ };
171
+
172
+ const dispatch = Object.assign( jest.fn(), {
173
+ saveEntityRecord: jest.fn(),
174
+ } );
175
+ // Provide entities
176
+ dispatch.mockReturnValueOnce( entities );
177
+
178
+ // Provide response
179
+ const updatedRecord = { ...area, menu: 10 };
180
+ apiFetch.mockImplementation( () => {
181
+ return updatedRecord;
182
+ } );
183
+
184
+ await saveEditedEntityRecord(
185
+ 'root',
186
+ 'navigationArea',
187
+ 'primary'
188
+ )( { dispatch, select } );
189
+
190
+ expect( dispatch.saveEntityRecord ).toHaveBeenCalledWith(
191
+ 'root',
192
+ 'navigationArea',
193
+ { area: 'primary' },
194
+ undefined
195
+ );
196
+ } );
197
+ } );
198
+
110
199
  describe( 'saveEntityRecord', () => {
111
200
  beforeEach( async () => {
112
201
  apiFetch.mockReset();
@@ -418,7 +418,7 @@ describe( 'getEntityRecords', () => {
418
418
  } );
419
419
 
420
420
  describe( '__experimentalGetDirtyEntityRecords', () => {
421
- it( 'should return a map of objects with each raw edited entity record and its corresponding edits', () => {
421
+ it( 'returns a map of objects with each raw edited entity record and its corresponding edits', () => {
422
422
  const state = deepFreeze( {
423
423
  entities: {
424
424
  config: [
@@ -466,6 +466,61 @@ describe( '__experimentalGetDirtyEntityRecords', () => {
466
466
  { kind: 'someKind', name: 'someName', key: 'someKey', title: '' },
467
467
  ] );
468
468
  } );
469
+
470
+ it( 'excludes entity records that no longer exist', () => {
471
+ const state = deepFreeze( {
472
+ entities: {
473
+ config: [
474
+ {
475
+ kind: 'someKind',
476
+ name: 'someName',
477
+ transientEdits: { someTransientEditProperty: true },
478
+ },
479
+ ],
480
+ data: {
481
+ someKind: {
482
+ someName: {
483
+ queriedData: {
484
+ items: {
485
+ default: {
486
+ someKey: {
487
+ someProperty: 'somePersistedValue',
488
+ someRawProperty: {
489
+ raw: 'somePersistedRawValue',
490
+ },
491
+ id: 'someKey',
492
+ },
493
+ },
494
+ },
495
+ itemIsComplete: {
496
+ default: {
497
+ someKey: true,
498
+ },
499
+ },
500
+ },
501
+ edits: {
502
+ someKey: {
503
+ someProperty: 'someEditedValue',
504
+ someRawProperty: 'someEditedRawValue',
505
+ someTransientEditProperty:
506
+ 'someEditedTransientEditValue',
507
+ },
508
+ deletedKey: {
509
+ someProperty: 'someEditedValue',
510
+ someRawProperty: 'someEditedRawValue',
511
+ someTransientEditProperty:
512
+ 'someEditedTransientEditValue',
513
+ },
514
+ },
515
+ },
516
+ },
517
+ },
518
+ },
519
+ } );
520
+ expect( __experimentalGetDirtyEntityRecords( state ) ).toEqual( [
521
+ { kind: 'someKind', name: 'someName', key: 'someKey', title: '' },
522
+ ] );
523
+ } );
469
524
  } );
470
525
 
471
526
  describe( '__experimentalGetEntitiesBeingSaved', () => {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Higher-order function which forward the resolution to another resolver with the same arguments.
3
+ *
4
+ * @param {string} resolverName forwarded resolver.
5
+ *
6
+ * @return {Function} Enhanced resolver.
7
+ */
8
+ const forwardResolver = ( resolverName ) => ( ...args ) => async ( {
9
+ resolveSelect,
10
+ } ) => {
11
+ await resolveSelect[ resolverName ]( ...args );
12
+ };
13
+
14
+ export default forwardResolver;
@@ -1,7 +1,7 @@
1
1
  export { default as conservativeMapItem } from './conservative-map-item';
2
2
  export { default as getNormalizedCommaSeparable } from './get-normalized-comma-separable';
3
3
  export { default as ifMatchingAction } from './if-matching-action';
4
- export { default as ifNotResolved } from './if-not-resolved';
4
+ export { default as forwardResolver } from './forward-resolver';
5
5
  export { default as onSubKey } from './on-sub-key';
6
6
  export { default as replaceAction } from './replace-action';
7
7
  export { default as withWeakMapCache } from './with-weak-map-cache';
@@ -1,31 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- /**
9
- * Higher-order function which invokes the given resolver only if it has not
10
- * already been resolved with the arguments passed to the enhanced function.
11
- *
12
- * This only considers resolution state, and notably does not support resolver
13
- * custom `isFulfilled` behavior.
14
- *
15
- * @param {Function} resolver Original resolver.
16
- * @param {string} selectorName Selector name associated with resolver.
17
- *
18
- * @return {Function} Enhanced resolver.
19
- */
20
- const ifNotResolved = (resolver, selectorName) => (...args) => async ({
21
- select,
22
- dispatch
23
- }) => {
24
- if (!select.hasStartedResolution(selectorName, args)) {
25
- await dispatch(resolver(...args));
26
- }
27
- };
28
-
29
- var _default = ifNotResolved;
30
- exports.default = _default;
31
- //# sourceMappingURL=if-not-resolved.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["@wordpress/core-data/src/utils/if-not-resolved.js"],"names":["ifNotResolved","resolver","selectorName","args","select","dispatch","hasStartedResolution"],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMA,aAAa,GAAG,CAAEC,QAAF,EAAYC,YAAZ,KAA8B,CAAE,GAAGC,IAAL,KAAe,OAAQ;AAC1EC,EAAAA,MAD0E;AAE1EC,EAAAA;AAF0E,CAAR,KAG5D;AACN,MAAK,CAAED,MAAM,CAACE,oBAAP,CAA6BJ,YAA7B,EAA2CC,IAA3C,CAAP,EAA2D;AAC1D,UAAME,QAAQ,CAAEJ,QAAQ,CAAE,GAAGE,IAAL,CAAV,CAAd;AACA;AACD,CAPD;;eASeH,a","sourcesContent":["/**\n * Higher-order function which invokes the given resolver only if it has not\n * already been resolved with the arguments passed to the enhanced function.\n *\n * This only considers resolution state, and notably does not support resolver\n * custom `isFulfilled` behavior.\n *\n * @param {Function} resolver Original resolver.\n * @param {string} selectorName Selector name associated with resolver.\n *\n * @return {Function} Enhanced resolver.\n */\nconst ifNotResolved = ( resolver, selectorName ) => ( ...args ) => async ( {\n\tselect,\n\tdispatch,\n} ) => {\n\tif ( ! select.hasStartedResolution( selectorName, args ) ) {\n\t\tawait dispatch( resolver( ...args ) );\n\t}\n};\n\nexport default ifNotResolved;\n"]}
@@ -1,23 +0,0 @@
1
- /**
2
- * Higher-order function which invokes the given resolver only if it has not
3
- * already been resolved with the arguments passed to the enhanced function.
4
- *
5
- * This only considers resolution state, and notably does not support resolver
6
- * custom `isFulfilled` behavior.
7
- *
8
- * @param {Function} resolver Original resolver.
9
- * @param {string} selectorName Selector name associated with resolver.
10
- *
11
- * @return {Function} Enhanced resolver.
12
- */
13
- const ifNotResolved = (resolver, selectorName) => (...args) => async ({
14
- select,
15
- dispatch
16
- }) => {
17
- if (!select.hasStartedResolution(selectorName, args)) {
18
- await dispatch(resolver(...args));
19
- }
20
- };
21
-
22
- export default ifNotResolved;
23
- //# sourceMappingURL=if-not-resolved.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["@wordpress/core-data/src/utils/if-not-resolved.js"],"names":["ifNotResolved","resolver","selectorName","args","select","dispatch","hasStartedResolution"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMA,aAAa,GAAG,CAAEC,QAAF,EAAYC,YAAZ,KAA8B,CAAE,GAAGC,IAAL,KAAe,OAAQ;AAC1EC,EAAAA,MAD0E;AAE1EC,EAAAA;AAF0E,CAAR,KAG5D;AACN,MAAK,CAAED,MAAM,CAACE,oBAAP,CAA6BJ,YAA7B,EAA2CC,IAA3C,CAAP,EAA2D;AAC1D,UAAME,QAAQ,CAAEJ,QAAQ,CAAE,GAAGE,IAAL,CAAV,CAAd;AACA;AACD,CAPD;;AASA,eAAeH,aAAf","sourcesContent":["/**\n * Higher-order function which invokes the given resolver only if it has not\n * already been resolved with the arguments passed to the enhanced function.\n *\n * This only considers resolution state, and notably does not support resolver\n * custom `isFulfilled` behavior.\n *\n * @param {Function} resolver Original resolver.\n * @param {string} selectorName Selector name associated with resolver.\n *\n * @return {Function} Enhanced resolver.\n */\nconst ifNotResolved = ( resolver, selectorName ) => ( ...args ) => async ( {\n\tselect,\n\tdispatch,\n} ) => {\n\tif ( ! select.hasStartedResolution( selectorName, args ) ) {\n\t\tawait dispatch( resolver( ...args ) );\n\t}\n};\n\nexport default ifNotResolved;\n"]}
@@ -1,22 +0,0 @@
1
- /**
2
- * Higher-order function which invokes the given resolver only if it has not
3
- * already been resolved with the arguments passed to the enhanced function.
4
- *
5
- * This only considers resolution state, and notably does not support resolver
6
- * custom `isFulfilled` behavior.
7
- *
8
- * @param {Function} resolver Original resolver.
9
- * @param {string} selectorName Selector name associated with resolver.
10
- *
11
- * @return {Function} Enhanced resolver.
12
- */
13
- const ifNotResolved = ( resolver, selectorName ) => ( ...args ) => async ( {
14
- select,
15
- dispatch,
16
- } ) => {
17
- if ( ! select.hasStartedResolution( selectorName, args ) ) {
18
- await dispatch( resolver( ...args ) );
19
- }
20
- };
21
-
22
- export default ifNotResolved;
@@ -1,76 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { controls } from '@wordpress/data';
5
-
6
- /**
7
- * Internal dependencies
8
- */
9
- import ifNotResolved from '../if-not-resolved';
10
-
11
- jest.mock( '@wordpress/data', () => ( {
12
- controls: {
13
- select: jest.fn(),
14
- },
15
- } ) );
16
-
17
- describe( 'ifNotResolved', () => {
18
- beforeEach( () => {
19
- controls.select.mockReset();
20
- } );
21
-
22
- it( 'returns a new function', () => {
23
- const originalResolver = () => {};
24
-
25
- const resolver = ifNotResolved( originalResolver, 'originalResolver' );
26
-
27
- expect( resolver ).toBeInstanceOf( Function );
28
- } );
29
-
30
- it( 'triggers original resolver if not already resolved', async () => {
31
- const select = { hasStartedResolution: () => false };
32
- const dispatch = () => {};
33
-
34
- const originalResolver = jest
35
- .fn()
36
- .mockImplementation( async function () {} );
37
-
38
- const resolver = ifNotResolved( originalResolver, 'originalResolver' );
39
- await resolver()( { select, dispatch } );
40
-
41
- expect( originalResolver ).toHaveBeenCalledTimes( 1 );
42
- } );
43
-
44
- it( 'does not trigger original resolver if already resolved', async () => {
45
- const select = { hasStartedResolution: () => true };
46
- const dispatch = () => {};
47
-
48
- const originalResolver = jest
49
- .fn()
50
- .mockImplementation( async function () {} );
51
-
52
- const resolver = ifNotResolved( originalResolver, 'originalResolver' );
53
- await resolver()( { select, dispatch } );
54
-
55
- expect( originalResolver ).toHaveBeenCalledTimes( 0 );
56
- } );
57
-
58
- it( 'returns a promise when the resolver was not already resolved', async () => {
59
- const select = { hasStartedResolution: () => false };
60
- let thunkRetval;
61
- const dispatch = jest.fn( ( thunk ) => {
62
- thunkRetval = thunk();
63
- return thunkRetval;
64
- } );
65
-
66
- const originalResolver = jest.fn( () => () =>
67
- Promise.resolve( 'success!' )
68
- );
69
-
70
- const resolver = ifNotResolved( originalResolver, 'originalResolver' );
71
- const result = resolver()( { select, dispatch } );
72
-
73
- await expect( result ).resolves.toBe( undefined );
74
- await expect( thunkRetval ).resolves.toBe( 'success!' );
75
- } );
76
- } );