@wordpress/core-data 7.45.0 → 7.45.1-next.v.202605131032.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 (181) hide show
  1. package/build/actions.cjs +8 -6
  2. package/build/actions.cjs.map +2 -2
  3. package/build/awareness/post-editor-awareness.cjs +1 -1
  4. package/build/awareness/post-editor-awareness.cjs.map +2 -2
  5. package/build/resolvers.cjs +2 -1
  6. package/build/resolvers.cjs.map +2 -2
  7. package/build/types.cjs.map +2 -2
  8. package/build/utils/block-selection-history.cjs +4 -1
  9. package/build/utils/block-selection-history.cjs.map +2 -2
  10. package/build/utils/crdt-blocks.cjs +157 -89
  11. package/build/utils/crdt-blocks.cjs.map +2 -2
  12. package/build/utils/crdt-selection.cjs +1 -1
  13. package/build/utils/crdt-selection.cjs.map +2 -2
  14. package/build/utils/crdt-user-selections.cjs +4 -1
  15. package/build/utils/crdt-user-selections.cjs.map +2 -2
  16. package/build/utils/crdt-utils.cjs +18 -6
  17. package/build/utils/crdt-utils.cjs.map +2 -2
  18. package/build/utils/crdt.cjs +12 -2
  19. package/build/utils/crdt.cjs.map +2 -2
  20. package/build-module/actions.mjs +8 -6
  21. package/build-module/actions.mjs.map +2 -2
  22. package/build-module/awareness/post-editor-awareness.mjs +5 -2
  23. package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
  24. package/build-module/resolvers.mjs +2 -1
  25. package/build-module/resolvers.mjs.map +2 -2
  26. package/build-module/types.mjs.map +2 -2
  27. package/build-module/utils/block-selection-history.mjs +5 -1
  28. package/build-module/utils/block-selection-history.mjs.map +2 -2
  29. package/build-module/utils/crdt-blocks.mjs +162 -90
  30. package/build-module/utils/crdt-blocks.mjs.map +2 -2
  31. package/build-module/utils/crdt-selection.mjs +2 -1
  32. package/build-module/utils/crdt-selection.mjs.map +2 -2
  33. package/build-module/utils/crdt-user-selections.mjs +9 -2
  34. package/build-module/utils/crdt-user-selections.mjs.map +2 -2
  35. package/build-module/utils/crdt-utils.mjs +16 -6
  36. package/build-module/utils/crdt-utils.mjs.map +2 -2
  37. package/build-module/utils/crdt.mjs +13 -2
  38. package/build-module/utils/crdt.mjs.map +2 -2
  39. package/build-types/actions.d.ts +177 -64
  40. package/build-types/actions.d.ts.map +1 -1
  41. package/build-types/awareness/awareness-state.d.ts.map +1 -1
  42. package/build-types/awareness/base-awareness.d.ts +0 -3
  43. package/build-types/awareness/base-awareness.d.ts.map +1 -1
  44. package/build-types/awareness/post-editor-awareness.d.ts +1 -8
  45. package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
  46. package/build-types/awareness/typed-awareness.d.ts.map +1 -1
  47. package/build-types/batch/create-batch.d.ts +1 -1
  48. package/build-types/batch/create-batch.d.ts.map +1 -1
  49. package/build-types/batch/default-processor.d.ts.map +1 -1
  50. package/build-types/batch/index.d.ts +2 -2
  51. package/build-types/batch/index.d.ts.map +1 -1
  52. package/build-types/entities.d.ts +114 -87
  53. package/build-types/entities.d.ts.map +1 -1
  54. package/build-types/entity-context.d.ts +1 -1
  55. package/build-types/entity-context.d.ts.map +1 -1
  56. package/build-types/entity-provider.d.ts +2 -2
  57. package/build-types/entity-provider.d.ts.map +1 -1
  58. package/build-types/entity-types/attachment.d.ts.map +1 -1
  59. package/build-types/entity-types/base-entity-records.d.ts.map +1 -1
  60. package/build-types/entity-types/base.d.ts.map +1 -1
  61. package/build-types/entity-types/comment.d.ts.map +1 -1
  62. package/build-types/entity-types/font-collection.d.ts.map +1 -1
  63. package/build-types/entity-types/font-family.d.ts.map +1 -1
  64. package/build-types/entity-types/global-styles-revision.d.ts.map +1 -1
  65. package/build-types/entity-types/icon.d.ts.map +1 -1
  66. package/build-types/entity-types/menu-location.d.ts.map +1 -1
  67. package/build-types/entity-types/nav-menu-item.d.ts.map +1 -1
  68. package/build-types/entity-types/nav-menu.d.ts.map +1 -1
  69. package/build-types/entity-types/page.d.ts.map +1 -1
  70. package/build-types/entity-types/plugin.d.ts.map +1 -1
  71. package/build-types/entity-types/post-revision.d.ts.map +1 -1
  72. package/build-types/entity-types/post-status.d.ts.map +1 -1
  73. package/build-types/entity-types/post.d.ts.map +1 -1
  74. package/build-types/entity-types/settings.d.ts.map +1 -1
  75. package/build-types/entity-types/sidebar.d.ts.map +1 -1
  76. package/build-types/entity-types/taxonomy.d.ts.map +1 -1
  77. package/build-types/entity-types/term.d.ts.map +1 -1
  78. package/build-types/entity-types/theme.d.ts.map +1 -1
  79. package/build-types/entity-types/type.d.ts.map +1 -1
  80. package/build-types/entity-types/user.d.ts.map +1 -1
  81. package/build-types/entity-types/widget-type.d.ts.map +1 -1
  82. package/build-types/entity-types/widget.d.ts.map +1 -1
  83. package/build-types/entity-types/wp-template-part.d.ts.map +1 -1
  84. package/build-types/entity-types/wp-template.d.ts.map +1 -1
  85. package/build-types/fetch/__experimental-fetch-url-data.d.ts +2 -5
  86. package/build-types/fetch/__experimental-fetch-url-data.d.ts.map +1 -1
  87. package/build-types/fetch/index.d.ts +3 -3
  88. package/build-types/fetch/index.d.ts.map +1 -1
  89. package/build-types/footnotes/get-footnotes-order.d.ts.map +1 -1
  90. package/build-types/footnotes/get-rich-text-values-cached.d.ts.map +1 -1
  91. package/build-types/footnotes/index.d.ts +1 -1
  92. package/build-types/footnotes/index.d.ts.map +1 -1
  93. package/build-types/hooks/use-entity-block-editor.d.ts +1 -1
  94. package/build-types/hooks/use-entity-block-editor.d.ts.map +1 -1
  95. package/build-types/hooks/use-entity-id.d.ts.map +1 -1
  96. package/build-types/hooks/use-entity-prop.d.ts.map +1 -1
  97. package/build-types/hooks/use-resource-permissions.d.ts.map +1 -1
  98. package/build-types/index.d.ts +155 -153
  99. package/build-types/index.d.ts.map +1 -1
  100. package/build-types/locks/actions.d.ts +1 -1
  101. package/build-types/locks/actions.d.ts.map +1 -1
  102. package/build-types/locks/engine.d.ts +1 -1
  103. package/build-types/locks/engine.d.ts.map +1 -1
  104. package/build-types/locks/reducer.d.ts.map +1 -1
  105. package/build-types/locks/selectors.d.ts +2 -2
  106. package/build-types/locks/selectors.d.ts.map +1 -1
  107. package/build-types/locks/utils.d.ts +5 -5
  108. package/build-types/locks/utils.d.ts.map +1 -1
  109. package/build-types/name.d.ts +1 -1
  110. package/build-types/name.d.ts.map +1 -1
  111. package/build-types/private-actions.d.ts +45 -29
  112. package/build-types/private-actions.d.ts.map +1 -1
  113. package/build-types/private-apis.d.ts +1 -1
  114. package/build-types/private-apis.d.ts.map +1 -1
  115. package/build-types/queried-data/actions.d.ts +3 -3
  116. package/build-types/queried-data/actions.d.ts.map +1 -1
  117. package/build-types/queried-data/get-query-parts.d.ts +10 -34
  118. package/build-types/queried-data/get-query-parts.d.ts.map +1 -1
  119. package/build-types/queried-data/index.d.ts +3 -3
  120. package/build-types/queried-data/index.d.ts.map +1 -1
  121. package/build-types/queried-data/reducer.d.ts +7 -23
  122. package/build-types/queried-data/reducer.d.ts.map +1 -1
  123. package/build-types/queried-data/selectors.d.ts +3 -3
  124. package/build-types/queried-data/selectors.d.ts.map +1 -1
  125. package/build-types/reducer.d.ts +40 -32
  126. package/build-types/reducer.d.ts.map +1 -1
  127. package/build-types/resolvers.d.ts +130 -47
  128. package/build-types/resolvers.d.ts.map +1 -1
  129. package/build-types/selectors.d.ts +1 -1
  130. package/build-types/selectors.d.ts.map +1 -1
  131. package/build-types/types.d.ts +61 -6
  132. package/build-types/types.d.ts.map +1 -1
  133. package/build-types/utils/block-selection-history.d.ts.map +1 -1
  134. package/build-types/utils/conservative-map-item.d.ts.map +1 -1
  135. package/build-types/utils/crdt-blocks.d.ts +19 -9
  136. package/build-types/utils/crdt-blocks.d.ts.map +1 -1
  137. package/build-types/utils/crdt-selection.d.ts.map +1 -1
  138. package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
  139. package/build-types/utils/crdt-utils.d.ts +35 -2
  140. package/build-types/utils/crdt-utils.d.ts.map +1 -1
  141. package/build-types/utils/crdt.d.ts.map +1 -1
  142. package/build-types/utils/forward-resolver.d.ts +2 -2
  143. package/build-types/utils/forward-resolver.d.ts.map +1 -1
  144. package/build-types/utils/get-nested-value.d.ts.map +1 -1
  145. package/build-types/utils/get-normalized-comma-separable.d.ts +1 -1
  146. package/build-types/utils/get-normalized-comma-separable.d.ts.map +1 -1
  147. package/build-types/utils/if-matching-action.d.ts +3 -3
  148. package/build-types/utils/if-matching-action.d.ts.map +1 -1
  149. package/build-types/utils/index.d.ts +12 -12
  150. package/build-types/utils/index.d.ts.map +1 -1
  151. package/build-types/utils/is-numeric-id.d.ts.map +1 -1
  152. package/build-types/utils/log-entity-deprecation.d.ts +1 -1
  153. package/build-types/utils/log-entity-deprecation.d.ts.map +1 -1
  154. package/build-types/utils/normalize-query-for-resolution.d.ts.map +1 -1
  155. package/build-types/utils/receive-intermediate-results.d.ts +1 -1
  156. package/build-types/utils/receive-intermediate-results.d.ts.map +1 -1
  157. package/build-types/utils/replace-action.d.ts +3 -3
  158. package/build-types/utils/replace-action.d.ts.map +1 -1
  159. package/build-types/utils/set-nested-value.d.ts.map +1 -1
  160. package/build-types/utils/user-permissions.d.ts +3 -3
  161. package/build-types/utils/user-permissions.d.ts.map +1 -1
  162. package/build-types/utils/with-weak-map-cache.d.ts +1 -1
  163. package/build-types/utils/with-weak-map-cache.d.ts.map +1 -1
  164. package/package.json +20 -20
  165. package/src/actions.js +7 -9
  166. package/src/awareness/post-editor-awareness.ts +5 -2
  167. package/src/resolvers.js +2 -1
  168. package/src/test/actions.js +58 -0
  169. package/src/test/resolvers.js +115 -2
  170. package/src/test/rtc-rich-text-offset-space.test.js +204 -0
  171. package/src/types.ts +63 -6
  172. package/src/utils/block-selection-history.ts +5 -1
  173. package/src/utils/crdt-blocks.ts +316 -116
  174. package/src/utils/crdt-selection.ts +2 -1
  175. package/src/utils/crdt-user-selections.ts +9 -2
  176. package/src/utils/crdt-utils.ts +53 -10
  177. package/src/utils/crdt.ts +30 -4
  178. package/src/utils/test/crdt-blocks.ts +74 -18
  179. package/src/utils/test/crdt-utils.ts +18 -2
  180. package/src/utils/test/rtc-rich-text-cursor-scope.test.js +267 -0
  181. package/src/utils/test/rtc-rich-text-offset-space.test.js +469 -0
@@ -0,0 +1,204 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { act, render, waitFor } from '@testing-library/react';
5
+ import {
6
+ afterEach,
7
+ beforeEach,
8
+ describe,
9
+ expect,
10
+ it,
11
+ jest,
12
+ } from '@jest/globals';
13
+
14
+ /**
15
+ * WordPress dependencies
16
+ */
17
+ import {
18
+ getBlockTypes,
19
+ registerBlockType,
20
+ unregisterBlockType,
21
+ } from '@wordpress/blocks';
22
+ import { RichText } from '@wordpress/block-editor';
23
+ import { createRegistry, RegistryProvider } from '@wordpress/data';
24
+ import { Y } from '@wordpress/sync';
25
+
26
+ /**
27
+ * Mock sync manager accessor.
28
+ */
29
+ jest.mock( '../sync', () => ( {
30
+ ...jest.requireActual( '../sync' ),
31
+ getSyncManager: jest.fn(),
32
+ LOCAL_EDITOR_ORIGIN: 'local-editor',
33
+ } ) );
34
+
35
+ /**
36
+ * Internal dependencies
37
+ */
38
+ import { store as coreDataStore } from '../index';
39
+ import { CRDT_RECORD_MAP_KEY, getSyncManager } from '../sync';
40
+ import useEntityBlockEditor from '../hooks/use-entity-block-editor';
41
+ import { applyPostChangesToCRDTDoc } from '../utils/crdt';
42
+ import { getRootMap } from '../utils/crdt-utils';
43
+
44
+ const mockGetSyncManager = jest.mocked( getSyncManager );
45
+
46
+ const postTypeConfig = {
47
+ kind: 'postType',
48
+ name: 'post',
49
+ baseURL: '/wp/v2/posts',
50
+ transientEdits: { blocks: true, selection: true },
51
+ mergedEdits: { meta: true },
52
+ rawAttributes: [ 'title', 'excerpt', 'content' ],
53
+ syncConfig: {},
54
+ };
55
+
56
+ const postTypeEntity = {
57
+ slug: 'post',
58
+ rest_base: 'posts',
59
+ labels: {
60
+ item_updated: 'Updated Post',
61
+ item_published: 'Post published',
62
+ item_reverted_to_draft: 'Post reverted to draft.',
63
+ },
64
+ };
65
+
66
+ const OLD_HTML = '<em>italic</em><em>italic</em>';
67
+ const NEW_HTML = '<em>italic</em>beta';
68
+ const TEXT_OFFSET = 10;
69
+ const SYNCED_PROPERTIES = new Set( [ 'blocks' ] );
70
+
71
+ function createRegistryWithStores() {
72
+ const registry = createRegistry();
73
+
74
+ registry.register( coreDataStore );
75
+ registry.dispatch( coreDataStore ).addEntities( [ postTypeConfig ] );
76
+ registry
77
+ .dispatch( coreDataStore )
78
+ .receiveEntityRecords( 'root', 'postType', [ postTypeEntity ] );
79
+ registry
80
+ .dispatch( coreDataStore )
81
+ .receiveEntityRecords( 'postType', 'post', [
82
+ {
83
+ id: 1,
84
+ type: 'post',
85
+ content: {
86
+ raw: `<!-- wp:paragraph --><p>${ OLD_HTML }</p><!-- /wp:paragraph -->`,
87
+ rendered: `<p>${ OLD_HTML }</p>`,
88
+ },
89
+ meta: {},
90
+ },
91
+ ] );
92
+
93
+ return registry;
94
+ }
95
+
96
+ function readFirstBlockContentFromDoc( doc ) {
97
+ const ymap = getRootMap( doc, CRDT_RECORD_MAP_KEY );
98
+ const yblocks = ymap.get( 'blocks' );
99
+ const attrs = yblocks.get( 0 ).get( 'attributes' );
100
+ return attrs.get( 'content' ).toString();
101
+ }
102
+
103
+ describe( 'useEntityBlockEditor RTC rich-text offset-space bug', () => {
104
+ let crdtDoc;
105
+
106
+ beforeEach( () => {
107
+ crdtDoc = new Y.Doc();
108
+
109
+ registerBlockType( 'core/paragraph', {
110
+ apiVersion: 3,
111
+ title: 'Paragraph',
112
+ category: 'text',
113
+ attributes: {
114
+ content: {
115
+ type: 'rich-text',
116
+ source: 'rich-text',
117
+ selector: 'p',
118
+ role: 'content',
119
+ },
120
+ },
121
+ edit: () => null,
122
+ save: ( { attributes } ) => (
123
+ <p>
124
+ <RichText.Content value={ attributes.content } />
125
+ </p>
126
+ ),
127
+ } );
128
+
129
+ mockGetSyncManager.mockReturnValue( {
130
+ update: jest.fn( ( _objectType, _objectId, changes ) => {
131
+ applyPostChangesToCRDTDoc(
132
+ crdtDoc,
133
+ changes,
134
+ SYNCED_PROPERTIES
135
+ );
136
+ } ),
137
+ } );
138
+ } );
139
+
140
+ afterEach( () => {
141
+ crdtDoc.destroy();
142
+ mockGetSyncManager.mockReset();
143
+ if (
144
+ getBlockTypes().some( ( block ) => block.name === 'core/paragraph' )
145
+ ) {
146
+ unregisterBlockType( 'core/paragraph' );
147
+ }
148
+ } );
149
+
150
+ it( 'preserves formatted paragraph content when onInput forwards blocks plus selection', async () => {
151
+ const registry = createRegistryWithStores();
152
+ let blocks;
153
+ let onInput;
154
+
155
+ const TestComponent = () => {
156
+ [ blocks, onInput ] = useEntityBlockEditor( 'postType', 'post', {
157
+ id: 1,
158
+ } );
159
+
160
+ return <div />;
161
+ };
162
+
163
+ render(
164
+ <RegistryProvider value={ registry }>
165
+ <TestComponent />
166
+ </RegistryProvider>
167
+ );
168
+
169
+ await waitFor( () => expect( blocks ).toHaveLength( 1 ) );
170
+
171
+ const selection = {
172
+ selectionStart: {
173
+ clientId: blocks[ 0 ].clientId,
174
+ attributeKey: 'content',
175
+ offset: TEXT_OFFSET,
176
+ },
177
+ selectionEnd: {
178
+ clientId: blocks[ 0 ].clientId,
179
+ attributeKey: 'content',
180
+ offset: TEXT_OFFSET,
181
+ },
182
+ };
183
+
184
+ act( () => {
185
+ onInput( blocks, { selection } );
186
+ } );
187
+
188
+ const nextBlocks = [
189
+ {
190
+ ...blocks[ 0 ],
191
+ attributes: {
192
+ ...blocks[ 0 ].attributes,
193
+ content: NEW_HTML,
194
+ },
195
+ },
196
+ ];
197
+
198
+ act( () => {
199
+ onInput( nextBlocks, { selection } );
200
+ } );
201
+
202
+ expect( readFirstBlockContentFromDoc( crdtDoc ) ).toBe( NEW_HTML );
203
+ } );
204
+ } );
package/src/types.ts CHANGED
@@ -32,18 +32,75 @@ export interface AnyFunction {
32
32
  export type AbsoluteBlockIndexPath = number[];
33
33
 
34
34
  /**
35
- * Avoid a circular dependency with @wordpress/editor
35
+ * Describes part of a visible selection in the editor. Namely, either a cursor
36
+ * position within a block or a block entirely contained within a selection.
36
37
  *
37
- * Additionaly, this type marks `attributeKey` and `offset` as possibly
38
- * `undefined`, which can happen in two known scenarios:
38
+ * This description covers multiple selection scenarios, each of which impacts
39
+ * how the `attributeKey` and `offset` are set. These two parameters indicate
40
+ * where in a RichText component the cursor sits and with which block attribute
41
+ * that RichText component is associated.
39
42
  *
40
- * 1. If a user has an entire block highlighted (e.g., a `core/image` block).
41
- * 2. If there's an intermediate selection state while inserting a block, those
42
- * properties will be temporarily`undefined`.
43
+ * - When a selection covers an entire block or covers multiple blocks, there
44
+ * is no associated RichText, so both of the parameters are unset.
45
+ *
46
+ * - When a selection starts, ends, or is a simple position within a block,
47
+ * both parameters will be set accordingly.
48
+ *
49
+ * - When a block is being inserted into a document, however, there are multiple
50
+ * stages determining which parameters are set.
51
+ *
52
+ * 1. A block is created in the data store, but has no appearance in the
53
+ * editor otherwise. Both parameters are unset.
54
+ *
55
+ * 2. The block has loaded into the editor and there is a RichText field,
56
+ * but editor focus hasn’t yet placed a browser selection inside it.
57
+ * Only the `attributeKey` is set.
58
+ *
59
+ * 3. The browser has focused into a RichText field and both parameters are set.
60
+ *
61
+ * Selections are thus dynamic because block creation itself loads through multiple
62
+ * intermediate stages before someone is able to highlight, type, or modify text.
63
+ *
64
+ * This type is duplicated to avoid creating circular dependencies.
65
+ * @see {import("@wordpress/block-editor/src/store/actions").WPBlockSelection}
66
+ * @see {import("@wordpress/block-editor/src/store/selectors").WPBlockSelection}
67
+ * @see {import("@wordpress/editor/src/store/selectors").WPBlockSelection}
68
+ *
69
+ * @todo Move this into a canonical types file which can be imported in separate
70
+ * packages without causing circular dependencies.
43
71
  */
44
72
  export interface WPBlockSelection {
73
+ /**
74
+ * The selection cursor (start or end) is found within this block.
75
+ * or this entire block is contained within a multi-block selection.
76
+ */
45
77
  clientId: string;
78
+
79
+ /**
80
+ * When a selection cursor appears within a RichText component which
81
+ * maps back to a block’s attribute, e.g. a paragraph block’s `content`
82
+ * attribute, this will hold the attribute key for that associated
83
+ * block attribute.
84
+ *
85
+ * An attribute key is mostly the same as an attribute name, but in some
86
+ * circumstances, such as in a multiline attribute, there can be multiple
87
+ * RichText instances associated with a given attribute. The key will
88
+ * usually be the attribute name verbatim, but in these cases, an index
89
+ * will be appended to differentiate the multiple RichText instances
90
+ * associated with the array-like attribute.
91
+ *
92
+ * Technically, the `attributeKey` is the value stored in the DOM node
93
+ * for a RichText instance in the `data-wp-block-attribute-key` attribute.
94
+ * This “key” links the actual instance with the block attribute, provided
95
+ * by the `identifier` React prop when creating a `<RichText>` element.
96
+ */
46
97
  attributeKey?: string;
98
+
99
+ /**
100
+ * When a selection cursor appears within a block, it can be found this
101
+ * many Unicode code points into the RichText component’s decoded text
102
+ * which is associated with the given attribute key.
103
+ */
47
104
  offset?: number;
48
105
  }
49
106
 
@@ -10,6 +10,7 @@ import { Y } from '@wordpress/sync';
10
10
  * Internal dependencies
11
11
  */
12
12
  import {
13
+ asRichTextOffset,
13
14
  findBlockByClientIdInDoc,
14
15
  richTextOffsetToHtmlIndex,
15
16
  } from './crdt-utils';
@@ -166,7 +167,10 @@ function convertWPBlockSelectionToSelection(
166
167
  const offset = selection.offset ?? 0;
167
168
  const relativePosition = Y.createRelativePositionFromTypeIndex(
168
169
  changedYText,
169
- richTextOffsetToHtmlIndex( changedYText.toString(), offset )
170
+ richTextOffsetToHtmlIndex(
171
+ changedYText.toString(),
172
+ asRichTextOffset( offset )
173
+ )
170
174
  );
171
175
 
172
176
  return {