@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.
- package/README.md +2 -4
- package/build/actions.js +54 -5
- package/build/actions.js.map +1 -1
- package/build/entities.js +46 -5
- package/build/entities.js.map +1 -1
- package/build/fetch/__experimental-fetch-url-data.js +1 -1
- package/build/fetch/__experimental-fetch-url-data.js.map +1 -1
- package/build/reducer.js +16 -18
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +41 -16
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +48 -3
- package/build/selectors.js.map +1 -1
- package/build/utils/forward-resolver.js +23 -0
- package/build/utils/forward-resolver.js.map +1 -0
- package/build/utils/index.js +3 -3
- package/build/utils/index.js.map +1 -1
- package/build-module/actions.js +49 -5
- package/build-module/actions.js.map +1 -1
- package/build-module/entities.js +46 -5
- package/build-module/entities.js.map +1 -1
- package/build-module/fetch/__experimental-fetch-url-data.js +1 -1
- package/build-module/fetch/__experimental-fetch-url-data.js.map +1 -1
- package/build-module/reducer.js +14 -16
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +36 -14
- 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 +15 -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/package.json +6 -6
- package/src/actions.js +51 -5
- package/src/entities.js +32 -3
- package/src/fetch/__experimental-fetch-url-data.js +1 -1
- package/src/reducer.js +14 -17
- package/src/resolvers.js +51 -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/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:
|
|
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:
|
|
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 = '/
|
|
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
|
|
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.
|
|
@@ -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 =
|
|
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 =
|
|
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
|
|
229
|
-
|
|
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 = (
|
|
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(
|
|
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: '/__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();
|
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
|
-
} );
|