@wordpress/core-data 7.51.0 → 7.52.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 (84) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/actions.cjs +54 -1
  3. package/build/actions.cjs.map +3 -3
  4. package/build/components/entities-saved-states/entity-record-item.cjs +67 -0
  5. package/build/components/entities-saved-states/entity-record-item.cjs.map +7 -0
  6. package/build/components/entities-saved-states/entity-type-list.cjs +135 -0
  7. package/build/components/entities-saved-states/entity-type-list.cjs.map +7 -0
  8. package/build/components/entities-saved-states/hooks/use-is-dirty.cjs +97 -0
  9. package/build/components/entities-saved-states/hooks/use-is-dirty.cjs.map +7 -0
  10. package/build/components/entities-saved-states/index.cjs +215 -0
  11. package/build/components/entities-saved-states/index.cjs.map +7 -0
  12. package/build/entities.cjs +2 -0
  13. package/build/entities.cjs.map +2 -2
  14. package/build/private-actions.cjs +85 -0
  15. package/build/private-actions.cjs.map +3 -3
  16. package/build/private-apis.cjs +19 -0
  17. package/build/private-apis.cjs.map +3 -3
  18. package/build/utils/crdt.cjs +4 -2
  19. package/build/utils/crdt.cjs.map +2 -2
  20. package/build/utils/get-template-info.cjs +53 -0
  21. package/build/utils/get-template-info.cjs.map +7 -0
  22. package/build/utils/get-template-part-icon.cjs +43 -0
  23. package/build/utils/get-template-part-icon.cjs.map +7 -0
  24. package/build-module/actions.mjs +57 -1
  25. package/build-module/actions.mjs.map +2 -2
  26. package/build-module/components/entities-saved-states/entity-record-item.mjs +46 -0
  27. package/build-module/components/entities-saved-states/entity-record-item.mjs.map +7 -0
  28. package/build-module/components/entities-saved-states/entity-type-list.mjs +104 -0
  29. package/build-module/components/entities-saved-states/entity-type-list.mjs.map +7 -0
  30. package/build-module/components/entities-saved-states/hooks/use-is-dirty.mjs +72 -0
  31. package/build-module/components/entities-saved-states/hooks/use-is-dirty.mjs.map +7 -0
  32. package/build-module/components/entities-saved-states/index.mjs +187 -0
  33. package/build-module/components/entities-saved-states/index.mjs.map +7 -0
  34. package/build-module/entities.mjs +2 -0
  35. package/build-module/entities.mjs.map +2 -2
  36. package/build-module/private-actions.mjs +84 -0
  37. package/build-module/private-actions.mjs.map +2 -2
  38. package/build-module/private-apis.mjs +12 -0
  39. package/build-module/private-apis.mjs.map +2 -2
  40. package/build-module/utils/crdt.mjs +2 -1
  41. package/build-module/utils/crdt.mjs.map +2 -2
  42. package/build-module/utils/get-template-info.mjs +28 -0
  43. package/build-module/utils/get-template-info.mjs.map +7 -0
  44. package/build-module/utils/get-template-part-icon.mjs +24 -0
  45. package/build-module/utils/get-template-part-icon.mjs.map +7 -0
  46. package/build-types/actions.d.ts.map +1 -1
  47. package/build-types/components/entities-saved-states/entity-record-item.d.ts +6 -0
  48. package/build-types/components/entities-saved-states/entity-record-item.d.ts.map +1 -0
  49. package/build-types/components/entities-saved-states/entity-type-list.d.ts +6 -0
  50. package/build-types/components/entities-saved-states/entity-type-list.d.ts.map +1 -0
  51. package/build-types/components/entities-saved-states/hooks/use-is-dirty.d.ts +11 -0
  52. package/build-types/components/entities-saved-states/hooks/use-is-dirty.d.ts.map +1 -0
  53. package/build-types/components/entities-saved-states/index.d.ts +49 -0
  54. package/build-types/components/entities-saved-states/index.d.ts.map +1 -0
  55. package/build-types/entities.d.ts.map +1 -1
  56. package/build-types/private-actions.d.ts +19 -0
  57. package/build-types/private-actions.d.ts.map +1 -1
  58. package/build-types/private-apis.d.ts +9 -0
  59. package/build-types/private-apis.d.ts.map +1 -1
  60. package/build-types/utils/crdt.d.ts +8 -0
  61. package/build-types/utils/crdt.d.ts.map +1 -1
  62. package/build-types/utils/get-template-info.d.ts +13 -0
  63. package/build-types/utils/get-template-info.d.ts.map +1 -0
  64. package/build-types/utils/get-template-part-icon.d.ts +9 -0
  65. package/build-types/utils/get-template-part-icon.d.ts.map +1 -0
  66. package/build-types/utils/on-sub-key.d.ts +4 -0
  67. package/build-types/utils/on-sub-key.d.ts.map +1 -0
  68. package/package.json +27 -22
  69. package/src/actions.js +92 -1
  70. package/src/components/entities-saved-states/entity-record-item.js +60 -0
  71. package/src/components/entities-saved-states/entity-type-list.js +130 -0
  72. package/src/components/entities-saved-states/hooks/use-is-dirty.js +101 -0
  73. package/src/components/entities-saved-states/index.js +242 -0
  74. package/src/components/entities-saved-states/style.scss +70 -0
  75. package/src/components/entities-saved-states/test/use-is-dirty.js +88 -0
  76. package/src/entities.js +2 -0
  77. package/src/private-actions.js +127 -0
  78. package/src/private-apis.ts +12 -0
  79. package/src/style.scss +1 -0
  80. package/src/test/actions.js +587 -0
  81. package/src/utils/crdt.ts +1 -1
  82. package/src/utils/get-template-info.js +52 -0
  83. package/src/utils/get-template-part-icon.js +30 -0
  84. package/src/utils/test/get-template-info.js +224 -0
@@ -0,0 +1,70 @@
1
+ @use "@wordpress/base-styles/colors" as *;
2
+ @use "@wordpress/base-styles/variables" as *;
3
+
4
+ .entities-saved-states__panel-header {
5
+ box-sizing: border-box;
6
+ background: $white;
7
+ padding-left: $grid-unit-20;
8
+ padding-right: $grid-unit-20;
9
+ height: $header-height;
10
+ border-bottom: $border-width solid $gray-300;
11
+ }
12
+
13
+ .entities-saved-states__text-prompt {
14
+ padding: $grid-unit-20;
15
+ padding-bottom: $grid-unit-05;
16
+ .entities-saved-states__text-prompt--header {
17
+ display: block;
18
+ margin-bottom: $grid-unit-15;
19
+ }
20
+ }
21
+
22
+ .entities-saved-states__panel.is-inline {
23
+ .entities-saved-states__text-prompt {
24
+ padding: 0;
25
+ }
26
+
27
+ .entities-saved-states__panel-body {
28
+ padding-left: 0;
29
+ padding-right: 0;
30
+ border: 0;
31
+
32
+ > h2 {
33
+ margin-left: -1 * $grid-unit-20;
34
+ margin-right: -1 * $grid-unit-20;
35
+ margin-bottom: 0;
36
+
37
+ button {
38
+ font-size: $font-size-x-small;
39
+ text-transform: uppercase;
40
+ }
41
+ }
42
+ }
43
+
44
+ .entities-saved-states__text-prompt--header-wrapper {
45
+ display: none;
46
+ }
47
+
48
+ .entities-saved-states__text-prompt--changes-count {
49
+ margin-top: 0;
50
+ margin-bottom: $grid-unit-10;
51
+ }
52
+
53
+ .entities-saved-states__panel-footer {
54
+ margin-top: $grid-unit-20;
55
+ }
56
+ }
57
+
58
+ .entities-saved-states__change-control {
59
+ flex: 1;
60
+ }
61
+
62
+ .entities-saved-states__changes {
63
+ font-size: $default-font-size;
64
+ margin: $grid-unit-05 $grid-unit-20 0 $grid-unit-30;
65
+ list-style: disc;
66
+
67
+ li {
68
+ margin-bottom: $grid-unit-05;
69
+ }
70
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { act, renderHook } from '@testing-library/react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { useIsDirty } from '../hooks/use-is-dirty';
10
+
11
+ jest.mock( '@wordpress/data', () => {
12
+ return {
13
+ useSelect: jest.fn().mockImplementation( ( fn ) => {
14
+ const select = () => {
15
+ return {
16
+ __experimentalGetDirtyEntityRecords: jest
17
+ .fn()
18
+ .mockReturnValue( [
19
+ {
20
+ kind: 'root',
21
+ name: 'site',
22
+ title: 'title',
23
+ property: 'property',
24
+ },
25
+ {
26
+ kind: 'postType',
27
+ name: 'post',
28
+ title: 'title',
29
+ property: 'property',
30
+ },
31
+ ] ),
32
+ getEntityRecordEdits: jest.fn().mockReturnValue( {
33
+ title: 'My Site',
34
+ } ),
35
+ getEntityConfig: jest.fn().mockReturnValue( {
36
+ meta: { labels: { title: 'Title' } },
37
+ } ),
38
+ };
39
+ };
40
+ return fn( select );
41
+ } ),
42
+ };
43
+ } );
44
+
45
+ describe( 'useIsDirty', () => {
46
+ it( 'should calculate dirtyEntityRecords', () => {
47
+ const { result } = renderHook( () => useIsDirty() );
48
+ expect( result.current.dirtyEntityRecords ).toEqual( [
49
+ {
50
+ kind: 'postType',
51
+ name: 'post',
52
+ property: 'property',
53
+ title: 'title',
54
+ },
55
+ { kind: 'root', name: 'site', property: 'title', title: 'Title' },
56
+ ] );
57
+ } );
58
+ it( 'should return `isDirty: true` when there are changes', () => {
59
+ const { result } = renderHook( () => useIsDirty() );
60
+ expect( result.current.isDirty ).toBeTruthy();
61
+ } );
62
+ it( 'should return `isDirty: false` when there are NO changes', async () => {
63
+ const { result } = renderHook( () => useIsDirty() );
64
+ act( () => {
65
+ result.current.setUnselectedEntities(
66
+ {
67
+ kind: 'postType',
68
+ name: 'post',
69
+ key: 'key',
70
+ property: 'property',
71
+ },
72
+ false
73
+ );
74
+ } );
75
+ act( () => {
76
+ result.current.setUnselectedEntities(
77
+ {
78
+ kind: 'root',
79
+ name: 'site',
80
+ key: 'key',
81
+ property: 'property',
82
+ },
83
+ false
84
+ );
85
+ } );
86
+ expect( result.current.isDirty ).toBeFalsy();
87
+ } );
88
+ } );
package/src/entities.js CHANGED
@@ -49,8 +49,10 @@ export const rootEntitiesConfig = [
49
49
  'description',
50
50
  'gmt_offset',
51
51
  'home',
52
+ 'image_max_bit_depth',
52
53
  'image_sizes',
53
54
  'image_size_threshold',
55
+ 'image_strip_meta',
54
56
  'name',
55
57
  'site_icon',
56
58
  'site_icon_url',
@@ -2,6 +2,9 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import apiFetch from '@wordpress/api-fetch';
5
+ import { store as noticesStore } from '@wordpress/notices';
6
+ import { store as blockEditorStore } from '@wordpress/block-editor';
7
+ import { __ } from '@wordpress/i18n';
5
8
 
6
9
  /**
7
10
  * Internal dependencies
@@ -247,3 +250,127 @@ export function setSyncConnectionStatus( kind, name, key, status ) {
247
250
  status,
248
251
  };
249
252
  }
253
+
254
+ /**
255
+ * Save entity records marked as dirty.
256
+ *
257
+ * @param {Object} options Options for the action.
258
+ * @param {Function} [options.onSave] Callback when saving happens.
259
+ * @param {object[]} [options.dirtyEntityRecords] Array of dirty entities.
260
+ * @param {object[]} [options.entitiesToSkip] Array of entities to skip saving.
261
+ * @param {Function} [options.close] Callback when the actions is called. It should be consolidated with `onSave`.
262
+ * @param {string} [options.successNoticeContent] Optional custom success notice content. Defaults to 'Site updated.'.
263
+ */
264
+ export const saveDirtyEntities =
265
+ ( {
266
+ onSave,
267
+ dirtyEntityRecords = [],
268
+ entitiesToSkip = [],
269
+ close,
270
+ successNoticeContent,
271
+ } = {} ) =>
272
+ ( { registry } ) => {
273
+ const PUBLISH_ON_SAVE_ENTITIES = [
274
+ { kind: 'postType', name: 'wp_navigation' },
275
+ ];
276
+ const saveNoticeId = 'site-editor-save-success';
277
+ const homeUrl = registry
278
+ .select( STORE_NAME )
279
+ .getEntityRecord( 'root', '__unstableBase' )?.home;
280
+ registry.dispatch( noticesStore ).removeNotice( saveNoticeId );
281
+ const entitiesToSave = dirtyEntityRecords.filter(
282
+ ( { kind, name, key, property } ) => {
283
+ return ! entitiesToSkip.some(
284
+ ( elt ) =>
285
+ elt.kind === kind &&
286
+ elt.name === name &&
287
+ elt.key === key &&
288
+ elt.property === property
289
+ );
290
+ }
291
+ );
292
+ close?.( entitiesToSave );
293
+ const siteItemsToSave = [];
294
+ const pendingSavedRecords = [];
295
+ entitiesToSave.forEach( ( { kind, name, key, property } ) => {
296
+ if ( 'root' === kind && 'site' === name ) {
297
+ siteItemsToSave.push( property );
298
+ } else {
299
+ if (
300
+ PUBLISH_ON_SAVE_ENTITIES.some(
301
+ ( typeToPublish ) =>
302
+ typeToPublish.kind === kind &&
303
+ typeToPublish.name === name
304
+ )
305
+ ) {
306
+ registry
307
+ .dispatch( STORE_NAME )
308
+ .editEntityRecord( kind, name, key, {
309
+ status: 'publish',
310
+ } );
311
+ }
312
+
313
+ pendingSavedRecords.push(
314
+ registry
315
+ .dispatch( STORE_NAME )
316
+ .saveEditedEntityRecord( kind, name, key )
317
+ );
318
+ }
319
+ } );
320
+ if ( siteItemsToSave.length ) {
321
+ pendingSavedRecords.push(
322
+ registry
323
+ .dispatch( STORE_NAME )
324
+ .__experimentalSaveSpecifiedEntityEdits(
325
+ 'root',
326
+ 'site',
327
+ undefined,
328
+ siteItemsToSave
329
+ )
330
+ );
331
+ }
332
+ registry
333
+ .dispatch( blockEditorStore )
334
+ .__unstableMarkLastChangeAsPersistent();
335
+
336
+ Promise.all( pendingSavedRecords )
337
+ .then( async ( values ) => {
338
+ if ( onSave ) {
339
+ await onSave();
340
+ }
341
+ return values;
342
+ } )
343
+ .then( ( values ) => {
344
+ if (
345
+ values.some( ( value ) => typeof value === 'undefined' )
346
+ ) {
347
+ registry
348
+ .dispatch( noticesStore )
349
+ .createErrorNotice( __( 'Saving failed.' ) );
350
+ } else {
351
+ registry
352
+ .dispatch( noticesStore )
353
+ .createSuccessNotice(
354
+ successNoticeContent || __( 'Site updated.' ),
355
+ {
356
+ type: 'snackbar',
357
+ id: saveNoticeId,
358
+ actions: [
359
+ {
360
+ label: __( 'View site' ),
361
+ url: homeUrl,
362
+ openInNewTab: true,
363
+ },
364
+ ],
365
+ }
366
+ );
367
+ }
368
+ } )
369
+ .catch( ( error ) =>
370
+ registry
371
+ .dispatch( noticesStore )
372
+ .createErrorNotice(
373
+ `${ __( 'Saving failed.' ) } ${ error }`
374
+ )
375
+ );
376
+ };
@@ -10,6 +10,13 @@ import {
10
10
  useOnCollaboratorLeave,
11
11
  useOnPostSave,
12
12
  } from './hooks/use-post-editor-awareness-state';
13
+ import { getTemplateInfo } from './utils/get-template-info';
14
+ import { getTemplatePartIcon } from './utils/get-template-part-icon';
15
+ import {
16
+ EntitiesSavedStatesExtensible,
17
+ default as EntitiesSavedStates,
18
+ } from './components/entities-saved-states';
19
+ import { useIsDirty as useEntitiesSavedStatesIsDirty } from './components/entities-saved-states/hooks/use-is-dirty';
13
20
  import { lock } from './lock-unlock';
14
21
  import { retrySyncConnection } from './sync';
15
22
  import {
@@ -18,6 +25,11 @@ import {
18
25
  } from './utils/crdt-user-selections';
19
26
 
20
27
  const lockedApis = {
28
+ EntitiesSavedStates,
29
+ EntitiesSavedStatesExtensible,
30
+ getTemplateInfo,
31
+ getTemplatePartIcon,
32
+ useEntitiesSavedStatesIsDirty,
21
33
  useEntityRecordsWithPermissions,
22
34
  RECEIVE_INTERMEDIATE_RESULTS,
23
35
  retrySyncConnection,
package/src/style.scss ADDED
@@ -0,0 +1 @@
1
+ @use "./components/entities-saved-states/style.scss" as *;