@wordpress/core-data 4.0.4 → 4.0.8
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.
- package/README.md +2 -4
- package/build/actions.js +321 -247
- package/build/actions.js.map +1 -1
- package/build/batch/create-batch.js +17 -9
- package/build/batch/create-batch.js.map +1 -1
- package/build/entities.js +67 -14
- package/build/entities.js.map +1 -1
- package/build/entity-provider.js +18 -13
- package/build/entity-provider.js.map +1 -1
- package/build/fetch/__experimental-fetch-link-suggestions.js +3 -1
- package/build/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
- package/build/fetch/__experimental-fetch-url-data.js +3 -2
- package/build/fetch/__experimental-fetch-url-data.js.map +1 -1
- package/build/index.js +25 -5
- package/build/index.js.map +1 -1
- package/build/locks/actions.js +4 -3
- package/build/locks/actions.js.map +1 -1
- package/build/locks/reducer.js +4 -1
- package/build/locks/reducer.js.map +1 -1
- package/build/locks/selectors.js +4 -3
- package/build/locks/selectors.js.map +1 -1
- package/build/locks/utils.js +7 -5
- package/build/locks/utils.js.map +1 -1
- package/build/queried-data/actions.js +6 -3
- package/build/queried-data/actions.js.map +1 -1
- package/build/queried-data/get-query-parts.js +1 -1
- package/build/queried-data/reducer.js +17 -6
- package/build/queried-data/reducer.js.map +1 -1
- package/build/queried-data/selectors.js +2 -1
- package/build/queried-data/selectors.js.map +1 -1
- package/build/reducer.js +90 -43
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +216 -163
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +75 -22
- package/build/selectors.js.map +1 -1
- package/build/utils/forward-resolver.js +30 -0
- package/build/utils/forward-resolver.js.map +1 -0
- package/build/utils/index.js +9 -9
- package/build/utils/index.js.map +1 -1
- package/build/utils/on-sub-key.js +4 -2
- package/build/utils/on-sub-key.js.map +1 -1
- package/build-module/actions.js +306 -240
- package/build-module/actions.js.map +1 -1
- package/build-module/batch/create-batch.js +17 -9
- package/build-module/batch/create-batch.js.map +1 -1
- package/build-module/entities.js +66 -13
- package/build-module/entities.js.map +1 -1
- package/build-module/entity-provider.js +17 -12
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/fetch/__experimental-fetch-link-suggestions.js +3 -1
- package/build-module/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
- package/build-module/fetch/__experimental-fetch-url-data.js +3 -2
- package/build-module/fetch/__experimental-fetch-url-data.js.map +1 -1
- package/build-module/index.js +21 -3
- package/build-module/index.js.map +1 -1
- package/build-module/locks/actions.js +4 -3
- package/build-module/locks/actions.js.map +1 -1
- package/build-module/locks/reducer.js +4 -1
- package/build-module/locks/reducer.js.map +1 -1
- package/build-module/locks/selectors.js +4 -3
- package/build-module/locks/selectors.js.map +1 -1
- package/build-module/locks/utils.js +5 -3
- package/build-module/locks/utils.js.map +1 -1
- package/build-module/queried-data/actions.js +5 -2
- package/build-module/queried-data/actions.js.map +1 -1
- package/build-module/queried-data/reducer.js +15 -4
- package/build-module/queried-data/reducer.js.map +1 -1
- package/build-module/queried-data/selectors.js +2 -1
- package/build-module/queried-data/selectors.js.map +1 -1
- package/build-module/reducer.js +80 -34
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +211 -161
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +44 -3
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/forward-resolver.js +22 -0
- package/build-module/utils/forward-resolver.js.map +1 -0
- package/build-module/utils/index.js +1 -1
- package/build-module/utils/index.js.map +1 -1
- package/build-module/utils/on-sub-key.js +3 -1
- package/build-module/utils/on-sub-key.js.map +1 -1
- package/package.json +12 -12
- package/src/actions.js +51 -5
- package/src/entities.js +43 -6
- package/src/fetch/__experimental-fetch-url-data.js +1 -1
- package/src/reducer.js +14 -17
- package/src/resolvers.js +56 -19
- package/src/selectors.js +43 -4
- package/src/test/actions.js +89 -0
- package/src/test/selectors.js +56 -1
- package/src/utils/forward-resolver.js +14 -0
- package/src/utils/index.js +1 -1
- package/build/utils/if-not-resolved.js +0 -31
- package/build/utils/if-not-resolved.js.map +0 -1
- package/build-module/utils/if-not-resolved.js +0 -23
- package/build-module/utils/if-not-resolved.js.map +0 -1
- package/src/utils/if-not-resolved.js +0 -22
- 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
|
|
123
|
+
* Reducer managing the current global styles id.
|
|
124
124
|
*
|
|
125
|
-
* @param {
|
|
125
|
+
* @param {string} state Current state.
|
|
126
126
|
* @param {Object} action Dispatched action.
|
|
127
127
|
*
|
|
128
|
-
* @return {
|
|
128
|
+
* @return {string} Updated state.
|
|
129
129
|
*/
|
|
130
|
-
export function
|
|
130
|
+
export function currentGlobalStylesId( state = undefined, action ) {
|
|
131
131
|
switch ( action.type ) {
|
|
132
|
-
case '
|
|
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
|
|
140
|
+
* Reducer managing the theme base global styles.
|
|
144
141
|
*
|
|
145
|
-
* @param {
|
|
142
|
+
* @param {string} state Current state.
|
|
146
143
|
* @param {Object} action Dispatched action.
|
|
147
144
|
*
|
|
148
|
-
* @return {
|
|
145
|
+
* @return {string} Updated state.
|
|
149
146
|
*/
|
|
150
|
-
export function
|
|
147
|
+
export function themeBaseGlobalStyles( state = {}, action ) {
|
|
151
148
|
switch ( action.type ) {
|
|
152
|
-
case '
|
|
149
|
+
case 'RECEIVE_THEME_GLOBAL_STYLES':
|
|
153
150
|
return {
|
|
154
151
|
...state,
|
|
155
|
-
|
|
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 {
|
|
17
|
+
import { forwardResolver, getNormalizedCommaSeparable } from './utils';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Requests authors from the REST API.
|
|
@@ -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 =
|
|
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 =
|
|
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
|
|
229
|
-
|
|
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 = (
|
|
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(
|
|
330
|
-
|
|
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
|
|
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
|
|
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
|
+
}
|
package/src/test/actions.js
CHANGED
|
@@ -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();
|
package/src/test/selectors.js
CHANGED
|
@@ -418,7 +418,7 @@ describe( 'getEntityRecords', () => {
|
|
|
418
418
|
} );
|
|
419
419
|
|
|
420
420
|
describe( '__experimentalGetDirtyEntityRecords', () => {
|
|
421
|
-
it( '
|
|
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;
|
package/src/utils/index.js
CHANGED
|
@@ -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
|
|
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
|
-
} );
|