@wordpress/core-data 4.0.2 → 4.0.6
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 +3 -5
- package/build/actions.js +79 -34
- package/build/actions.js.map +1 -1
- package/build/entities.js +51 -9
- 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/index.js +0 -7
- package/build/index.js.map +1 -1
- package/build/queried-data/get-query-parts.js +2 -0
- package/build/queried-data/get-query-parts.js.map +1 -1
- package/build/reducer.js +16 -18
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +48 -31
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +70 -10
- 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 +74 -33
- package/build-module/actions.js.map +1 -1
- package/build-module/entities.js +51 -9
- 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/index.js +0 -5
- package/build-module/index.js.map +1 -1
- package/build-module/queried-data/get-query-parts.js +2 -0
- package/build-module/queried-data/get-query-parts.js.map +1 -1
- package/build-module/reducer.js +14 -16
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +51 -37
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +64 -8
- 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 +10 -11
- package/src/actions.js +83 -52
- package/src/entities.js +36 -6
- package/src/fetch/__experimental-fetch-url-data.js +1 -1
- package/src/index.js +0 -3
- package/src/queried-data/get-query-parts.js +2 -0
- package/src/reducer.js +14 -17
- package/src/resolvers.js +67 -42
- package/src/selectors.js +143 -35
- package/src/test/actions.js +116 -37
- package/src/test/selectors.js +56 -1
- package/src/utils/forward-resolver.js +14 -0
- package/src/utils/index.js +1 -1
- package/build/controls.js +0 -44
- package/build/controls.js.map +0 -1
- package/build/utils/if-not-resolved.js +0 -31
- package/build/utils/if-not-resolved.js.map +0 -1
- package/build-module/controls.js +0 -31
- package/build-module/controls.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/controls.js +0 -31
- package/src/utils/if-not-resolved.js +0 -22
- package/src/utils/test/if-not-resolved.js +0 -76
package/src/resolvers.js
CHANGED
|
@@ -7,18 +7,14 @@ import { find, includes, get, hasIn, compact, uniq } from 'lodash';
|
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
9
|
import { addQueryArgs } from '@wordpress/url';
|
|
10
|
-
import
|
|
10
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Internal dependencies
|
|
14
14
|
*/
|
|
15
15
|
import { STORE_NAME } from './name';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Internal dependencies
|
|
19
|
-
*/
|
|
20
16
|
import { getKindEntities, DEFAULT_ENTITY_KEY } from './entities';
|
|
21
|
-
import {
|
|
17
|
+
import { forwardResolver, getNormalizedCommaSeparable } from './utils';
|
|
22
18
|
|
|
23
19
|
/**
|
|
24
20
|
* Requests authors from the REST API.
|
|
@@ -31,7 +27,7 @@ export const getAuthors = ( query ) => async ( { dispatch } ) => {
|
|
|
31
27
|
'/wp/v2/users/?who=authors&per_page=100',
|
|
32
28
|
query
|
|
33
29
|
);
|
|
34
|
-
const users = await
|
|
30
|
+
const users = await apiFetch( { path } );
|
|
35
31
|
dispatch.receiveUserQuery( path, users );
|
|
36
32
|
};
|
|
37
33
|
|
|
@@ -39,7 +35,7 @@ export const getAuthors = ( query ) => async ( { dispatch } ) => {
|
|
|
39
35
|
* Requests the current user from the REST API.
|
|
40
36
|
*/
|
|
41
37
|
export const getCurrentUser = () => async ( { dispatch } ) => {
|
|
42
|
-
const currentUser = await
|
|
38
|
+
const currentUser = await apiFetch( { path: '/wp/v2/users/me' } );
|
|
43
39
|
dispatch.receiveCurrentUser( currentUser );
|
|
44
40
|
};
|
|
45
41
|
|
|
@@ -58,7 +54,7 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
|
|
|
58
54
|
} ) => {
|
|
59
55
|
const entities = await dispatch( getKindEntities( kind ) );
|
|
60
56
|
const entity = find( entities, { kind, name } );
|
|
61
|
-
if ( ! entity ) {
|
|
57
|
+
if ( ! entity || entity?.__experimentalNoFetch ) {
|
|
62
58
|
return;
|
|
63
59
|
}
|
|
64
60
|
|
|
@@ -89,7 +85,7 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
|
|
|
89
85
|
// for how the request is made to the REST API.
|
|
90
86
|
|
|
91
87
|
// eslint-disable-next-line @wordpress/no-unused-vars-before-return
|
|
92
|
-
const path = addQueryArgs( entity.baseURL + '/' + key, {
|
|
88
|
+
const path = addQueryArgs( entity.baseURL + ( key ? '/' + key : '' ), {
|
|
93
89
|
...entity.baseURLParams,
|
|
94
90
|
...query,
|
|
95
91
|
} );
|
|
@@ -106,11 +102,12 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
|
|
|
106
102
|
}
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
const record = await
|
|
105
|
+
const record = await apiFetch( { path } );
|
|
110
106
|
dispatch.receiveEntityRecords( kind, name, record, query );
|
|
111
107
|
} catch ( error ) {
|
|
112
108
|
// We need a way to handle and access REST API errors in state
|
|
113
109
|
// Until then, catching the error ensures the resolver is marked as resolved.
|
|
110
|
+
// See similar implementation in `getEntityRecords()`.
|
|
114
111
|
} finally {
|
|
115
112
|
dispatch.__unstableReleaseStoreLock( lock );
|
|
116
113
|
}
|
|
@@ -119,26 +116,12 @@ export const getEntityRecord = ( kind, name, key = '', query ) => async ( {
|
|
|
119
116
|
/**
|
|
120
117
|
* Requests an entity's record from the REST API.
|
|
121
118
|
*/
|
|
122
|
-
export const getRawEntityRecord =
|
|
123
|
-
getEntityRecord,
|
|
124
|
-
'getEntityRecord'
|
|
125
|
-
);
|
|
119
|
+
export const getRawEntityRecord = forwardResolver( 'getEntityRecord' );
|
|
126
120
|
|
|
127
121
|
/**
|
|
128
122
|
* Requests an entity's record from the REST API.
|
|
129
123
|
*/
|
|
130
|
-
export const getEditedEntityRecord =
|
|
131
|
-
getRawEntityRecord,
|
|
132
|
-
'getRawEntityRecord'
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Requests the entity's records from the REST API.
|
|
137
|
-
*
|
|
138
|
-
* @param {string} kind Entity kind.
|
|
139
|
-
* @param {string} name Entity name.
|
|
140
|
-
* @param {Object?} query Query Object.
|
|
141
|
-
*/
|
|
124
|
+
export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
|
|
142
125
|
|
|
143
126
|
/**
|
|
144
127
|
* Requests the entity's records from the REST API.
|
|
@@ -152,7 +135,7 @@ export const getEntityRecords = ( kind, name, query = {} ) => async ( {
|
|
|
152
135
|
} ) => {
|
|
153
136
|
const entities = await dispatch( getKindEntities( kind ) );
|
|
154
137
|
const entity = find( entities, { kind, name } );
|
|
155
|
-
if ( ! entity ) {
|
|
138
|
+
if ( ! entity || entity?.__experimentalNoFetch ) {
|
|
156
139
|
return;
|
|
157
140
|
}
|
|
158
141
|
|
|
@@ -181,7 +164,7 @@ export const getEntityRecords = ( kind, name, query = {} ) => async ( {
|
|
|
181
164
|
...query,
|
|
182
165
|
} );
|
|
183
166
|
|
|
184
|
-
let records = Object.values( await
|
|
167
|
+
let records = Object.values( await apiFetch( { path } ) );
|
|
185
168
|
// If we request fields but the result doesn't contain the fields,
|
|
186
169
|
// explicitely set these fields as "undefined"
|
|
187
170
|
// that way we consider the query "fullfilled".
|
|
@@ -219,6 +202,10 @@ export const getEntityRecords = ( kind, name, query = {} ) => async ( {
|
|
|
219
202
|
args: resolutionsArgs,
|
|
220
203
|
} );
|
|
221
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()`.
|
|
222
209
|
} finally {
|
|
223
210
|
dispatch.__unstableReleaseStoreLock( lock );
|
|
224
211
|
}
|
|
@@ -236,22 +223,20 @@ getEntityRecords.shouldInvalidate = ( action, kind, name ) => {
|
|
|
236
223
|
/**
|
|
237
224
|
* Requests the current theme.
|
|
238
225
|
*/
|
|
239
|
-
export const getCurrentTheme = () => async ( { dispatch } ) => {
|
|
240
|
-
const activeThemes = await
|
|
241
|
-
|
|
242
|
-
|
|
226
|
+
export const getCurrentTheme = () => async ( { dispatch, resolveSelect } ) => {
|
|
227
|
+
const activeThemes = await resolveSelect.getEntityRecords(
|
|
228
|
+
'root',
|
|
229
|
+
'theme',
|
|
230
|
+
{ status: 'active' }
|
|
231
|
+
);
|
|
232
|
+
|
|
243
233
|
dispatch.receiveCurrentTheme( activeThemes[ 0 ] );
|
|
244
234
|
};
|
|
245
235
|
|
|
246
236
|
/**
|
|
247
237
|
* Requests theme supports data from the index.
|
|
248
238
|
*/
|
|
249
|
-
export const getThemeSupports = (
|
|
250
|
-
const activeThemes = await triggerFetch( {
|
|
251
|
-
path: '/wp/v2/themes?status=active',
|
|
252
|
-
} );
|
|
253
|
-
dispatch.receiveThemeSupports( activeThemes[ 0 ].theme_supports );
|
|
254
|
-
};
|
|
239
|
+
export const getThemeSupports = forwardResolver( 'getCurrentTheme' );
|
|
255
240
|
|
|
256
241
|
/**
|
|
257
242
|
* Requests a preview from the from the Embed API.
|
|
@@ -260,7 +245,7 @@ export const getThemeSupports = () => async ( { dispatch } ) => {
|
|
|
260
245
|
*/
|
|
261
246
|
export const getEmbedPreview = ( url ) => async ( { dispatch } ) => {
|
|
262
247
|
try {
|
|
263
|
-
const embedProxyResponse = await
|
|
248
|
+
const embedProxyResponse = await apiFetch( {
|
|
264
249
|
path: addQueryArgs( '/oembed/1.0/proxy', { url } ),
|
|
265
250
|
} );
|
|
266
251
|
dispatch.receiveEmbedPreview( url, embedProxyResponse );
|
|
@@ -296,7 +281,7 @@ export const canUser = ( action, resource, id ) => async ( { dispatch } ) => {
|
|
|
296
281
|
|
|
297
282
|
let response;
|
|
298
283
|
try {
|
|
299
|
-
response = await
|
|
284
|
+
response = await apiFetch( {
|
|
300
285
|
path,
|
|
301
286
|
// Ideally this would always be an OPTIONS request, but unfortunately there's
|
|
302
287
|
// a bug in the REST API which causes the Allow header to not be sent on
|
|
@@ -359,7 +344,7 @@ export const getAutosaves = ( postType, postId ) => async ( {
|
|
|
359
344
|
resolveSelect,
|
|
360
345
|
} ) => {
|
|
361
346
|
const { rest_base: restBase } = await resolveSelect.getPostType( postType );
|
|
362
|
-
const autosaves = await
|
|
347
|
+
const autosaves = await apiFetch( {
|
|
363
348
|
path: `/wp/v2/${ restBase }/${ postId }/autosaves?context=edit`,
|
|
364
349
|
} );
|
|
365
350
|
|
|
@@ -430,3 +415,43 @@ __experimentalGetTemplateForLink.shouldInvalidate = ( action ) => {
|
|
|
430
415
|
action.name === 'wp_template'
|
|
431
416
|
);
|
|
432
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
|
|
@@ -134,40 +143,63 @@ export function getEntity( state, kind, name ) {
|
|
|
134
143
|
*
|
|
135
144
|
* @return {Object?} Record.
|
|
136
145
|
*/
|
|
137
|
-
export
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
const context = query?.context ?? 'default';
|
|
147
|
-
|
|
148
|
-
if ( query === undefined ) {
|
|
149
|
-
// If expecting a complete item, validate that completeness.
|
|
150
|
-
if ( ! queriedState.itemIsComplete[ context ]?.[ key ] ) {
|
|
146
|
+
export const getEntityRecord = createSelector(
|
|
147
|
+
( state, kind, name, key, query ) => {
|
|
148
|
+
const queriedState = get( state.entities.data, [
|
|
149
|
+
kind,
|
|
150
|
+
name,
|
|
151
|
+
'queriedData',
|
|
152
|
+
] );
|
|
153
|
+
if ( ! queriedState ) {
|
|
151
154
|
return undefined;
|
|
152
155
|
}
|
|
156
|
+
const context = query?.context ?? 'default';
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
if ( query === undefined ) {
|
|
159
|
+
// If expecting a complete item, validate that completeness.
|
|
160
|
+
if ( ! queriedState.itemIsComplete[ context ]?.[ key ] ) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
156
163
|
|
|
157
|
-
|
|
158
|
-
if ( item && query._fields ) {
|
|
159
|
-
const filteredItem = {};
|
|
160
|
-
const fields = getNormalizedCommaSeparable( query._fields );
|
|
161
|
-
for ( let f = 0; f < fields.length; f++ ) {
|
|
162
|
-
const field = fields[ f ].split( '.' );
|
|
163
|
-
const value = get( item, field );
|
|
164
|
-
set( filteredItem, field, value );
|
|
164
|
+
return queriedState.items[ context ][ key ];
|
|
165
165
|
}
|
|
166
|
-
return filteredItem;
|
|
167
|
-
}
|
|
168
166
|
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
const item = queriedState.items[ context ]?.[ key ];
|
|
168
|
+
if ( item && query._fields ) {
|
|
169
|
+
const filteredItem = {};
|
|
170
|
+
const fields = getNormalizedCommaSeparable( query._fields );
|
|
171
|
+
for ( let f = 0; f < fields.length; f++ ) {
|
|
172
|
+
const field = fields[ f ].split( '.' );
|
|
173
|
+
const value = get( item, field );
|
|
174
|
+
set( filteredItem, field, value );
|
|
175
|
+
}
|
|
176
|
+
return filteredItem;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return item;
|
|
180
|
+
},
|
|
181
|
+
( state, kind, name, recordId, query ) => {
|
|
182
|
+
const context = query?.context ?? 'default';
|
|
183
|
+
return [
|
|
184
|
+
get( state.entities.data, [
|
|
185
|
+
kind,
|
|
186
|
+
name,
|
|
187
|
+
'queriedData',
|
|
188
|
+
'items',
|
|
189
|
+
context,
|
|
190
|
+
recordId,
|
|
191
|
+
] ),
|
|
192
|
+
get( state.entities.data, [
|
|
193
|
+
kind,
|
|
194
|
+
name,
|
|
195
|
+
'queriedData',
|
|
196
|
+
'itemIsComplete',
|
|
197
|
+
context,
|
|
198
|
+
recordId,
|
|
199
|
+
] ),
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
);
|
|
171
203
|
|
|
172
204
|
/**
|
|
173
205
|
* Returns the Entity's record object by key. Doesn't trigger a resolver nor requests the entity from the API if the entity record isn't available in the local state.
|
|
@@ -221,7 +253,28 @@ export const getRawEntityRecord = createSelector(
|
|
|
221
253
|
}, {} )
|
|
222
254
|
);
|
|
223
255
|
},
|
|
224
|
-
( state ) =>
|
|
256
|
+
( state, kind, name, recordId, query ) => {
|
|
257
|
+
const context = query?.context ?? 'default';
|
|
258
|
+
return [
|
|
259
|
+
state.entities.config,
|
|
260
|
+
get( state.entities.data, [
|
|
261
|
+
kind,
|
|
262
|
+
name,
|
|
263
|
+
'queriedData',
|
|
264
|
+
'items',
|
|
265
|
+
context,
|
|
266
|
+
recordId,
|
|
267
|
+
] ),
|
|
268
|
+
get( state.entities.data, [
|
|
269
|
+
kind,
|
|
270
|
+
name,
|
|
271
|
+
'queriedData',
|
|
272
|
+
'itemIsComplete',
|
|
273
|
+
context,
|
|
274
|
+
recordId,
|
|
275
|
+
] ),
|
|
276
|
+
];
|
|
277
|
+
}
|
|
225
278
|
);
|
|
226
279
|
|
|
227
280
|
/**
|
|
@@ -282,8 +335,12 @@ export const __experimentalGetDirtyEntityRecords = createSelector(
|
|
|
282
335
|
Object.keys( data[ kind ] ).forEach( ( name ) => {
|
|
283
336
|
const primaryKeys = Object.keys(
|
|
284
337
|
data[ kind ][ name ].edits
|
|
285
|
-
).filter(
|
|
286
|
-
|
|
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 )
|
|
287
344
|
);
|
|
288
345
|
|
|
289
346
|
if ( primaryKeys.length ) {
|
|
@@ -408,7 +465,10 @@ export const getEntityRecordNonTransientEdits = createSelector(
|
|
|
408
465
|
return acc;
|
|
409
466
|
}, {} );
|
|
410
467
|
},
|
|
411
|
-
( state ) => [
|
|
468
|
+
( state, kind, name, recordId ) => [
|
|
469
|
+
state.entities.config,
|
|
470
|
+
get( state.entities.data, [ kind, name, 'edits', recordId ] ),
|
|
471
|
+
]
|
|
412
472
|
);
|
|
413
473
|
|
|
414
474
|
/**
|
|
@@ -446,7 +506,29 @@ export const getEditedEntityRecord = createSelector(
|
|
|
446
506
|
...getRawEntityRecord( state, kind, name, recordId ),
|
|
447
507
|
...getEntityRecordEdits( state, kind, name, recordId ),
|
|
448
508
|
} ),
|
|
449
|
-
( state ) =>
|
|
509
|
+
( state, kind, name, recordId, query ) => {
|
|
510
|
+
const context = query?.context ?? 'default';
|
|
511
|
+
return [
|
|
512
|
+
state.entities.config,
|
|
513
|
+
get( state.entities.data, [
|
|
514
|
+
kind,
|
|
515
|
+
name,
|
|
516
|
+
'queriedData',
|
|
517
|
+
'items',
|
|
518
|
+
context,
|
|
519
|
+
recordId,
|
|
520
|
+
] ),
|
|
521
|
+
get( state.entities.data, [
|
|
522
|
+
kind,
|
|
523
|
+
name,
|
|
524
|
+
'queriedData',
|
|
525
|
+
'itemIsComplete',
|
|
526
|
+
context,
|
|
527
|
+
recordId,
|
|
528
|
+
] ),
|
|
529
|
+
get( state.entities.data, [ kind, name, 'edits', recordId ] ),
|
|
530
|
+
];
|
|
531
|
+
}
|
|
450
532
|
);
|
|
451
533
|
|
|
452
534
|
/**
|
|
@@ -615,7 +697,18 @@ export function hasRedo( state ) {
|
|
|
615
697
|
* @return {Object} The current theme.
|
|
616
698
|
*/
|
|
617
699
|
export function getCurrentTheme( state ) {
|
|
618
|
-
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;
|
|
619
712
|
}
|
|
620
713
|
|
|
621
714
|
/**
|
|
@@ -626,7 +719,7 @@ export function getCurrentTheme( state ) {
|
|
|
626
719
|
* @return {*} Index data.
|
|
627
720
|
*/
|
|
628
721
|
export function getThemeSupports( state ) {
|
|
629
|
-
return state
|
|
722
|
+
return getCurrentTheme( state )?.theme_supports ?? EMPTY_OBJECT;
|
|
630
723
|
}
|
|
631
724
|
|
|
632
725
|
/**
|
|
@@ -813,3 +906,18 @@ export function __experimentalGetTemplateForLink( state, link ) {
|
|
|
813
906
|
}
|
|
814
907
|
return template;
|
|
815
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,
|
|
@@ -33,16 +34,17 @@ describe( 'editEntityRecord', () => {
|
|
|
33
34
|
const select = {
|
|
34
35
|
getEntity: jest.fn(),
|
|
35
36
|
};
|
|
36
|
-
const fulfillment =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
const fulfillment = () =>
|
|
38
|
+
editEntityRecord(
|
|
39
|
+
entity.kind,
|
|
40
|
+
entity.name,
|
|
41
|
+
entity.id,
|
|
42
|
+
{}
|
|
43
|
+
)( { select } );
|
|
44
|
+
expect( fulfillment ).toThrow(
|
|
44
45
|
`The entity being edited (${ entity.kind }, ${ entity.name }) does not have a loaded config.`
|
|
45
46
|
);
|
|
47
|
+
expect( select.getEntity ).toHaveBeenCalledTimes( 1 );
|
|
46
48
|
} );
|
|
47
49
|
} );
|
|
48
50
|
|
|
@@ -106,6 +108,94 @@ describe( 'deleteEntityRecord', () => {
|
|
|
106
108
|
} );
|
|
107
109
|
} );
|
|
108
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
|
+
|
|
109
199
|
describe( 'saveEntityRecord', () => {
|
|
110
200
|
beforeEach( async () => {
|
|
111
201
|
apiFetch.mockReset();
|
|
@@ -386,21 +476,7 @@ describe( 'receiveCurrentUser', () => {
|
|
|
386
476
|
|
|
387
477
|
describe( '__experimentalBatch', () => {
|
|
388
478
|
it( 'batches multiple actions together', async () => {
|
|
389
|
-
const
|
|
390
|
-
[
|
|
391
|
-
( { saveEntityRecord: _saveEntityRecord } ) =>
|
|
392
|
-
_saveEntityRecord( 'root', 'widget', {} ),
|
|
393
|
-
( { saveEditedEntityRecord: _saveEditedEntityRecord } ) =>
|
|
394
|
-
_saveEditedEntityRecord( 'root', 'widget', 123 ),
|
|
395
|
-
( { deleteEntityRecord: _deleteEntityRecord } ) =>
|
|
396
|
-
_deleteEntityRecord( 'root', 'widget', 123, {} ),
|
|
397
|
-
],
|
|
398
|
-
{ __unstableProcessor: ( inputs ) => Promise.resolve( inputs ) }
|
|
399
|
-
);
|
|
400
|
-
// Run generator up to `yield getDispatch()`.
|
|
401
|
-
const { value: getDispatchControl } = generator.next();
|
|
402
|
-
expect( getDispatchControl ).toEqual( { type: 'GET_DISPATCH' } );
|
|
403
|
-
const actions = {
|
|
479
|
+
const dispatch = {
|
|
404
480
|
saveEntityRecord: jest.fn(
|
|
405
481
|
( kind, name, record, { __unstableFetch } ) => {
|
|
406
482
|
__unstableFetch( {} );
|
|
@@ -420,36 +496,39 @@ describe( '__experimentalBatch', () => {
|
|
|
420
496
|
}
|
|
421
497
|
),
|
|
422
498
|
};
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
499
|
+
|
|
500
|
+
const results = await __experimentalBatch(
|
|
501
|
+
[
|
|
502
|
+
( { saveEntityRecord: _saveEntityRecord } ) =>
|
|
503
|
+
_saveEntityRecord( 'root', 'widget', {} ),
|
|
504
|
+
( { saveEditedEntityRecord: _saveEditedEntityRecord } ) =>
|
|
505
|
+
_saveEditedEntityRecord( 'root', 'widget', 123 ),
|
|
506
|
+
( { deleteEntityRecord: _deleteEntityRecord } ) =>
|
|
507
|
+
_deleteEntityRecord( 'root', 'widget', 123, {} ),
|
|
508
|
+
],
|
|
509
|
+
{ __unstableProcessor: ( inputs ) => Promise.resolve( inputs ) }
|
|
510
|
+
)( { dispatch } );
|
|
511
|
+
|
|
512
|
+
expect( dispatch.saveEntityRecord ).toHaveBeenCalledWith(
|
|
427
513
|
'root',
|
|
428
514
|
'widget',
|
|
429
515
|
{},
|
|
430
516
|
{ __unstableFetch: expect.any( Function ) }
|
|
431
517
|
);
|
|
432
|
-
expect(
|
|
518
|
+
expect( dispatch.saveEditedEntityRecord ).toHaveBeenCalledWith(
|
|
433
519
|
'root',
|
|
434
520
|
'widget',
|
|
435
521
|
123,
|
|
436
522
|
{ __unstableFetch: expect.any( Function ) }
|
|
437
523
|
);
|
|
438
|
-
expect(
|
|
524
|
+
expect( dispatch.deleteEntityRecord ).toHaveBeenCalledWith(
|
|
439
525
|
'root',
|
|
440
526
|
'widget',
|
|
441
527
|
123,
|
|
442
528
|
{},
|
|
443
529
|
{ __unstableFetch: expect.any( Function ) }
|
|
444
530
|
);
|
|
445
|
-
|
|
446
|
-
type: 'AWAIT_PROMISE',
|
|
447
|
-
promise: expect.any( Promise ),
|
|
448
|
-
} );
|
|
449
|
-
// Run generator to the end.
|
|
450
|
-
const { value: results } = generator.next(
|
|
451
|
-
await awaitPromiseControl.promise
|
|
452
|
-
);
|
|
531
|
+
|
|
453
532
|
expect( results ).toEqual( [
|
|
454
533
|
{ id: 123, created: true },
|
|
455
534
|
{ id: 123, updated: true },
|