@wordpress/editor 14.35.2-next.dc3f6d3c1.0 → 14.36.1-next.6deb34194.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 (144) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +2 -2
  3. package/build/bindings/post-data.js +15 -15
  4. package/build/bindings/post-data.js.map +2 -2
  5. package/build/bindings/term-data.js +97 -95
  6. package/build/bindings/term-data.js.map +2 -2
  7. package/build/components/autocompleters/user.js.map +2 -2
  8. package/build/components/collab-sidebar/comments.js.map +2 -2
  9. package/build/components/collab-sidebar/hooks.js +7 -11
  10. package/build/components/collab-sidebar/hooks.js.map +2 -2
  11. package/build/components/document-bar/index.js +19 -8
  12. package/build/components/document-bar/index.js.map +3 -3
  13. package/build/components/document-bar/useEditedSectionDetails.js +95 -0
  14. package/build/components/document-bar/useEditedSectionDetails.js.map +7 -0
  15. package/build/components/global-styles/index.js +1 -1
  16. package/build/components/global-styles/index.js.map +1 -1
  17. package/build/components/page-attributes/parent.js +1 -0
  18. package/build/components/page-attributes/parent.js.map +2 -2
  19. package/build/components/post-card-panel/index.js +1 -1
  20. package/build/components/post-card-panel/index.js.map +2 -2
  21. package/build/components/post-schedule/panel.js +1 -7
  22. package/build/components/post-schedule/panel.js.map +2 -2
  23. package/build/components/post-status/index.js +1 -7
  24. package/build/components/post-status/index.js.map +2 -2
  25. package/build/components/post-template/block-theme.js +1 -1
  26. package/build/components/post-template/block-theme.js.map +2 -2
  27. package/build/components/post-template/hooks.js +1 -1
  28. package/build/components/post-template/hooks.js.map +2 -2
  29. package/build/components/provider/index.js +3 -1
  30. package/build/components/provider/index.js.map +2 -2
  31. package/build/components/visual-editor/index.js +1 -7
  32. package/build/components/visual-editor/index.js.map +2 -2
  33. package/build/components/visual-editor/use-edit-content-only-section-exit.js +5 -6
  34. package/build/components/visual-editor/use-edit-content-only-section-exit.js.map +2 -2
  35. package/build/dataviews/store/private-actions.js +14 -2
  36. package/build/dataviews/store/private-actions.js.map +2 -2
  37. package/build/hooks/template-part-navigation-edit-button.js +8 -11
  38. package/build/hooks/template-part-navigation-edit-button.js.map +3 -3
  39. package/build/store/actions.js +1 -1
  40. package/build/store/actions.js.map +2 -2
  41. package/build/store/constants.js +8 -0
  42. package/build/store/constants.js.map +2 -2
  43. package/build/utils/get-template-part-icon.js +9 -4
  44. package/build/utils/get-template-part-icon.js.map +3 -3
  45. package/build-module/bindings/post-data.js +15 -15
  46. package/build-module/bindings/post-data.js.map +2 -2
  47. package/build-module/bindings/term-data.js +93 -95
  48. package/build-module/bindings/term-data.js.map +2 -2
  49. package/build-module/components/autocompleters/user.js.map +2 -2
  50. package/build-module/components/collab-sidebar/comments.js.map +2 -2
  51. package/build-module/components/collab-sidebar/hooks.js +7 -12
  52. package/build-module/components/collab-sidebar/hooks.js.map +2 -2
  53. package/build-module/components/document-bar/index.js +20 -9
  54. package/build-module/components/document-bar/index.js.map +2 -2
  55. package/build-module/components/document-bar/useEditedSectionDetails.js +74 -0
  56. package/build-module/components/document-bar/useEditedSectionDetails.js.map +7 -0
  57. package/build-module/components/global-styles/index.js +1 -1
  58. package/build-module/components/global-styles/index.js.map +1 -1
  59. package/build-module/components/page-attributes/parent.js +1 -0
  60. package/build-module/components/page-attributes/parent.js.map +2 -2
  61. package/build-module/components/post-card-panel/index.js +1 -1
  62. package/build-module/components/post-card-panel/index.js.map +2 -2
  63. package/build-module/components/post-schedule/panel.js +1 -12
  64. package/build-module/components/post-schedule/panel.js.map +2 -2
  65. package/build-module/components/post-status/index.js +1 -12
  66. package/build-module/components/post-status/index.js.map +2 -2
  67. package/build-module/components/post-template/block-theme.js +1 -1
  68. package/build-module/components/post-template/block-theme.js.map +2 -2
  69. package/build-module/components/post-template/hooks.js +1 -1
  70. package/build-module/components/post-template/hooks.js.map +2 -2
  71. package/build-module/components/provider/index.js +3 -1
  72. package/build-module/components/provider/index.js.map +2 -2
  73. package/build-module/components/visual-editor/index.js +2 -7
  74. package/build-module/components/visual-editor/index.js.map +2 -2
  75. package/build-module/components/visual-editor/use-edit-content-only-section-exit.js +5 -6
  76. package/build-module/components/visual-editor/use-edit-content-only-section-exit.js.map +2 -2
  77. package/build-module/dataviews/store/private-actions.js +16 -3
  78. package/build-module/dataviews/store/private-actions.js.map +2 -2
  79. package/build-module/hooks/template-part-navigation-edit-button.js +10 -17
  80. package/build-module/hooks/template-part-navigation-edit-button.js.map +2 -2
  81. package/build-module/store/actions.js +1 -1
  82. package/build-module/store/actions.js.map +2 -2
  83. package/build-module/store/constants.js +7 -0
  84. package/build-module/store/constants.js.map +2 -2
  85. package/build-module/utils/get-template-part-icon.js +10 -4
  86. package/build-module/utils/get-template-part-icon.js.map +2 -2
  87. package/build-style/style-rtl.css +296 -142
  88. package/build-style/style.css +296 -142
  89. package/build-types/bindings/post-data.d.ts +2 -1
  90. package/build-types/bindings/term-data.d.ts +86 -8
  91. package/build-types/bindings/term-data.d.ts.map +1 -1
  92. package/build-types/components/autocompleters/user.d.ts +8 -2
  93. package/build-types/components/autocompleters/user.d.ts.map +1 -1
  94. package/build-types/components/collab-sidebar/comments.d.ts.map +1 -1
  95. package/build-types/components/collab-sidebar/hooks.d.ts +0 -1
  96. package/build-types/components/collab-sidebar/hooks.d.ts.map +1 -1
  97. package/build-types/components/document-bar/index.d.ts.map +1 -1
  98. package/build-types/components/document-bar/useEditedSectionDetails.d.ts +8 -0
  99. package/build-types/components/document-bar/useEditedSectionDetails.d.ts.map +1 -0
  100. package/build-types/components/page-attributes/parent.d.ts.map +1 -1
  101. package/build-types/components/post-schedule/panel.d.ts.map +1 -1
  102. package/build-types/components/post-status/index.d.ts.map +1 -1
  103. package/build-types/components/post-template/block-theme.d.ts.map +1 -1
  104. package/build-types/components/post-template/hooks.d.ts +1 -1
  105. package/build-types/components/post-template/hooks.d.ts.map +1 -1
  106. package/build-types/components/provider/index.d.ts.map +1 -1
  107. package/build-types/components/visual-editor/index.d.ts.map +1 -1
  108. package/build-types/components/visual-editor/use-edit-content-only-section-exit.d.ts.map +1 -1
  109. package/build-types/dataviews/store/private-actions.d.ts +5 -0
  110. package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
  111. package/build-types/store/actions.d.ts.map +1 -1
  112. package/build-types/store/constants.d.ts +1 -0
  113. package/build-types/store/constants.d.ts.map +1 -1
  114. package/build-types/store/index.d.ts +1 -1
  115. package/build-types/store/index.d.ts.map +1 -1
  116. package/build-types/utils/get-template-part-icon.d.ts +3 -3
  117. package/build-types/utils/get-template-part-icon.d.ts.map +1 -1
  118. package/package.json +40 -40
  119. package/src/bindings/post-data.js +21 -20
  120. package/src/bindings/term-data.js +124 -151
  121. package/src/bindings/test/post-data.js +31 -20
  122. package/src/bindings/test/term-data.js +406 -0
  123. package/src/components/autocompleters/user.js +6 -0
  124. package/src/components/collab-sidebar/comments.js +1 -0
  125. package/src/components/collab-sidebar/hooks.js +7 -13
  126. package/src/components/document-bar/index.js +41 -9
  127. package/src/components/document-bar/useEditedSectionDetails.js +103 -0
  128. package/src/components/global-styles/index.js +1 -1
  129. package/src/components/global-styles-sidebar/style.scss +2 -2
  130. package/src/components/page-attributes/parent.js +2 -1
  131. package/src/components/post-card-panel/index.js +1 -1
  132. package/src/components/post-schedule/panel.js +1 -13
  133. package/src/components/post-status/index.js +1 -13
  134. package/src/components/post-template/block-theme.js +4 -1
  135. package/src/components/post-template/hooks.js +1 -1
  136. package/src/components/provider/index.js +7 -2
  137. package/src/components/visual-editor/index.js +1 -6
  138. package/src/components/visual-editor/use-edit-content-only-section-exit.js +9 -10
  139. package/src/dataviews/store/private-actions.ts +33 -1
  140. package/src/hooks/template-part-navigation-edit-button.js +4 -15
  141. package/src/store/actions.js +2 -0
  142. package/src/store/constants.ts +6 -0
  143. package/src/utils/get-template-part-icon.js +19 -6
  144. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,406 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { store as blockEditorStore } from '@wordpress/block-editor';
5
+ import { store as coreDataStore } from '@wordpress/core-data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { default as termDataBindings, termDataFields } from '../term-data';
11
+
12
+ describe( 'term-data bindings', () => {
13
+ const bindings = {
14
+ id: {
15
+ source: 'core/term-data',
16
+ args: { field: 'id' },
17
+ },
18
+ name: {
19
+ source: 'core/term-data',
20
+ args: { field: 'name' },
21
+ },
22
+ slug: {
23
+ source: 'core/term-data',
24
+ args: { field: 'slug' },
25
+ },
26
+ link: {
27
+ source: 'core/term-data',
28
+ args: { field: 'link' },
29
+ },
30
+ description: {
31
+ source: 'core/term-data',
32
+ args: { field: 'description' },
33
+ },
34
+ parent: {
35
+ source: 'core/term-data',
36
+ args: { field: 'parent' },
37
+ },
38
+ count: {
39
+ source: 'core/term-data',
40
+ args: { field: 'count' },
41
+ },
42
+ content: {
43
+ source: 'core/term-data',
44
+ args: { field: 'unknown' },
45
+ },
46
+ };
47
+
48
+ const getEntityRecordMock = ( kind, taxonomy, termId ) => {
49
+ if (
50
+ kind === 'taxonomy' &&
51
+ taxonomy === 'category' &&
52
+ termId === 123
53
+ ) {
54
+ return {
55
+ id: 123,
56
+ name: 'Technology',
57
+ slug: 'technology',
58
+ link: 'https://example.com/category/technology',
59
+ parent: 0,
60
+ count: 42,
61
+ };
62
+ }
63
+ return undefined;
64
+ };
65
+
66
+ describe( 'getValues', () => {
67
+ describe( 'for regular blocks using block context', () => {
68
+ describe( 'when termId and taxonomy are provided in context', () => {
69
+ const select = ( store ) => {
70
+ if ( store === blockEditorStore ) {
71
+ return {
72
+ getBlockName: ( clientId ) =>
73
+ clientId === '123abc456'
74
+ ? 'core/paragraph'
75
+ : undefined,
76
+ getBlockAttributes: () => ( {} ),
77
+ };
78
+ }
79
+ if ( store === coreDataStore ) {
80
+ return {
81
+ getEntityRecord: getEntityRecordMock,
82
+ };
83
+ }
84
+ };
85
+
86
+ it( 'should return entity field values when they exist, fall back to field label, and to field name for unknown fields', () => {
87
+ const values = termDataBindings.getValues( {
88
+ select,
89
+ context: {
90
+ taxonomy: 'category',
91
+ termId: 123,
92
+ },
93
+ bindings,
94
+ clientId: '123abc456',
95
+ } );
96
+
97
+ expect( values ).toStrictEqual( {
98
+ id: 123,
99
+ name: 'Technology',
100
+ slug: 'technology',
101
+ link: 'https://example.com/category/technology',
102
+ description: 'Description',
103
+ parent: 0,
104
+ count: '(42)',
105
+ content: 'unknown',
106
+ } );
107
+ } );
108
+
109
+ it( 'should fall back to field label when entity does not exist, and to field name for unknown fields', () => {
110
+ const values = termDataBindings.getValues( {
111
+ select,
112
+ context: {
113
+ taxonomy: 'category',
114
+ termId: 456,
115
+ },
116
+ bindings,
117
+ clientId: '123abc456',
118
+ } );
119
+
120
+ expect( values ).toStrictEqual( {
121
+ id: 'Term ID',
122
+ name: 'Name',
123
+ slug: 'Slug',
124
+ link: 'Link',
125
+ description: 'Description',
126
+ parent: 'Parent ID',
127
+ count: 'Count',
128
+ content: 'unknown',
129
+ } );
130
+ } );
131
+ } );
132
+
133
+ describe( 'when termData is provided in context', () => {
134
+ const select = ( store ) => {
135
+ if ( store === blockEditorStore ) {
136
+ return {
137
+ getBlockName: () => 'core/paragraph',
138
+ getBlockAttributes: () => ( {} ),
139
+ };
140
+ }
141
+ if ( store === coreDataStore ) {
142
+ return {
143
+ getEntityRecord: () => null,
144
+ };
145
+ }
146
+ };
147
+
148
+ const termData = {
149
+ id: 456,
150
+ name: 'Design',
151
+ link: 'https://example.com/category/design',
152
+ description: 'Design resources',
153
+ parent: 0,
154
+ count: 15,
155
+ };
156
+
157
+ it( 'should use termData from context when entity record is not available', () => {
158
+ const values = termDataBindings.getValues( {
159
+ select,
160
+ context: {
161
+ taxonomy: 'category',
162
+ termId: 456,
163
+ termData,
164
+ },
165
+ bindings,
166
+ clientId: '123abc456',
167
+ } );
168
+
169
+ expect( values ).toStrictEqual( {
170
+ id: 456,
171
+ name: 'Design',
172
+ slug: 'Slug',
173
+ link: 'https://example.com/category/design',
174
+ description: 'Design resources',
175
+ parent: 0,
176
+ count: '(15)',
177
+ content: 'unknown',
178
+ } );
179
+ } );
180
+
181
+ it( 'should use termData when taxonomy and termId are not provided', () => {
182
+ const values = termDataBindings.getValues( {
183
+ select,
184
+ context: {
185
+ termData,
186
+ },
187
+ bindings,
188
+ clientId: '123abc456',
189
+ } );
190
+
191
+ expect( values ).toStrictEqual( {
192
+ id: 456,
193
+ name: 'Design',
194
+ slug: 'Slug',
195
+ link: 'https://example.com/category/design',
196
+ description: 'Design resources',
197
+ parent: 0,
198
+ count: '(15)',
199
+ content: 'unknown',
200
+ } );
201
+ } );
202
+ } );
203
+ } );
204
+
205
+ describe( 'for navigation blocks using block attributes', () => {
206
+ it( 'should use block attributes instead of context', () => {
207
+ const select = ( store ) => {
208
+ if ( store === blockEditorStore ) {
209
+ return {
210
+ getBlockName: () => 'core/navigation-link',
211
+ getBlockAttributes: () => ( {
212
+ id: 789,
213
+ type: 'category',
214
+ } ),
215
+ };
216
+ }
217
+ if ( store === coreDataStore ) {
218
+ return {
219
+ getEntityRecord: ( kind, taxonomy, termId ) => {
220
+ if (
221
+ kind === 'taxonomy' &&
222
+ taxonomy === 'category' &&
223
+ termId === 789
224
+ ) {
225
+ return {
226
+ id: 789,
227
+ name: 'Programming',
228
+ slug: 'programming',
229
+ link: 'https://example.com/category/programming',
230
+ description: 'Programming resources',
231
+ parent: 0,
232
+ count: 10,
233
+ };
234
+ }
235
+ return null;
236
+ },
237
+ };
238
+ }
239
+ };
240
+
241
+ const values = termDataBindings.getValues( {
242
+ select,
243
+ context: {},
244
+ bindings,
245
+ clientId: '123abc456',
246
+ } );
247
+
248
+ expect( values ).toStrictEqual( {
249
+ id: 789,
250
+ name: 'Programming',
251
+ slug: 'programming',
252
+ link: 'https://example.com/category/programming',
253
+ description: 'Programming resources',
254
+ parent: 0,
255
+ count: '(10)',
256
+ content: 'unknown',
257
+ } );
258
+ } );
259
+
260
+ it( 'should convert "tag" type to "post_tag" taxonomy', () => {
261
+ const select = ( store ) => {
262
+ if ( store === blockEditorStore ) {
263
+ return {
264
+ getBlockName: () => 'core/navigation-link',
265
+ getBlockAttributes: () => ( {
266
+ id: 321,
267
+ type: 'tag',
268
+ } ),
269
+ };
270
+ }
271
+ if ( store === coreDataStore ) {
272
+ return {
273
+ getEntityRecord: ( kind, taxonomy, termId ) => {
274
+ if (
275
+ kind === 'taxonomy' &&
276
+ taxonomy === 'post_tag' &&
277
+ termId === 321
278
+ ) {
279
+ return {
280
+ name: 'JavaScript',
281
+ };
282
+ }
283
+ return null;
284
+ },
285
+ };
286
+ }
287
+ };
288
+
289
+ const values = termDataBindings.getValues( {
290
+ select,
291
+ context: {},
292
+ bindings: {
293
+ content: {
294
+ source: 'core/term-data',
295
+ args: { field: 'name' },
296
+ },
297
+ },
298
+ clientId: '123abc456',
299
+ } );
300
+
301
+ expect( values.content ).toBe( 'JavaScript' );
302
+ } );
303
+ } );
304
+ } );
305
+
306
+ describe( 'getFieldsList', () => {
307
+ describe( 'when a Navigation block is selected', () => {
308
+ it( 'should return the list of available term data fields if id and type attributes are present', () => {
309
+ const select = ( store ) => {
310
+ if ( store === blockEditorStore ) {
311
+ return {
312
+ getSelectedBlockClientId: () => '123abc456',
313
+ getBlockName: () => 'core/navigation-link',
314
+ getBlockAttributes: () => ( {
315
+ id: 123,
316
+ type: 'category',
317
+ } ),
318
+ };
319
+ }
320
+ if ( store === coreDataStore ) {
321
+ return {
322
+ getEntityRecord: getEntityRecordMock,
323
+ };
324
+ }
325
+ };
326
+ const fields = termDataBindings.getFieldsList( { select } );
327
+
328
+ expect( fields ).toEqual( termDataFields );
329
+ } );
330
+
331
+ it( 'should return an empty array if id or type attributes are missing', () => {
332
+ const select = ( store ) => {
333
+ if ( store === blockEditorStore ) {
334
+ return {
335
+ getSelectedBlockClientId: () => '123abc456',
336
+ getBlockName: () => 'core/navigation-link',
337
+ getBlockAttributes: () => ( { type: 'category' } ),
338
+ };
339
+ }
340
+ if ( store === coreDataStore ) {
341
+ return {
342
+ getEntityRecord: getEntityRecordMock,
343
+ };
344
+ }
345
+ };
346
+ const fields = termDataBindings.getFieldsList( { select } );
347
+
348
+ expect( fields ).toEqual( [] );
349
+ } );
350
+ } );
351
+
352
+ describe( 'when a non-Navigation block is selected', () => {
353
+ const select = ( store ) => {
354
+ if ( store === blockEditorStore ) {
355
+ return {
356
+ getSelectedBlockClientId: () => '123abc456',
357
+ getBlockName: () => 'core/paragraph',
358
+ getBlockAttributes: () => ( {} ),
359
+ };
360
+ }
361
+ if ( store === coreDataStore ) {
362
+ return {
363
+ getEntityRecord: getEntityRecordMock,
364
+ };
365
+ }
366
+ };
367
+
368
+ it( 'should return the list of available term data fields when taxonomy and termId are provided by context', () => {
369
+ const fields = termDataBindings.getFieldsList( {
370
+ select,
371
+ context: { taxonomy: 'category', termId: 123 },
372
+ } );
373
+
374
+ expect( fields ).toEqual( termDataFields );
375
+ } );
376
+
377
+ it( 'should return empty array when neither termId nor termData is provided from context', () => {
378
+ const fields = termDataBindings.getFieldsList( {
379
+ select,
380
+ context: { taxonomy: 'category' },
381
+ } );
382
+
383
+ expect( fields ).toEqual( [] );
384
+ } );
385
+
386
+ it( 'should return fields when using termData from context', () => {
387
+ const fields = termDataBindings.getFieldsList( {
388
+ select,
389
+ context: {
390
+ termData: {
391
+ id: 456,
392
+ name: 'Design',
393
+ slug: 'design',
394
+ link: 'https://example.com/category/design',
395
+ description: 'Design resources',
396
+ parent: 0,
397
+ count: 15,
398
+ },
399
+ },
400
+ } );
401
+
402
+ expect( fields ).toEqual( termDataFields );
403
+ } );
404
+ } );
405
+ } );
406
+ } );
@@ -5,6 +5,12 @@ import { useMemo } from '@wordpress/element';
5
5
  import { useSelect } from '@wordpress/data';
6
6
  import { store as coreStore } from '@wordpress/core-data';
7
7
 
8
+ /**
9
+ * Renders a user label for the autocompleter.
10
+ *
11
+ * @param {Object} user User object.
12
+ * @return {JSX.Element} User label component.
13
+ */
8
14
  export function getUserLabel( user ) {
9
15
  const avatar =
10
16
  user.avatar_urls && user.avatar_urls[ 24 ] ? (
@@ -69,6 +69,7 @@ export function Comments( {
69
69
  const { selectBlock, toggleBlockSpotlight } = unlock(
70
70
  useDispatch( blockEditorStore )
71
71
  );
72
+
72
73
  const { blockCommentId, selectedBlockClientId, orderedBlockIds } =
73
74
  useSelect( ( select ) => {
74
75
  const {
@@ -14,7 +14,6 @@ import { __ } from '@wordpress/i18n';
14
14
  import {
15
15
  useEffect,
16
16
  useMemo,
17
- useRef,
18
17
  useCallback,
19
18
  useReducer,
20
19
  } from '@wordpress/element';
@@ -36,9 +35,7 @@ import { collabSidebarName } from './constants';
36
35
  import { unlock } from '../../lock-unlock';
37
36
  import { noop } from './utils';
38
37
 
39
- const { useBlockElementRef, cleanEmptyObject } = unlock(
40
- blockEditorPrivateApis
41
- );
38
+ const { useBlockElement, cleanEmptyObject } = unlock( blockEditorPrivateApis );
42
39
 
43
40
  export function useBlockComments( postId ) {
44
41
  const [ commentLastUpdated, reflowComments ] = useReducer(
@@ -369,9 +366,7 @@ export function useFloatingThread( {
369
366
  setBlockRef,
370
367
  commentLastUpdated,
371
368
  } ) {
372
- const blockRef = useRef();
373
- useBlockElementRef( thread.blockClientId, blockRef );
374
-
369
+ const blockElement = useBlockElement( thread.blockClientId );
375
370
  const updateHeight = useCallback(
376
371
  ( id, newHeight ) => {
377
372
  setHeights( ( prev ) => {
@@ -397,17 +392,17 @@ export function useFloatingThread( {
397
392
 
398
393
  // Store the block reference for each thread.
399
394
  useEffect( () => {
400
- if ( blockRef.current ) {
401
- refs.setReference( blockRef.current );
395
+ if ( blockElement ) {
396
+ refs.setReference( blockElement );
402
397
  }
403
- }, [ blockRef, refs, commentLastUpdated ] );
398
+ }, [ blockElement, refs, commentLastUpdated ] );
404
399
 
405
400
  // Track thread heights.
406
401
  useEffect( () => {
407
402
  if ( refs.floating?.current ) {
408
- setBlockRef( thread.id, blockRef.current );
403
+ setBlockRef( thread.id, blockElement );
409
404
  }
410
- }, [ thread.id, refs.floating, setBlockRef ] );
405
+ }, [ blockElement, thread.id, refs.floating, setBlockRef ] );
411
406
 
412
407
  // When the selected thread changes, update heights, triggering offset recalculation.
413
408
  useEffect( () => {
@@ -424,7 +419,6 @@ export function useFloatingThread( {
424
419
  ] );
425
420
 
426
421
  return {
427
- blockRef,
428
422
  y,
429
423
  refs,
430
424
  };
@@ -14,7 +14,7 @@ import {
14
14
  __unstableMotion as motion,
15
15
  __unstableAnimatePresence as AnimatePresence,
16
16
  } from '@wordpress/components';
17
- import { BlockIcon } from '@wordpress/block-editor';
17
+ import { BlockIcon, store as blockEditorStore } from '@wordpress/block-editor';
18
18
  import { chevronLeftSmall, chevronRightSmall, layout } from '@wordpress/icons';
19
19
  import { displayShortcut } from '@wordpress/keycodes';
20
20
  import { store as coreStore } from '@wordpress/core-data';
@@ -33,6 +33,7 @@ import usePageTypeBadge from '../../utils/pageTypeBadge';
33
33
  import { getTemplateInfo } from '../../utils/get-template-info';
34
34
  import { getStylesCanvasTitle } from '../styles-canvas';
35
35
  import { unlock } from '../../lock-unlock';
36
+ import useEditedSectionDetails from './useEditedSectionDetails';
36
37
 
37
38
  /** @typedef {import("@wordpress/components").IconType} IconType */
38
39
 
@@ -56,6 +57,14 @@ const MotionButton = motion.create( Button );
56
57
  * @return {React.ReactNode} The rendered DocumentBar component.
57
58
  */
58
59
  export default function DocumentBar( props ) {
60
+ // Get action to lock the pattern design
61
+ const { stopEditingContentOnlySection } = unlock(
62
+ useDispatch( blockEditorStore )
63
+ );
64
+
65
+ // Get details about the currently edited content-only section
66
+ const unlockedPatternInfo = useEditedSectionDetails();
67
+
59
68
  const {
60
69
  postId,
61
70
  postType,
@@ -133,11 +142,28 @@ export default function DocumentBar( props ) {
133
142
  const isReducedMotion = useReducedMotion();
134
143
 
135
144
  const isTemplate = TEMPLATE_POST_TYPES.includes( postType );
136
- const hasBackButton = !! onNavigateToPreviousEntityRecord;
145
+ const hasBackButton =
146
+ !! onNavigateToPreviousEntityRecord || !! unlockedPatternInfo;
137
147
  const entityTitle = isTemplate ? templateTitle : documentTitle;
138
- const title = props.title || stylesCanvasTitle || entityTitle;
148
+
149
+ // Use pattern info if a pattern block is unlocked, otherwise use document/entity info
150
+ const title =
151
+ unlockedPatternInfo?.patternTitle ||
152
+ props.title ||
153
+ stylesCanvasTitle ||
154
+ entityTitle;
139
155
  const icon = props.icon;
140
156
 
157
+ // Determine the back button action
158
+ const handleBackClick = ( event ) => {
159
+ event.stopPropagation();
160
+ if ( unlockedPatternInfo ) {
161
+ stopEditingContentOnlySection();
162
+ } else if ( onNavigateToPreviousEntityRecord ) {
163
+ onNavigateToPreviousEntityRecord();
164
+ }
165
+ };
166
+
141
167
  const pageTypeBadge = usePageTypeBadge( postId );
142
168
 
143
169
  const mountedRef = useRef( false );
@@ -156,10 +182,7 @@ export default function DocumentBar( props ) {
156
182
  <MotionButton
157
183
  className="editor-document-bar__back"
158
184
  icon={ isRTL() ? chevronRightSmall : chevronLeftSmall }
159
- onClick={ ( event ) => {
160
- event.stopPropagation();
161
- onNavigateToPreviousEntityRecord();
162
- } }
185
+ onClick={ handleBackClick }
163
186
  size="compact"
164
187
  initial={
165
188
  mountedRef.current
@@ -219,12 +242,21 @@ export default function DocumentBar( props ) {
219
242
  ? stripHTML( title )
220
243
  : __( 'No title' ) }
221
244
  </span>
222
- { pageTypeBadge && (
245
+ { unlockedPatternInfo && (
246
+ <span className="editor-document-bar__post-type-label">
247
+ { unlockedPatternInfo.type ===
248
+ 'template-part'
249
+ ? `· ${ __( 'Template Part' ) }`
250
+ : `· ${ __( 'Pattern' ) }` }
251
+ </span>
252
+ ) }
253
+ { ! unlockedPatternInfo && pageTypeBadge && (
223
254
  <span className="editor-document-bar__post-type-label">
224
255
  { `· ${ pageTypeBadge }` }
225
256
  </span>
226
257
  ) }
227
- { postTypeLabel &&
258
+ { ! unlockedPatternInfo &&
259
+ postTypeLabel &&
228
260
  ! props.title &&
229
261
  ! pageTypeBadge && (
230
262
  <span className="editor-document-bar__post-type-label">
@@ -0,0 +1,103 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+ import { decodeEntities } from '@wordpress/html-entities';
6
+ import { store as blockEditorStore } from '@wordpress/block-editor';
7
+ import { store as coreStore } from '@wordpress/core-data';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { unlock } from '../../lock-unlock';
13
+
14
+ /**
15
+ * Hook to get details about the currently edited content-only section.
16
+ * Only returns information when the content only pattern insertion experiment is enabled.
17
+ *
18
+ * @return {Object|null} Object with patternName, patternTitle, and type, or null if no section is being edited or experiment is disabled.
19
+ */
20
+ export default function useEditedSectionDetails() {
21
+ return useSelect( ( select ) => {
22
+ // Only run when the content only pattern insertion experiment is enabled
23
+ if ( ! window?.__experimentalContentOnlyPatternInsertion ) {
24
+ return null;
25
+ }
26
+
27
+ const {
28
+ getBlockAttributes,
29
+ getBlockName,
30
+ __experimentalGetParsedPattern,
31
+ } = select( blockEditorStore );
32
+ const { getEditedEntityRecord, getCurrentTheme } = select( coreStore );
33
+ const { getEditedContentOnlySection } = unlock(
34
+ select( blockEditorStore )
35
+ );
36
+
37
+ // Get the clientId of the unlocked pattern/section
38
+ const editedSectionId = getEditedContentOnlySection();
39
+ if ( ! editedSectionId ) {
40
+ return null;
41
+ }
42
+
43
+ const attributes = getBlockAttributes( editedSectionId );
44
+
45
+ // Handle unsynced patterns (contentOnly patterns with patternName)
46
+ const patternName = attributes?.metadata?.patternName;
47
+ if ( patternName ) {
48
+ // Get pattern details if available
49
+ const pattern =
50
+ typeof __experimentalGetParsedPattern === 'function'
51
+ ? __experimentalGetParsedPattern( patternName )
52
+ : null;
53
+
54
+ return {
55
+ patternName,
56
+ patternTitle: pattern?.title || attributes?.metadata?.name,
57
+ type: 'pattern',
58
+ };
59
+ }
60
+
61
+ const blockName = getBlockName( editedSectionId );
62
+
63
+ // Handle synced patterns (core/block)
64
+ if ( blockName === 'core/block' && !! attributes?.ref ) {
65
+ const entity = getEditedEntityRecord(
66
+ 'postType',
67
+ 'wp_block',
68
+ attributes.ref
69
+ );
70
+ if ( entity?.title ) {
71
+ return {
72
+ patternName: attributes.ref,
73
+ patternTitle: decodeEntities( entity.title ),
74
+ type: 'synced-pattern',
75
+ };
76
+ }
77
+ }
78
+
79
+ // Handle template parts (core/template-part)
80
+ if ( blockName === 'core/template-part' && !! attributes?.slug ) {
81
+ const theme = attributes.theme || getCurrentTheme()?.stylesheet;
82
+ const templatePartId = theme
83
+ ? `${ theme }//${ attributes.slug }`
84
+ : null;
85
+ if ( templatePartId ) {
86
+ const entity = getEditedEntityRecord(
87
+ 'postType',
88
+ 'wp_template_part',
89
+ templatePartId
90
+ );
91
+ if ( entity?.title ) {
92
+ return {
93
+ patternName: attributes.slug,
94
+ patternTitle: decodeEntities( entity.title ),
95
+ type: 'template-part',
96
+ };
97
+ }
98
+ }
99
+ }
100
+
101
+ return null;
102
+ }, [] );
103
+ }
@@ -33,7 +33,7 @@ function useServerData() {
33
33
 
34
34
  const canUserUploadMedia = canUser( 'create', {
35
35
  kind: 'postType',
36
- name: 'attachement',
36
+ name: 'attachment',
37
37
  } );
38
38
 
39
39
  return {
@@ -10,8 +10,8 @@
10
10
  flex: 1;
11
11
  }
12
12
 
13
- .components-navigator-screen {
14
- padding: 0;
13
+ .global-styles-ui-sidebar__navigator-screen.components-navigator-screen {
14
+ padding: $grid-unit-15;
15
15
  }
16
16
  }
17
17