@wordpress/core-data 7.48.1 → 7.48.2-next.v.202606191442.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 (82) hide show
  1. package/build/actions.cjs +1 -7
  2. package/build/actions.cjs.map +3 -3
  3. package/build/entities.cjs +2 -1
  4. package/build/entities.cjs.map +2 -2
  5. package/build/entity-types/helpers.cjs.map +1 -1
  6. package/build/hooks/use-entity-record.cjs +21 -19
  7. package/build/hooks/use-entity-record.cjs.map +3 -3
  8. package/build/hooks/use-entity-records.cjs +22 -20
  9. package/build/hooks/use-entity-records.cjs.map +3 -3
  10. package/build/hooks/use-post-editor-awareness-state.cjs.map +1 -1
  11. package/build/hooks/use-query-select.cjs +2 -20
  12. package/build/hooks/use-query-select.cjs.map +2 -2
  13. package/build/hooks/utils.cjs +53 -0
  14. package/build/hooks/utils.cjs.map +7 -0
  15. package/build/reducer.cjs +10 -7
  16. package/build/reducer.cjs.map +2 -2
  17. package/build/types.cjs.map +1 -1
  18. package/build/utils/clear-unchanged-edits.cjs +51 -0
  19. package/build/utils/clear-unchanged-edits.cjs.map +7 -0
  20. package/build/utils/crdt-blocks.cjs.map +1 -1
  21. package/build/utils/crdt-user-selections.cjs.map +1 -1
  22. package/build/utils/crdt-utils.cjs.map +1 -1
  23. package/build/utils/crdt.cjs.map +1 -1
  24. package/build/utils/index.cjs +3 -0
  25. package/build/utils/index.cjs.map +2 -2
  26. package/build/utils/set-nested-value.cjs.map +1 -1
  27. package/build-module/actions.mjs +2 -8
  28. package/build-module/actions.mjs.map +2 -2
  29. package/build-module/entities.mjs +2 -1
  30. package/build-module/entities.mjs.map +2 -2
  31. package/build-module/hooks/use-entity-record.mjs +21 -19
  32. package/build-module/hooks/use-entity-record.mjs.map +2 -2
  33. package/build-module/hooks/use-entity-records.mjs +20 -18
  34. package/build-module/hooks/use-entity-records.mjs.map +2 -2
  35. package/build-module/hooks/use-post-editor-awareness-state.mjs.map +1 -1
  36. package/build-module/hooks/use-query-select.mjs +2 -20
  37. package/build-module/hooks/use-query-select.mjs.map +2 -2
  38. package/build-module/hooks/utils.mjs +28 -0
  39. package/build-module/hooks/utils.mjs.map +7 -0
  40. package/build-module/reducer.mjs +11 -8
  41. package/build-module/reducer.mjs.map +2 -2
  42. package/build-module/utils/clear-unchanged-edits.mjs +20 -0
  43. package/build-module/utils/clear-unchanged-edits.mjs.map +7 -0
  44. package/build-module/utils/crdt-blocks.mjs.map +1 -1
  45. package/build-module/utils/crdt-user-selections.mjs.map +1 -1
  46. package/build-module/utils/crdt-utils.mjs.map +1 -1
  47. package/build-module/utils/crdt.mjs.map +1 -1
  48. package/build-module/utils/index.mjs +22 -20
  49. package/build-module/utils/index.mjs.map +2 -2
  50. package/build-module/utils/set-nested-value.mjs.map +1 -1
  51. package/build-types/actions.d.ts.map +1 -1
  52. package/build-types/entities.d.ts.map +1 -1
  53. package/build-types/hooks/use-entity-record.d.ts +4 -0
  54. package/build-types/hooks/use-entity-record.d.ts.map +1 -1
  55. package/build-types/hooks/use-entity-records.d.ts +5 -1
  56. package/build-types/hooks/use-entity-records.d.ts.map +1 -1
  57. package/build-types/hooks/utils.d.ts +22 -0
  58. package/build-types/hooks/utils.d.ts.map +1 -0
  59. package/build-types/index.d.ts +8 -8
  60. package/build-types/private-actions.d.ts.map +1 -1
  61. package/build-types/reducer.d.ts.map +1 -1
  62. package/build-types/selectors.d.ts +8 -8
  63. package/build-types/selectors.d.ts.map +1 -1
  64. package/build-types/utils/clear-unchanged-edits.d.ts +12 -0
  65. package/build-types/utils/clear-unchanged-edits.d.ts.map +1 -0
  66. package/build-types/utils/index.d.ts +1 -0
  67. package/build-types/utils/index.d.ts.map +1 -1
  68. package/package.json +24 -19
  69. package/src/actions.js +2 -10
  70. package/src/entities.js +5 -0
  71. package/src/hooks/test/use-entity-record.js +5 -1
  72. package/src/hooks/use-entity-record.ts +26 -19
  73. package/src/hooks/use-entity-records.ts +26 -18
  74. package/src/hooks/use-query-select.ts +2 -23
  75. package/src/hooks/utils.ts +40 -0
  76. package/src/reducer.js +13 -9
  77. package/src/test/entities.js +51 -0
  78. package/src/utils/clear-unchanged-edits.ts +34 -0
  79. package/src/utils/index.js +1 -0
  80. package/src/utils/test/clear-unchanged-edits.js +42 -0
  81. package/build-types/utils/on-sub-key.d.ts +0 -4
  82. package/build-types/utils/on-sub-key.d.ts.map +0 -1
@@ -7,7 +7,7 @@ import { useSelect } from '@wordpress/data';
7
7
  * Internal dependencies
8
8
  */
9
9
  import memoize from 'memize';
10
- import { Status } from './constants';
10
+ import { getResolutionStatus } from './utils';
11
11
 
12
12
  export const META_SELECTORS = [
13
13
  'getIsResolving',
@@ -114,30 +114,9 @@ const enrichSelectors = memoize( ( ( selectors ) => {
114
114
  args
115
115
  )?.status;
116
116
 
117
- let status;
118
- switch ( resolutionStatus ) {
119
- case 'resolving':
120
- status = Status.Resolving;
121
- break;
122
- case 'finished':
123
- status = Status.Success;
124
- break;
125
- case 'error':
126
- status = Status.Error;
127
- break;
128
- case undefined:
129
- status = Status.Idle;
130
- break;
131
- }
132
-
133
117
  return {
134
118
  data,
135
- status,
136
- isResolving: status === Status.Resolving,
137
- hasStarted: status !== Status.Idle,
138
- hasResolved:
139
- status === Status.Success ||
140
- status === Status.Error,
119
+ ...getResolutionStatus( resolutionStatus ),
141
120
  };
142
121
  },
143
122
  } );
@@ -0,0 +1,40 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import type { ResolutionStatus } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { Status } from './constants';
10
+
11
+ /**
12
+ * Normalizes a resolution status from the store into the resolution info
13
+ * shared by the entity record hooks and `useQuerySelect`.
14
+ *
15
+ * @param resolutionStatus Status returned by the `getResolutionState` selector.
16
+ * @return Resolution info object.
17
+ */
18
+ export function getResolutionStatus( resolutionStatus?: ResolutionStatus ) {
19
+ let status: Status;
20
+ switch ( resolutionStatus ) {
21
+ case 'resolving':
22
+ status = Status.Resolving;
23
+ break;
24
+ case 'finished':
25
+ status = Status.Success;
26
+ break;
27
+ case 'error':
28
+ status = Status.Error;
29
+ break;
30
+ default:
31
+ status = Status.Idle;
32
+ }
33
+
34
+ return {
35
+ status,
36
+ isResolving: status === Status.Resolving,
37
+ hasStarted: status !== Status.Idle,
38
+ hasResolved: status === Status.Success || status === Status.Error,
39
+ };
40
+ }
package/src/reducer.js CHANGED
@@ -13,7 +13,7 @@ import { createUndoManager } from '@wordpress/undo-manager';
13
13
  /**
14
14
  * Internal dependencies
15
15
  */
16
- import { ifMatchingAction, replaceAction } from './utils';
16
+ import { clearUnchangedEdits, ifMatchingAction, replaceAction } from './utils';
17
17
  import { reducer as queriedDataReducer } from './queried-data';
18
18
  import { rootEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities';
19
19
  import { ConnectionErrorCode } from './sync';
@@ -150,19 +150,23 @@ const withMultiEntityRecordEdits = ( reducer ) => ( state, action ) => {
150
150
 
151
151
  let newState = state;
152
152
  record.forEach( ( { id: { kind, name, recordId }, changes } ) => {
153
+ const persistedRecord =
154
+ state?.queriedData?.items?.default?.[ recordId ];
155
+ const edits = Object.fromEntries(
156
+ Object.entries( changes ).map( ( [ key, value ] ) => [
157
+ key,
158
+ action.type === 'UNDO' ? value.from : value.to,
159
+ ] )
160
+ );
161
+
153
162
  newState = reducer( newState, {
154
163
  type: 'EDIT_ENTITY_RECORD',
155
164
  kind,
156
165
  name,
157
166
  recordId,
158
- edits: Object.entries( changes ).reduce(
159
- ( acc, [ key, value ] ) => {
160
- acc[ key ] =
161
- action.type === 'UNDO' ? value.from : value.to;
162
- return acc;
163
- },
164
- {}
165
- ),
167
+ // Clear edits matching the persisted record so the entity is
168
+ // no longer dirty after undoing back to its saved state.
169
+ edits: clearUnchangedEdits( edits, persistedRecord ),
166
170
  } );
167
171
  } );
168
172
  return newState;
@@ -136,15 +136,20 @@ describe( 'prePersistPostType', () => {
136
136
 
137
137
  describe( 'loadPostTypeEntities', () => {
138
138
  let originalCollaborationEnabled;
139
+ let originalCollaborationDisabledPostTypes;
139
140
 
140
141
  beforeEach( () => {
141
142
  apiFetch.mockReset();
142
143
  applyPostChangesToCRDTDoc.mockReset();
143
144
  originalCollaborationEnabled = window._wpCollaborationEnabled;
145
+ originalCollaborationDisabledPostTypes =
146
+ window._wpCollaborationDisabledPostTypes;
144
147
  } );
145
148
 
146
149
  afterEach( () => {
147
150
  window._wpCollaborationEnabled = originalCollaborationEnabled;
151
+ window._wpCollaborationDisabledPostTypes =
152
+ originalCollaborationDisabledPostTypes;
148
153
  } );
149
154
 
150
155
  it( 'should include custom taxonomy rest_bases in synced properties when collaboration is enabled', async () => {
@@ -224,6 +229,52 @@ describe( 'loadPostTypeEntities', () => {
224
229
  expect( syncedProperties ).not.toContain( 'tags' );
225
230
  } );
226
231
 
232
+ it( 'should sync post type entities by default', async () => {
233
+ window._wpCollaborationEnabled = false;
234
+ window._wpCollaborationDisabledPostTypes = undefined;
235
+
236
+ const mockPostTypes = {
237
+ post: {
238
+ name: 'Posts',
239
+ rest_base: 'posts',
240
+ rest_namespace: 'wp/v2',
241
+ },
242
+ };
243
+
244
+ apiFetch.mockResolvedValueOnce( mockPostTypes );
245
+
246
+ const postTypeLoader = additionalEntityConfigLoaders.find(
247
+ ( loader ) => loader.kind === 'postType'
248
+ );
249
+ const entities = await postTypeLoader.loadEntities();
250
+ const postEntity = entities.find( ( e ) => e.name === 'post' );
251
+
252
+ expect( postEntity.syncConfig.shouldSync() ).toBe( true );
253
+ } );
254
+
255
+ it( 'should not sync post type entities disabled for collaboration', async () => {
256
+ window._wpCollaborationEnabled = false;
257
+ window._wpCollaborationDisabledPostTypes = [ 'book' ];
258
+
259
+ const mockPostTypes = {
260
+ book: {
261
+ name: 'Books',
262
+ rest_base: 'books',
263
+ rest_namespace: 'wp/v2',
264
+ },
265
+ };
266
+
267
+ apiFetch.mockResolvedValueOnce( mockPostTypes );
268
+
269
+ const postTypeLoader = additionalEntityConfigLoaders.find(
270
+ ( loader ) => loader.kind === 'postType'
271
+ );
272
+ const entities = await postTypeLoader.loadEntities();
273
+ const bookEntity = entities.find( ( e ) => e.name === 'book' );
274
+
275
+ expect( bookEntity.syncConfig.shouldSync() ).toBe( false );
276
+ } );
277
+
227
278
  it( 'should skip taxonomy rest_base when taxonomy is not found in fetched taxonomies', async () => {
228
279
  window._wpCollaborationEnabled = true;
229
280
 
@@ -0,0 +1,34 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import fastDeepEqual from 'fast-deep-equal/es6/index.js';
5
+
6
+ /**
7
+ * Returns a copy of `edits` where any value equal to its persisted counterpart
8
+ * is set to `undefined`. The edits reducer treats `undefined` as a signal to
9
+ * drop the edit, so the property is no longer considered dirty.
10
+ *
11
+ * @param edits Edits keyed by property name.
12
+ * @param persistedRecord Persisted entity record to compare against.
13
+ *
14
+ * @return Edits with unchanged properties set to `undefined`.
15
+ */
16
+ export default function clearUnchangedEdits(
17
+ edits: Record< string, unknown >,
18
+ persistedRecord: Record< string, any > | undefined
19
+ ): Record< string, unknown > {
20
+ if ( ! persistedRecord ) {
21
+ return edits;
22
+ }
23
+
24
+ return Object.fromEntries(
25
+ Object.entries( edits ).map( ( [ key, value ] ) => {
26
+ const persisted =
27
+ persistedRecord[ key ]?.raw ?? persistedRecord[ key ];
28
+ return [
29
+ key,
30
+ fastDeepEqual( value, persisted ) ? undefined : value,
31
+ ];
32
+ } )
33
+ );
34
+ }
@@ -1,3 +1,4 @@
1
+ export { default as clearUnchangedEdits } from './clear-unchanged-edits';
1
2
  export { default as conservativeMapItem } from './conservative-map-item';
2
3
  export { default as getNormalizedCommaSeparable } from './get-normalized-comma-separable';
3
4
  export { default as ifMatchingAction } from './if-matching-action';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import clearUnchangedEdits from '../clear-unchanged-edits';
5
+
6
+ describe( 'clearUnchangedEdits', () => {
7
+ it( 'sets edits matching the persisted record to undefined', () => {
8
+ const result = clearUnchangedEdits(
9
+ { title: 'Hello', status: 'draft' },
10
+ { title: 'Hello', status: 'publish' }
11
+ );
12
+
13
+ expect( result ).toEqual( { title: undefined, status: 'draft' } );
14
+ } );
15
+
16
+ it( 'unwraps the persisted value from its `raw` subfield', () => {
17
+ const result = clearUnchangedEdits(
18
+ { title: 'Hello', content: 'World' },
19
+ {
20
+ title: { raw: 'Hello', rendered: '<p>Hello</p>' },
21
+ content: { raw: 'Changed', rendered: '<p>Changed</p>' },
22
+ }
23
+ );
24
+
25
+ expect( result ).toEqual( { title: undefined, content: 'World' } );
26
+ } );
27
+
28
+ it( 'compares deeply for object values', () => {
29
+ const result = clearUnchangedEdits(
30
+ { meta: { a: 1 }, other: { b: 1 } },
31
+ { meta: { a: 1 }, other: { b: 2 } }
32
+ );
33
+
34
+ expect( result ).toEqual( { meta: undefined, other: { b: 1 } } );
35
+ } );
36
+
37
+ it( 'keeps all edits when there is no persisted record', () => {
38
+ const result = clearUnchangedEdits( { title: 'Hello' }, undefined );
39
+
40
+ expect( result ).toEqual( { title: 'Hello' } );
41
+ } );
42
+ } );
@@ -1,4 +0,0 @@
1
- export function onSubKey(actionProperty: string): AnyFunction;
2
- export default onSubKey;
3
- export type AnyFunction = import("../types").AnyFunction;
4
- //# sourceMappingURL=on-sub-key.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"on-sub-key.d.ts","sourceRoot":"","sources":["../../src/utils/on-sub-key.js"],"names":[],"mappings":"AAUO,yCAJI,MAAM,GAEL,WAAW,CAwBrB;;0BAhCY,OAAO,UAAU,EAAE,WAAW"}