@wordpress/core-data 4.0.4 → 4.0.5

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 (50) hide show
  1. package/README.md +2 -4
  2. package/build/actions.js +54 -5
  3. package/build/actions.js.map +1 -1
  4. package/build/entities.js +46 -5
  5. package/build/entities.js.map +1 -1
  6. package/build/fetch/__experimental-fetch-url-data.js +1 -1
  7. package/build/fetch/__experimental-fetch-url-data.js.map +1 -1
  8. package/build/reducer.js +16 -18
  9. package/build/reducer.js.map +1 -1
  10. package/build/resolvers.js +41 -16
  11. package/build/resolvers.js.map +1 -1
  12. package/build/selectors.js +48 -3
  13. package/build/selectors.js.map +1 -1
  14. package/build/utils/forward-resolver.js +23 -0
  15. package/build/utils/forward-resolver.js.map +1 -0
  16. package/build/utils/index.js +3 -3
  17. package/build/utils/index.js.map +1 -1
  18. package/build-module/actions.js +49 -5
  19. package/build-module/actions.js.map +1 -1
  20. package/build-module/entities.js +46 -5
  21. package/build-module/entities.js.map +1 -1
  22. package/build-module/fetch/__experimental-fetch-url-data.js +1 -1
  23. package/build-module/fetch/__experimental-fetch-url-data.js.map +1 -1
  24. package/build-module/reducer.js +14 -16
  25. package/build-module/reducer.js.map +1 -1
  26. package/build-module/resolvers.js +36 -14
  27. package/build-module/resolvers.js.map +1 -1
  28. package/build-module/selectors.js +44 -3
  29. package/build-module/selectors.js.map +1 -1
  30. package/build-module/utils/forward-resolver.js +15 -0
  31. package/build-module/utils/forward-resolver.js.map +1 -0
  32. package/build-module/utils/index.js +1 -1
  33. package/build-module/utils/index.js.map +1 -1
  34. package/package.json +6 -6
  35. package/src/actions.js +51 -5
  36. package/src/entities.js +32 -3
  37. package/src/fetch/__experimental-fetch-url-data.js +1 -1
  38. package/src/reducer.js +14 -17
  39. package/src/resolvers.js +51 -19
  40. package/src/selectors.js +43 -4
  41. package/src/test/actions.js +89 -0
  42. package/src/test/selectors.js +56 -1
  43. package/src/utils/forward-resolver.js +14 -0
  44. package/src/utils/index.js +1 -1
  45. package/build/utils/if-not-resolved.js +0 -31
  46. package/build/utils/if-not-resolved.js.map +0 -1
  47. package/build-module/utils/if-not-resolved.js +0 -23
  48. package/build-module/utils/if-not-resolved.js.map +0 -1
  49. package/src/utils/if-not-resolved.js +0 -22
  50. package/src/utils/test/if-not-resolved.js +0 -76
package/src/entities.js CHANGED
@@ -23,7 +23,7 @@ export const defaultEntities = [
23
23
  label: __( 'Base' ),
24
24
  name: '__unstableBase',
25
25
  kind: 'root',
26
- baseURL: '',
26
+ baseURL: '/',
27
27
  },
28
28
  {
29
29
  label: __( 'Site' ),
@@ -127,6 +127,33 @@ export const defaultEntities = [
127
127
  label: __( 'Menu Location' ),
128
128
  key: 'name',
129
129
  },
130
+ {
131
+ name: 'navigationArea',
132
+ kind: 'root',
133
+ baseURL: '/__experimental/block-navigation-areas',
134
+ baseURLParams: { context: 'edit' },
135
+ plural: 'navigationAreas',
136
+ label: __( 'Navigation Area' ),
137
+ key: 'name',
138
+ getTitle: ( record ) => record?.description,
139
+ },
140
+ {
141
+ label: __( 'Global Styles' ),
142
+ name: 'globalStyles',
143
+ kind: 'root',
144
+ baseURL: '/wp/v2/global-styles',
145
+ baseURLParams: { context: 'edit' },
146
+ plural: 'globalStylesVariations', // should be different than name
147
+ getTitle: ( record ) => record?.title?.rendered || record?.title,
148
+ },
149
+ {
150
+ label: __( 'Themes' ),
151
+ name: 'theme',
152
+ kind: 'root',
153
+ baseURL: '/wp/v2/themes',
154
+ baseURLParams: { context: 'edit' },
155
+ key: 'stylesheet',
156
+ },
130
157
  ];
131
158
 
132
159
  export const kinds = [
@@ -175,9 +202,10 @@ async function loadPostTypeEntities() {
175
202
  const isTemplate = [ 'wp_template', 'wp_template_part' ].includes(
176
203
  name
177
204
  );
205
+ const namespace = postType?.rest_namespace ?? 'wp/v2';
178
206
  return {
179
207
  kind: 'postType',
180
- baseURL: '/wp/v2/' + postType.rest_base,
208
+ baseURL: `/${ namespace }/${ postType.rest_base }`,
181
209
  baseURLParams: { context: 'edit' },
182
210
  name,
183
211
  label: postType.labels.singular_name,
@@ -207,9 +235,10 @@ async function loadTaxonomyEntities() {
207
235
  path: '/wp/v2/taxonomies?context=edit',
208
236
  } );
209
237
  return map( taxonomies, ( taxonomy, name ) => {
238
+ const namespace = taxonomy?.rest_namespace ?? 'wp/v2';
210
239
  return {
211
240
  kind: 'taxonomy',
212
- baseURL: '/wp/v2/' + taxonomy.rest_base,
241
+ baseURL: `/${ namespace }/${ taxonomy.rest_base }`,
213
242
  baseURLParams: { context: 'edit' },
214
243
  name,
215
244
  label: taxonomy.labels.singular_name,
@@ -45,7 +45,7 @@ const CACHE = new Map();
45
45
  * @return {Promise< WPRemoteUrlData[] >} Remote URL data.
46
46
  */
47
47
  const fetchUrlData = async ( url, options = {} ) => {
48
- const endpoint = '/__experimental/url-details';
48
+ const endpoint = '/wp-block-editor/v1/url-details';
49
49
 
50
50
  const args = {
51
51
  url: prependHTTP( url ),
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.
@@ -115,18 +115,12 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
115
115
  /**
116
116
  * Requests an entity's record from the REST API.
117
117
  */
118
- export const getRawEntityRecord = ifNotResolved(
119
- getEntityRecord,
120
- 'getEntityRecord'
121
- );
118
+ export const getRawEntityRecord = forwardResolver( 'getEntityRecord' );
122
119
 
123
120
  /**
124
121
  * Requests an entity's record from the REST API.
125
122
  */
126
- export const getEditedEntityRecord = ifNotResolved(
127
- getRawEntityRecord,
128
- 'getRawEntityRecord'
129
- );
123
+ export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
130
124
 
131
125
  /**
132
126
  * Requests the entity's records from the REST API.
@@ -224,22 +218,20 @@ getEntityRecords.shouldInvalidate = ( action, kind, name ) => {
224
218
  /**
225
219
  * Requests the current theme.
226
220
  */
227
- export const getCurrentTheme = () => async ( { dispatch } ) => {
228
- const activeThemes = await apiFetch( {
229
- path: '/wp/v2/themes?status=active',
230
- } );
221
+ export const getCurrentTheme = () => async ( { dispatch, resolveSelect } ) => {
222
+ const activeThemes = await resolveSelect.getEntityRecords(
223
+ 'root',
224
+ 'theme',
225
+ { status: 'active' }
226
+ );
227
+
231
228
  dispatch.receiveCurrentTheme( activeThemes[ 0 ] );
232
229
  };
233
230
 
234
231
  /**
235
232
  * Requests theme supports data from the index.
236
233
  */
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
- };
234
+ export const getThemeSupports = forwardResolver( 'getCurrentTheme' );
243
235
 
244
236
  /**
245
237
  * Requests a preview from the from the Embed API.
@@ -418,3 +410,43 @@ __experimentalGetTemplateForLink.shouldInvalidate = ( action ) => {
418
410
  action.name === 'wp_template'
419
411
  );
420
412
  };
413
+
414
+ export const __experimentalGetCurrentGlobalStylesId = () => async ( {
415
+ dispatch,
416
+ resolveSelect,
417
+ } ) => {
418
+ const activeThemes = await resolveSelect.getEntityRecords(
419
+ 'root',
420
+ 'theme',
421
+ { status: 'active' }
422
+ );
423
+ const globalStylesURL = get( activeThemes, [
424
+ 0,
425
+ '_links',
426
+ 'wp:user-global-styles',
427
+ 0,
428
+ 'href',
429
+ ] );
430
+ if ( globalStylesURL ) {
431
+ const globalStylesObject = await apiFetch( {
432
+ url: globalStylesURL,
433
+ } );
434
+ dispatch.__experimentalReceiveCurrentGlobalStylesId(
435
+ globalStylesObject.id
436
+ );
437
+ }
438
+ };
439
+
440
+ export const __experimentalGetCurrentThemeBaseGlobalStyles = () => async ( {
441
+ resolveSelect,
442
+ dispatch,
443
+ } ) => {
444
+ const currentTheme = await resolveSelect.getCurrentTheme();
445
+ const themeGlobalStyles = await apiFetch( {
446
+ path: `/wp/v2/global-styles/themes/${ currentTheme.stylesheet }`,
447
+ } );
448
+ await dispatch.__experimentalReceiveThemeBaseGlobalStyles(
449
+ currentTheme.stylesheet,
450
+ themeGlobalStyles
451
+ );
452
+ };
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: '/__experimental/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: '/__experimental/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
- } );