@wordpress/core-data 4.3.0 → 4.4.0

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 (65) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +27 -3
  3. package/build/batch/default-processor.js +1 -1
  4. package/build/batch/default-processor.js.map +1 -1
  5. package/build/entities.js.map +1 -1
  6. package/build/fetch/__experimental-fetch-link-suggestions.js +48 -11
  7. package/build/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
  8. package/build/hooks/use-entity-record.js +4 -4
  9. package/build/hooks/use-entity-record.js.map +1 -1
  10. package/build/hooks/use-entity-records.js +4 -4
  11. package/build/hooks/use-entity-records.js.map +1 -1
  12. package/build/hooks/use-query-select.js.map +1 -1
  13. package/build/reducer.js +61 -9
  14. package/build/reducer.js.map +1 -1
  15. package/build/resolvers.js +31 -1
  16. package/build/resolvers.js.map +1 -1
  17. package/build/selectors.js +30 -4
  18. package/build/selectors.js.map +1 -1
  19. package/build/types.js +6 -0
  20. package/build/types.js.map +1 -0
  21. package/build/utils/if-matching-action.js +4 -2
  22. package/build/utils/if-matching-action.js.map +1 -1
  23. package/build/utils/on-sub-key.js +3 -1
  24. package/build/utils/on-sub-key.js.map +1 -1
  25. package/build/utils/replace-action.js +4 -2
  26. package/build/utils/replace-action.js.map +1 -1
  27. package/build-module/batch/default-processor.js +1 -1
  28. package/build-module/batch/default-processor.js.map +1 -1
  29. package/build-module/entities.js.map +1 -1
  30. package/build-module/fetch/__experimental-fetch-link-suggestions.js +48 -11
  31. package/build-module/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
  32. package/build-module/hooks/use-entity-record.js +4 -4
  33. package/build-module/hooks/use-entity-record.js.map +1 -1
  34. package/build-module/hooks/use-entity-records.js +4 -4
  35. package/build-module/hooks/use-entity-records.js.map +1 -1
  36. package/build-module/hooks/use-query-select.js.map +1 -1
  37. package/build-module/reducer.js +58 -9
  38. package/build-module/reducer.js.map +1 -1
  39. package/build-module/resolvers.js +24 -0
  40. package/build-module/resolvers.js.map +1 -1
  41. package/build-module/selectors.js +26 -4
  42. package/build-module/selectors.js.map +1 -1
  43. package/build-module/types.js +2 -0
  44. package/build-module/types.js.map +1 -0
  45. package/build-module/utils/if-matching-action.js +4 -2
  46. package/build-module/utils/if-matching-action.js.map +1 -1
  47. package/build-module/utils/on-sub-key.js +3 -1
  48. package/build-module/utils/on-sub-key.js.map +1 -1
  49. package/build-module/utils/replace-action.js +4 -2
  50. package/build-module/utils/replace-action.js.map +1 -1
  51. package/package.json +11 -11
  52. package/src/batch/default-processor.js +1 -0
  53. package/src/{entities.js → entities.ts} +7 -4
  54. package/src/fetch/__experimental-fetch-link-suggestions.js +56 -20
  55. package/src/fetch/test/__experimental-fetch-link-suggestions.js +39 -1
  56. package/src/hooks/use-entity-record.ts +4 -4
  57. package/src/hooks/use-entity-records.ts +4 -4
  58. package/src/hooks/use-query-select.ts +12 -7
  59. package/src/reducer.js +50 -7
  60. package/src/resolvers.js +14 -0
  61. package/src/{selectors.js → selectors.ts} +26 -4
  62. package/src/types.ts +3 -0
  63. package/src/utils/if-matching-action.js +4 -2
  64. package/src/utils/on-sub-key.js +3 -1
  65. package/src/utils/replace-action.js +4 -2
@@ -70,6 +70,18 @@ jest.mock( '@wordpress/api-fetch', () =>
70
70
  subtype: 'page',
71
71
  },
72
72
  ] );
73
+ case '/wp/v2/media?search=&per_page=20':
74
+ return Promise.resolve( [
75
+ {
76
+ id: 54,
77
+ title: {
78
+ rendered: 'Some Test Media Title',
79
+ },
80
+ type: 'attachment',
81
+ source_url:
82
+ 'http://localhost:8888/wp-content/uploads/2022/03/test-pdf.pdf',
83
+ },
84
+ ] );
73
85
  default:
74
86
  return Promise.resolve( [
75
87
  {
@@ -154,7 +166,25 @@ describe( 'fetchLinkSuggestions', () => {
154
166
  { disablePostFormats: true }
155
167
  ).then( ( suggestions ) => expect( suggestions ).toEqual( [] ) );
156
168
  } );
157
- it( 'returns suggestions from post, term, and post-format', () => {
169
+
170
+ it( 'filters suggestions by attachment', () => {
171
+ return fetchLinkSuggestions( '', {
172
+ type: 'attachment',
173
+ } ).then( ( suggestions ) =>
174
+ expect( suggestions ).toEqual( [
175
+ {
176
+ id: 54,
177
+ title: 'Some Test Media Title',
178
+ url:
179
+ 'http://localhost:8888/wp-content/uploads/2022/03/test-pdf.pdf',
180
+ type: 'attachment',
181
+ kind: 'media',
182
+ },
183
+ ] )
184
+ );
185
+ } );
186
+
187
+ it( 'returns suggestions from post, term, post-format and media', () => {
158
188
  return fetchLinkSuggestions( '', {} ).then( ( suggestions ) =>
159
189
  expect( suggestions ).toEqual( [
160
190
  {
@@ -192,6 +222,14 @@ describe( 'fetchLinkSuggestions', () => {
192
222
  type: 'post-format',
193
223
  kind: 'taxonomy',
194
224
  },
225
+ {
226
+ id: 54,
227
+ title: 'Some Test Media Title',
228
+ url:
229
+ 'http://localhost:8888/wp-content/uploads/2022/03/test-pdf.pdf',
230
+ type: 'attachment',
231
+ kind: 'media',
232
+ },
195
233
  ] )
196
234
  );
197
235
  } );
@@ -30,10 +30,10 @@ interface Options {
30
30
  /**
31
31
  * Resolves the specified entity record.
32
32
  *
33
- * @param kind Kind of the requested entity.
34
- * @param name Name of the requested entity.
35
- * @param recordId Record ID of the requested entity.
36
- * @param options Hook options.
33
+ * @param kind Kind of the requested entity.
34
+ * @param name Name of the requested entity.
35
+ * @param recordId Record ID of the requested entity.
36
+ * @param options Hook options.
37
37
  * @param [options.enabled=true] Whether to run the query or short-circuit and return null. Defaults to true.
38
38
  * @example
39
39
  * ```js
@@ -40,10 +40,10 @@ interface Options {
40
40
  /**
41
41
  * Resolves the specified entity records.
42
42
  *
43
- * @param kind Kind of the requested entities.
44
- * @param name Name of the requested entities.
45
- * @param queryArgs HTTP query for the requested entities.
46
- * @param options Hook options.
43
+ * @param kind Kind of the requested entities.
44
+ * @param name Name of the requested entities.
45
+ * @param queryArgs HTTP query for the requested entities.
46
+ * @param options Hook options.
47
47
  * @example
48
48
  * ```js
49
49
  * import { useEntityRecord } from '@wordpress/core-data';
@@ -17,9 +17,9 @@ export const META_SELECTORS = [
17
17
  'getCachedResolvers',
18
18
  ];
19
19
 
20
- interface QuerySelectResponse {
20
+ interface QuerySelectResponse< Data > {
21
21
  /** the requested selector return value */
22
- data: Object;
22
+ data: Data;
23
23
 
24
24
  /** is the record still being resolved? Via the `getIsResolving` meta-selector */
25
25
  isResolving: boolean;
@@ -78,9 +78,14 @@ export default function __experimentalUseQuerySelect( mapQuerySelect, deps ) {
78
78
  }, deps );
79
79
  }
80
80
 
81
- type QuerySelector = ( ...args ) => QuerySelectResponse;
82
81
  interface EnrichedSelectors {
83
- [ key: string ]: QuerySelector;
82
+ < Selectors extends Record< string, ( ...args: any[] ) => any > >(
83
+ selectors: Selectors
84
+ ): {
85
+ [ Selector in keyof Selectors ]: (
86
+ ...args: Parameters< Selectors[ Selector ] >
87
+ ) => QuerySelectResponse< ReturnType< Selectors[ Selector ] > >;
88
+ };
84
89
  }
85
90
 
86
91
  /**
@@ -90,14 +95,14 @@ interface EnrichedSelectors {
90
95
  * @param {Object} selectors Selectors to enrich
91
96
  * @return {EnrichedSelectors} Enriched selectors
92
97
  */
93
- const enrichSelectors = memoize( ( selectors ) => {
98
+ const enrichSelectors = memoize( ( ( selectors ) => {
94
99
  const resolvers = {};
95
100
  for ( const selectorName in selectors ) {
96
101
  if ( META_SELECTORS.includes( selectorName ) ) {
97
102
  continue;
98
103
  }
99
104
  Object.defineProperty( resolvers, selectorName, {
100
- get: () => ( ...args ) => {
105
+ get: () => ( ...args: unknown[] ) => {
101
106
  const { getIsResolving, hasFinishedResolution } = selectors;
102
107
  const isResolving = !! getIsResolving( selectorName, args );
103
108
  const hasResolved =
@@ -128,4 +133,4 @@ const enrichSelectors = memoize( ( selectors ) => {
128
133
  } );
129
134
  }
130
135
  return resolvers;
131
- } );
136
+ } ) as EnrichedSelectors );
package/src/reducer.js CHANGED
@@ -16,6 +16,8 @@ import { ifMatchingAction, replaceAction } from './utils';
16
16
  import { reducer as queriedDataReducer } from './queried-data';
17
17
  import { rootEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities';
18
18
 
19
+ /** @typedef {import('./types').AnyFunction} AnyFunction */
20
+
19
21
  /**
20
22
  * Reducer managing terms state. Keyed by taxonomy slug, the value is either
21
23
  * undefined (if no request has been made for given taxonomy), null (if a
@@ -185,7 +187,7 @@ export function themeGlobalStyleVariations( state = {}, action ) {
185
187
  *
186
188
  * @param {Object} entityConfig Entity config.
187
189
  *
188
- * @return {Function} Reducer.
190
+ * @return {AnyFunction} Reducer.
189
191
  */
190
192
  function entity( entityConfig ) {
191
193
  return flowRight( [
@@ -398,16 +400,32 @@ export const entities = ( state = {}, action ) => {
398
400
  };
399
401
 
400
402
  /**
401
- * Reducer keeping track of entity edit undo history.
403
+ * @typedef {Object} UndoStateMeta
402
404
  *
403
- * @param {Object} state Current state.
404
- * @param {Object} action Dispatched action.
405
+ * @property {number} offset Where in the undo stack we are.
406
+ * @property {Object} [flattenedUndo] Flattened form of undo stack.
407
+ */
408
+
409
+ /** @typedef {Array<Object> & UndoStateMeta} UndoState */
410
+
411
+ /**
412
+ * @type {UndoState}
405
413
  *
406
- * @return {Object} Updated state.
414
+ * @todo Given how we use this we might want to make a custom class for it.
407
415
  */
408
- const UNDO_INITIAL_STATE = [];
409
- UNDO_INITIAL_STATE.offset = 0;
416
+ const UNDO_INITIAL_STATE = Object.assign( [], { offset: 0 } );
417
+
418
+ /** @type {Object} */
410
419
  let lastEditAction;
420
+
421
+ /**
422
+ * Reducer keeping track of entity edit undo history.
423
+ *
424
+ * @param {UndoState} state Current state.
425
+ * @param {Object} action Dispatched action.
426
+ *
427
+ * @return {UndoState} Updated state.
428
+ */
411
429
  export function undo( state = UNDO_INITIAL_STATE, action ) {
412
430
  switch ( action.type ) {
413
431
  case 'EDIT_ENTITY_RECORD':
@@ -439,8 +457,11 @@ export function undo( state = UNDO_INITIAL_STATE, action ) {
439
457
  }
440
458
  }
441
459
 
460
+ /** @type {UndoState} */
442
461
  let nextState;
462
+
443
463
  if ( isUndoOrRedo ) {
464
+ // @ts-ignore we might consider using Object.assign({}, state)
444
465
  nextState = [ ...state ];
445
466
  nextState.offset =
446
467
  state.offset + ( action.meta.isUndo ? -1 : 1 );
@@ -480,6 +501,7 @@ export function undo( state = UNDO_INITIAL_STATE, action ) {
480
501
  ( key ) => ! action.transientEdits[ key ]
481
502
  )
482
503
  ) {
504
+ // @ts-ignore we might consider using Object.assign({}, state)
483
505
  nextState = [ ...state ];
484
506
  nextState.flattenedUndo = {
485
507
  ...state.flattenedUndo,
@@ -491,6 +513,7 @@ export function undo( state = UNDO_INITIAL_STATE, action ) {
491
513
 
492
514
  // Clear potential redos, because this only supports linear history.
493
515
  nextState =
516
+ // @ts-ignore this needs additional cleanup, probably involving code-level changes
494
517
  nextState || state.slice( 0, state.offset || undefined );
495
518
  nextState.offset = nextState.offset || 0;
496
519
  nextState.pop();
@@ -592,6 +615,24 @@ export function autosaves( state = {}, action ) {
592
615
  return state;
593
616
  }
594
617
 
618
+ export function blockPatterns( state = [], action ) {
619
+ switch ( action.type ) {
620
+ case 'RECEIVE_BLOCK_PATTERNS':
621
+ return action.patterns;
622
+ }
623
+
624
+ return state;
625
+ }
626
+
627
+ export function blockPatternCategories( state = [], action ) {
628
+ switch ( action.type ) {
629
+ case 'RECEIVE_BLOCK_PATTERN_CATEGORIES':
630
+ return action.categories;
631
+ }
632
+
633
+ return state;
634
+ }
635
+
595
636
  export default combineReducers( {
596
637
  terms,
597
638
  users,
@@ -606,4 +647,6 @@ export default combineReducers( {
606
647
  embedPreviews,
607
648
  userPermissions,
608
649
  autosaves,
650
+ blockPatterns,
651
+ blockPatternCategories,
609
652
  } );
package/src/resolvers.js CHANGED
@@ -453,3 +453,17 @@ export const __experimentalGetCurrentThemeGlobalStylesVariations = () => async (
453
453
  variations
454
454
  );
455
455
  };
456
+
457
+ export const getBlockPatterns = () => async ( { dispatch } ) => {
458
+ const patterns = await apiFetch( {
459
+ path: '/__experimental/block-patterns/patterns',
460
+ } );
461
+ dispatch( { type: 'RECEIVE_BLOCK_PATTERNS', patterns } );
462
+ };
463
+
464
+ export const getBlockPatternCategories = () => async ( { dispatch } ) => {
465
+ const categories = await apiFetch( {
466
+ path: '/__experimental/block-patterns/categories',
467
+ } );
468
+ dispatch( { type: 'RECEIVE_BLOCK_PATTERN_CATEGORIES', categories } );
469
+ };
@@ -501,10 +501,10 @@ export const getEntityRecordNonTransientEdits = createSelector(
501
501
  * Returns true if the specified entity record has edits,
502
502
  * and false otherwise.
503
503
  *
504
- * @param {Object} state State tree.
505
- * @param {string} kind Entity kind.
506
- * @param {string} name Entity name.
507
- * @param {number} recordId Record ID.
504
+ * @param {Object} state State tree.
505
+ * @param {string} kind Entity kind.
506
+ * @param {string} name Entity name.
507
+ * @param {number|string} recordId Record ID.
508
508
  *
509
509
  * @return {boolean} Whether the entity record has edits or not.
510
510
  */
@@ -962,3 +962,25 @@ export function __experimentalGetCurrentThemeGlobalStylesVariations( state ) {
962
962
  }
963
963
  return state.themeGlobalStyleVariations[ currentTheme.stylesheet ];
964
964
  }
965
+
966
+ /**
967
+ * Retrieve the list of registered block patterns.
968
+ *
969
+ * @param {Object} state Data state.
970
+ *
971
+ * @return {Array} Block pattern list.
972
+ */
973
+ export function getBlockPatterns( state ) {
974
+ return state.blockPatterns;
975
+ }
976
+
977
+ /**
978
+ * Retrieve the list of registered block pattern categories.
979
+ *
980
+ * @param {Object} state Data state.
981
+ *
982
+ * @return {Array} Block pattern category list.
983
+ */
984
+ export function getBlockPatternCategories( state ) {
985
+ return state.blockPatternCategories;
986
+ }
package/src/types.ts ADDED
@@ -0,0 +1,3 @@
1
+ export interface AnyFunction {
2
+ ( ...args: any[] ): any;
3
+ }
@@ -1,11 +1,13 @@
1
+ /** @typedef {import('../types').AnyFunction} AnyFunction */
2
+
1
3
  /**
2
4
  * A higher-order reducer creator which invokes the original reducer only if
3
5
  * the dispatching action matches the given predicate, **OR** if state is
4
6
  * initializing (undefined).
5
7
  *
6
- * @param {Function} isMatch Function predicate for allowing reducer call.
8
+ * @param {AnyFunction} isMatch Function predicate for allowing reducer call.
7
9
  *
8
- * @return {Function} Higher-order reducer.
10
+ * @return {AnyFunction} Higher-order reducer.
9
11
  */
10
12
  const ifMatchingAction = ( isMatch ) => ( reducer ) => ( state, action ) => {
11
13
  if ( state === undefined || isMatch( action ) ) {
@@ -1,10 +1,12 @@
1
+ /** @typedef {import('../types').AnyFunction} AnyFunction */
2
+
1
3
  /**
2
4
  * Higher-order reducer creator which creates a combined reducer object, keyed
3
5
  * by a property on the action object.
4
6
  *
5
7
  * @param {string} actionProperty Action property by which to key object.
6
8
  *
7
- * @return {Function} Higher-order reducer.
9
+ * @return {AnyFunction} Higher-order reducer.
8
10
  */
9
11
  export const onSubKey = ( actionProperty ) => ( reducer ) => (
10
12
  state = {},
@@ -1,10 +1,12 @@
1
+ /** @typedef {import('../types').AnyFunction} AnyFunction */
2
+
1
3
  /**
2
4
  * Higher-order reducer creator which substitutes the action object before
3
5
  * passing to the original reducer.
4
6
  *
5
- * @param {Function} replacer Function mapping original action to replacement.
7
+ * @param {AnyFunction} replacer Function mapping original action to replacement.
6
8
  *
7
- * @return {Function} Higher-order reducer.
9
+ * @return {AnyFunction} Higher-order reducer.
8
10
  */
9
11
  const replaceAction = ( replacer ) => ( reducer ) => ( state, action ) => {
10
12
  return reducer( state, replacer( action ) );