@wordpress/block-library 8.7.0 → 8.8.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 (270) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/column/index.js +2 -1
  3. package/build/column/index.js.map +1 -1
  4. package/build/columns/edit.js +24 -6
  5. package/build/columns/edit.js.map +1 -1
  6. package/build/columns/index.js +2 -1
  7. package/build/columns/index.js.map +1 -1
  8. package/build/columns/utils.js +4 -9
  9. package/build/columns/utils.js.map +1 -1
  10. package/build/cover/edit/index.js +46 -50
  11. package/build/cover/edit/index.js.map +1 -1
  12. package/build/cover/edit/{resizable-cover.js → resizable-cover-popover.js} +32 -6
  13. package/build/cover/edit/resizable-cover-popover.js.map +1 -0
  14. package/build/cover/index.js +12 -0
  15. package/build/cover/index.js.map +1 -1
  16. package/build/details/edit.js +67 -0
  17. package/build/details/edit.js.map +1 -0
  18. package/build/details/index.js +110 -0
  19. package/build/details/index.js.map +1 -0
  20. package/build/details/save.js +33 -0
  21. package/build/details/save.js.map +1 -0
  22. package/build/details-content/edit.js +34 -0
  23. package/build/details-content/edit.js.map +1 -0
  24. package/build/details-content/index.js +94 -0
  25. package/build/details-content/index.js.map +1 -0
  26. package/build/details-content/save.js +20 -0
  27. package/build/details-content/save.js.map +1 -0
  28. package/build/details-summary/edit.js +42 -0
  29. package/build/details-summary/edit.js.map +1 -0
  30. package/build/details-summary/index.js +97 -0
  31. package/build/details-summary/index.js.map +1 -0
  32. package/build/details-summary/save.js +24 -0
  33. package/build/details-summary/save.js.map +1 -0
  34. package/build/embed/deprecated.js +4 -1
  35. package/build/embed/deprecated.js.map +1 -1
  36. package/build/embed/embed-link-settings.native.js +1 -1
  37. package/build/embed/embed-link-settings.native.js.map +1 -1
  38. package/build/embed/embed-placeholder.js +1 -1
  39. package/build/embed/embed-placeholder.js.map +1 -1
  40. package/build/embed/index.js +4 -1
  41. package/build/embed/index.js.map +1 -1
  42. package/build/embed/transforms.js +4 -1
  43. package/build/embed/transforms.js.map +1 -1
  44. package/build/embed/util.js +4 -1
  45. package/build/embed/util.js.map +1 -1
  46. package/build/file/edit.native.js +0 -2
  47. package/build/file/edit.native.js.map +1 -1
  48. package/build/gallery/use-get-media.native.js +2 -1
  49. package/build/gallery/use-get-media.native.js.map +1 -1
  50. package/build/image/index.js +4 -2
  51. package/build/image/index.js.map +1 -1
  52. package/build/index.js +24 -6
  53. package/build/index.js.map +1 -1
  54. package/build/media-text/media-container.js +2 -6
  55. package/build/media-text/media-container.js.map +1 -1
  56. package/build/media-text/media-container.native.js +3 -3
  57. package/build/media-text/media-container.native.js.map +1 -1
  58. package/build/navigation/deprecated.js +8 -11
  59. package/build/navigation/deprecated.js.map +1 -1
  60. package/build/navigation/edit/unsaved-inner-blocks.js +1 -14
  61. package/build/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  62. package/build/post-author/index.js +0 -1
  63. package/build/post-author/index.js.map +1 -1
  64. package/build/post-featured-image/dimension-controls.js +2 -1
  65. package/build/post-featured-image/dimension-controls.js.map +1 -1
  66. package/build/post-featured-image/edit.js +8 -22
  67. package/build/post-featured-image/edit.js.map +1 -1
  68. package/build/post-time-to-read/edit.js +1 -1
  69. package/build/post-time-to-read/edit.js.map +1 -1
  70. package/build/post-time-to-read/index.js +7 -0
  71. package/build/post-time-to-read/index.js.map +1 -1
  72. package/build/spacer/controls.js +25 -6
  73. package/build/spacer/controls.js.map +1 -1
  74. package/build/spacer/edit.js +14 -4
  75. package/build/spacer/edit.js.map +1 -1
  76. package/build/spacer/save.js +2 -2
  77. package/build/spacer/save.js.map +1 -1
  78. package/build/table/state.js +35 -35
  79. package/build/table/state.js.map +1 -1
  80. package/build/term-description/index.js +1 -2
  81. package/build/term-description/index.js.map +1 -1
  82. package/build/utils/clean-empty-object.js +5 -2
  83. package/build/utils/clean-empty-object.js.map +1 -1
  84. package/build-module/column/index.js +2 -1
  85. package/build-module/column/index.js.map +1 -1
  86. package/build-module/columns/edit.js +24 -6
  87. package/build-module/columns/edit.js.map +1 -1
  88. package/build-module/columns/index.js +2 -1
  89. package/build-module/columns/index.js.map +1 -1
  90. package/build-module/columns/utils.js +4 -8
  91. package/build-module/columns/utils.js.map +1 -1
  92. package/build-module/cover/edit/index.js +48 -52
  93. package/build-module/cover/edit/index.js.map +1 -1
  94. package/build-module/cover/edit/{resizable-cover.js → resizable-cover-popover.js} +31 -6
  95. package/build-module/cover/edit/resizable-cover-popover.js.map +1 -0
  96. package/build-module/cover/index.js +12 -0
  97. package/build-module/cover/index.js.map +1 -1
  98. package/build-module/details/edit.js +52 -0
  99. package/build-module/details/edit.js.map +1 -0
  100. package/build-module/details/index.js +91 -0
  101. package/build-module/details/index.js.map +1 -0
  102. package/build-module/details/save.js +20 -0
  103. package/build-module/details/save.js.map +1 -0
  104. package/build-module/details-content/edit.js +23 -0
  105. package/build-module/details-content/edit.js.map +1 -0
  106. package/build-module/details-content/index.js +76 -0
  107. package/build-module/details-content/index.js.map +1 -0
  108. package/build-module/details-content/save.js +11 -0
  109. package/build-module/details-content/save.js.map +1 -0
  110. package/build-module/details-summary/edit.js +30 -0
  111. package/build-module/details-summary/edit.js.map +1 -0
  112. package/build-module/details-summary/index.js +79 -0
  113. package/build-module/details-summary/index.js.map +1 -0
  114. package/build-module/details-summary/save.js +16 -0
  115. package/build-module/details-summary/save.js.map +1 -0
  116. package/build-module/embed/deprecated.js +4 -1
  117. package/build-module/embed/deprecated.js.map +1 -1
  118. package/build-module/embed/embed-link-settings.native.js +1 -1
  119. package/build-module/embed/embed-link-settings.native.js.map +1 -1
  120. package/build-module/embed/embed-placeholder.js +1 -1
  121. package/build-module/embed/embed-placeholder.js.map +1 -1
  122. package/build-module/embed/index.js +4 -1
  123. package/build-module/embed/index.js.map +1 -1
  124. package/build-module/embed/transforms.js +4 -1
  125. package/build-module/embed/transforms.js.map +1 -1
  126. package/build-module/embed/util.js +4 -1
  127. package/build-module/embed/util.js.map +1 -1
  128. package/build-module/file/edit.native.js +0 -2
  129. package/build-module/file/edit.native.js.map +1 -1
  130. package/build-module/gallery/use-get-media.native.js +2 -1
  131. package/build-module/gallery/use-get-media.native.js.map +1 -1
  132. package/build-module/image/index.js +4 -2
  133. package/build-module/image/index.js.map +1 -1
  134. package/build-module/index.js +21 -6
  135. package/build-module/index.js.map +1 -1
  136. package/build-module/media-text/media-container.js +1 -5
  137. package/build-module/media-text/media-container.js.map +1 -1
  138. package/build-module/media-text/media-container.native.js +1 -1
  139. package/build-module/media-text/media-container.native.js.map +1 -1
  140. package/build-module/navigation/deprecated.js +8 -10
  141. package/build-module/navigation/deprecated.js.map +1 -1
  142. package/build-module/navigation/edit/unsaved-inner-blocks.js +1 -14
  143. package/build-module/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  144. package/build-module/post-author/index.js +0 -1
  145. package/build-module/post-author/index.js.map +1 -1
  146. package/build-module/post-featured-image/dimension-controls.js +2 -1
  147. package/build-module/post-featured-image/dimension-controls.js.map +1 -1
  148. package/build-module/post-featured-image/edit.js +8 -22
  149. package/build-module/post-featured-image/edit.js.map +1 -1
  150. package/build-module/post-time-to-read/edit.js +1 -1
  151. package/build-module/post-time-to-read/edit.js.map +1 -1
  152. package/build-module/post-time-to-read/index.js +7 -0
  153. package/build-module/post-time-to-read/index.js.map +1 -1
  154. package/build-module/spacer/controls.js +27 -9
  155. package/build-module/spacer/controls.js.map +1 -1
  156. package/build-module/spacer/edit.js +14 -5
  157. package/build-module/spacer/edit.js.map +1 -1
  158. package/build-module/spacer/save.js +3 -3
  159. package/build-module/spacer/save.js.map +1 -1
  160. package/build-module/table/state.js +35 -33
  161. package/build-module/table/state.js.map +1 -1
  162. package/build-module/term-description/index.js +1 -2
  163. package/build-module/term-description/index.js.map +1 -1
  164. package/build-module/utils/clean-empty-object.js +6 -3
  165. package/build-module/utils/clean-empty-object.js.map +1 -1
  166. package/build-style/columns/style-rtl.css +4 -1
  167. package/build-style/columns/style.css +4 -1
  168. package/build-style/common-rtl.css +1 -1
  169. package/build-style/common.css +1 -1
  170. package/build-style/cover/editor-rtl.css +11 -12
  171. package/build-style/cover/editor.css +11 -12
  172. package/build-style/cover/style-rtl.css +3 -2
  173. package/build-style/cover/style.css +3 -2
  174. package/build-style/details/style-rtl.css +91 -0
  175. package/build-style/details/style.css +91 -0
  176. package/build-style/details-summary/editor-rtl.css +91 -0
  177. package/build-style/details-summary/editor.css +91 -0
  178. package/build-style/details-summary/style-rtl.css +91 -0
  179. package/build-style/details-summary/style.css +91 -0
  180. package/build-style/editor-rtl.css +34 -48
  181. package/build-style/editor.css +34 -48
  182. package/build-style/file/style-rtl.css +4 -3
  183. package/build-style/file/style.css +4 -3
  184. package/build-style/post-excerpt/style-rtl.css +1 -1
  185. package/build-style/post-excerpt/style.css +1 -1
  186. package/build-style/pullquote/style-rtl.css +4 -1
  187. package/build-style/pullquote/style.css +4 -1
  188. package/build-style/shortcode/editor-rtl.css +15 -34
  189. package/build-style/shortcode/editor.css +15 -34
  190. package/build-style/spacer/editor-rtl.css +4 -2
  191. package/build-style/spacer/editor.css +4 -2
  192. package/build-style/style-rtl.css +25 -9
  193. package/build-style/style.css +25 -9
  194. package/package.json +31 -31
  195. package/src/column/block.json +2 -1
  196. package/src/columns/block.json +2 -1
  197. package/src/columns/edit.js +33 -9
  198. package/src/columns/style.scss +5 -1
  199. package/src/columns/utils.js +8 -9
  200. package/src/common.scss +1 -1
  201. package/src/cover/block.json +12 -0
  202. package/src/cover/edit/index.js +44 -37
  203. package/src/cover/edit/resizable-cover-popover.js +82 -0
  204. package/src/cover/editor.scss +20 -13
  205. package/src/cover/index.php +9 -10
  206. package/src/cover/style.scss +2 -1
  207. package/src/cover/test/edit.js +1 -1
  208. package/src/details/block.json +54 -0
  209. package/src/details/edit.js +59 -0
  210. package/src/details/index.js +35 -0
  211. package/src/details/save.js +15 -0
  212. package/src/details/style.scss +3 -0
  213. package/src/details-content/block.json +50 -0
  214. package/src/details-content/edit.js +29 -0
  215. package/src/details-content/index.js +23 -0
  216. package/src/details-content/save.js +12 -0
  217. package/src/details-summary/block.json +53 -0
  218. package/src/details-summary/edit.js +27 -0
  219. package/src/details-summary/editor.scss +3 -0
  220. package/src/details-summary/index.js +23 -0
  221. package/src/details-summary/save.js +13 -0
  222. package/src/details-summary/style.scss +3 -0
  223. package/src/editor.scss +1 -0
  224. package/src/embed/block.json +4 -1
  225. package/src/embed/embed-link-settings.native.js +1 -1
  226. package/src/embed/embed-placeholder.js +1 -1
  227. package/src/file/edit.native.js +0 -2
  228. package/src/file/style.scss +5 -2
  229. package/src/gallery/test/use-get-media.native.js +24 -0
  230. package/src/gallery/use-get-media.native.js +1 -1
  231. package/src/image/block.json +4 -2
  232. package/src/index.js +13 -3
  233. package/src/media-text/media-container.js +1 -5
  234. package/src/media-text/media-container.native.js +1 -1
  235. package/src/navigation/deprecated.js +15 -19
  236. package/src/navigation/edit/unsaved-inner-blocks.js +34 -46
  237. package/src/navigation/index.php +0 -4
  238. package/src/paragraph/test/edit.native.js +356 -1
  239. package/src/post-author/block.json +0 -1
  240. package/src/post-excerpt/index.php +3 -3
  241. package/src/post-excerpt/style.scss +2 -1
  242. package/src/post-featured-image/dimension-controls.js +5 -1
  243. package/src/post-featured-image/edit.js +8 -29
  244. package/src/post-time-to-read/block.json +7 -0
  245. package/src/post-time-to-read/edit.js +1 -1
  246. package/src/post-time-to-read/index.php +3 -3
  247. package/src/preformatted/test/__snapshots__/edit.native.js.snap +2 -2
  248. package/src/preformatted/test/edit.native.js +45 -2
  249. package/src/pullquote/style.scss +5 -1
  250. package/src/pullquote/test/edit.native.js +70 -0
  251. package/src/quote/test/edit.native.js +92 -0
  252. package/src/search/index.php +1 -1
  253. package/src/shortcode/editor.scss +26 -5
  254. package/src/spacer/controls.js +42 -17
  255. package/src/spacer/edit.js +23 -4
  256. package/src/spacer/editor.scss +2 -1
  257. package/src/spacer/save.js +3 -3
  258. package/src/style.scss +2 -0
  259. package/src/table/state.js +83 -66
  260. package/src/term-description/block.json +1 -2
  261. package/src/utils/clean-empty-object.js +4 -4
  262. package/src/verse/test/edit.native.js +33 -0
  263. package/build/cover/edit/resizable-cover.js.map +0 -1
  264. package/build/media-text/media-container-icon.js +0 -27
  265. package/build/media-text/media-container-icon.js.map +0 -1
  266. package/build-module/cover/edit/resizable-cover.js.map +0 -1
  267. package/build-module/media-text/media-container-icon.js +0 -17
  268. package/build-module/media-text/media-container-icon.js.map +0 -1
  269. package/src/cover/edit/resizable-cover.js +0 -61
  270. package/src/media-text/media-container-icon.js +0 -12
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { mapValues } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -331,22 +326,23 @@ const migrateTypographyPresets = function ( attributes ) {
331
326
  ...attributes,
332
327
  style: {
333
328
  ...attributes.style,
334
- typography: mapValues(
335
- attributes.style.typography,
336
- ( value, key ) => {
337
- const prefix = TYPOGRAPHY_PRESET_DEPRECATION_MAP[ key ];
338
- if ( prefix && value.startsWith( prefix ) ) {
339
- const newValue = value.slice( prefix.length );
340
- if (
341
- 'textDecoration' === key &&
342
- 'strikethrough' === newValue
343
- ) {
344
- return 'line-through';
329
+ typography: Object.fromEntries(
330
+ Object.entries( attributes.style.typography ?? {} ).map(
331
+ ( [ key, value ] ) => {
332
+ const prefix = TYPOGRAPHY_PRESET_DEPRECATION_MAP[ key ];
333
+ if ( prefix && value.startsWith( prefix ) ) {
334
+ const newValue = value.slice( prefix.length );
335
+ if (
336
+ 'textDecoration' === key &&
337
+ 'strikethrough' === newValue
338
+ ) {
339
+ return [ key, 'line-through' ];
340
+ }
341
+ return [ key, newValue ];
345
342
  }
346
- return newValue;
343
+ return [ key, value ];
347
344
  }
348
- return value;
349
- }
345
+ )
350
346
  ),
351
347
  },
352
348
  };
@@ -120,53 +120,41 @@ export default function UnsavedInnerBlocks( {
120
120
  const { hasResolvedNavigationMenus } = useNavigationMenu();
121
121
 
122
122
  // Automatically save the uncontrolled blocks.
123
- useEffect(
124
- () => {
125
- // The block will be disabled when used in a BlockPreview.
126
- // In this case avoid automatic creation of a wp_navigation post.
127
- // Otherwise the user will be spammed with lots of menus!
128
- //
129
- // Also ensure other navigation menus have loaded so an
130
- // accurate name can be created.
131
- //
132
- // Don't try saving when another save is already
133
- // in progress.
134
- //
135
- // And finally only create the menu when the block is selected,
136
- // which is an indication they want to start editing.
137
- if (
138
- isDisabled ||
139
- isSaving ||
140
- ! hasResolvedDraftNavigationMenus ||
141
- ! hasResolvedNavigationMenus ||
142
- ! hasSelection ||
143
- ! innerBlocksAreDirty
144
- ) {
145
- return;
146
- }
123
+ useEffect( () => {
124
+ // The block will be disabled when used in a BlockPreview.
125
+ // In this case avoid automatic creation of a wp_navigation post.
126
+ // Otherwise the user will be spammed with lots of menus!
127
+ //
128
+ // Also ensure other navigation menus have loaded so an
129
+ // accurate name can be created.
130
+ //
131
+ // Don't try saving when another save is already
132
+ // in progress.
133
+ //
134
+ // And finally only create the menu when the block is selected,
135
+ // which is an indication they want to start editing.
136
+ if (
137
+ isDisabled ||
138
+ isSaving ||
139
+ ! hasResolvedDraftNavigationMenus ||
140
+ ! hasResolvedNavigationMenus ||
141
+ ! hasSelection ||
142
+ ! innerBlocksAreDirty
143
+ ) {
144
+ return;
145
+ }
147
146
 
148
- createNavigationMenu( null, blocks );
149
- },
150
- /* The dependency "blocks" is intentionally omitted here.
151
- * This is because making blocks a dependency would cause
152
- * createNavigationMenu to run on every block change whereas
153
- * we only want it to run when the blocks are first detected
154
- * as dirty.
155
- * A better solution might be to add a hard saving lock using
156
- * a ref to avoid having to disbale theses eslint rules.
157
- */
158
- /* eslint-disable react-hooks/exhaustive-deps */
159
- [
160
- createNavigationMenu,
161
- isDisabled,
162
- isSaving,
163
- hasResolvedDraftNavigationMenus,
164
- hasResolvedNavigationMenus,
165
- innerBlocksAreDirty,
166
- hasSelection,
167
- ]
168
- /* eslint-enable react-hooks/exhaustive-deps */
169
- );
147
+ createNavigationMenu( null, blocks );
148
+ }, [
149
+ blocks,
150
+ createNavigationMenu,
151
+ isDisabled,
152
+ isSaving,
153
+ hasResolvedDraftNavigationMenus,
154
+ hasResolvedNavigationMenus,
155
+ innerBlocksAreDirty,
156
+ hasSelection,
157
+ ] );
170
158
 
171
159
  const Wrapper = isSaving ? Disabled : 'div';
172
160
 
@@ -471,10 +471,6 @@ function block_core_navigation_get_fallback_blocks() {
471
471
  $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : $fallback_blocks;
472
472
  }
473
473
 
474
- // Normalizing blocks may result in an empty array of blocks if they were all `null` blocks.
475
- // In this case default empty blocks.
476
- $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : array();
477
-
478
474
  /**
479
475
  * Filters the fallback experience for the Navigation block.
480
476
  *
@@ -1,13 +1,33 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { render } from 'test/helpers';
4
+ import {
5
+ act,
6
+ addBlock,
7
+ getBlock,
8
+ changeTextOfRichText,
9
+ changeAndSelectTextOfRichText,
10
+ fireEvent,
11
+ getEditorHtml,
12
+ initializeEditor,
13
+ render,
14
+ setupCoreBlocks,
15
+ within,
16
+ } from 'test/helpers';
17
+ import Clipboard from '@react-native-clipboard/clipboard';
18
+
19
+ /**
20
+ * WordPress dependencies
21
+ */
22
+ import { ENTER } from '@wordpress/keycodes';
5
23
 
6
24
  /**
7
25
  * Internal dependencies
8
26
  */
9
27
  import Paragraph from '../edit';
10
28
 
29
+ setupCoreBlocks();
30
+
11
31
  const getTestComponentWithContent = ( content ) => {
12
32
  return render(
13
33
  <Paragraph
@@ -24,4 +44,339 @@ describe( 'Paragraph block', () => {
24
44
  const screen = getTestComponentWithContent( '' );
25
45
  expect( screen.container ).toBeTruthy();
26
46
  } );
47
+
48
+ it( 'should bold text', async () => {
49
+ // Arrange
50
+ const screen = await initializeEditor();
51
+ await addBlock( screen, 'Paragraph' );
52
+
53
+ // Act
54
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
55
+ fireEvent.press( paragraphBlock );
56
+ const paragraphTextInput =
57
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
58
+ changeAndSelectTextOfRichText(
59
+ paragraphTextInput,
60
+ 'A quick brown fox jumps over the lazy dog.',
61
+ { selectionStart: 2, selectionEnd: 7 }
62
+ );
63
+ fireEvent.press( screen.getByLabelText( 'Bold' ) );
64
+
65
+ // Assert
66
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
67
+ "<!-- wp:paragraph -->
68
+ <p>A <strong>quick</strong> brown fox jumps over the lazy dog.</p>
69
+ <!-- /wp:paragraph -->"
70
+ ` );
71
+ } );
72
+
73
+ it( 'should italicize text', async () => {
74
+ // Arrange
75
+ const screen = await initializeEditor();
76
+ await addBlock( screen, 'Paragraph' );
77
+
78
+ // Act
79
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
80
+ fireEvent.press( paragraphBlock );
81
+ const paragraphTextInput =
82
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
83
+ changeAndSelectTextOfRichText(
84
+ paragraphTextInput,
85
+ 'A quick brown fox jumps over the lazy dog.',
86
+ { selectionStart: 2, selectionEnd: 7 }
87
+ );
88
+ fireEvent.press( screen.getByLabelText( 'Italic' ) );
89
+
90
+ // Assert
91
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
92
+ "<!-- wp:paragraph -->
93
+ <p>A <em>quick</em> brown fox jumps over the lazy dog.</p>
94
+ <!-- /wp:paragraph -->"
95
+ ` );
96
+ } );
97
+
98
+ it( 'should strikethrough text', async () => {
99
+ // Arrange
100
+ const screen = await initializeEditor();
101
+ await addBlock( screen, 'Paragraph' );
102
+
103
+ // Act
104
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
105
+ fireEvent.press( paragraphBlock );
106
+ const paragraphTextInput =
107
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
108
+ changeAndSelectTextOfRichText(
109
+ paragraphTextInput,
110
+ 'A quick brown fox jumps over the lazy dog.',
111
+ { selectionStart: 2, selectionEnd: 7 }
112
+ );
113
+ fireEvent.press( screen.getByLabelText( 'Strikethrough' ) );
114
+
115
+ // Assert
116
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
117
+ "<!-- wp:paragraph -->
118
+ <p>A <s>quick</s> brown fox jumps over the lazy dog.</p>
119
+ <!-- /wp:paragraph -->"
120
+ ` );
121
+ } );
122
+
123
+ it( 'should left align text', async () => {
124
+ // Arrange
125
+ const screen = await initializeEditor();
126
+ await addBlock( screen, 'Paragraph' );
127
+
128
+ // Act
129
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
130
+ fireEvent.press( paragraphBlock );
131
+ const paragraphTextInput =
132
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
133
+ changeTextOfRichText(
134
+ paragraphTextInput,
135
+ 'A quick brown fox jumps over the lazy dog.'
136
+ );
137
+ fireEvent.press( screen.getByLabelText( 'Align text' ) );
138
+ fireEvent.press( screen.getByLabelText( 'Align text left' ) );
139
+
140
+ // Assert
141
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
142
+ "<!-- wp:paragraph {"align":"left"} -->
143
+ <p class="has-text-align-left">A quick brown fox jumps over the lazy dog.</p>
144
+ <!-- /wp:paragraph -->"
145
+ ` );
146
+ } );
147
+
148
+ it( 'should center align text', async () => {
149
+ // Arrange
150
+ const screen = await initializeEditor();
151
+ await addBlock( screen, 'Paragraph' );
152
+
153
+ // Act
154
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
155
+ fireEvent.press( paragraphBlock );
156
+ const paragraphTextInput =
157
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
158
+ changeTextOfRichText(
159
+ paragraphTextInput,
160
+ 'A quick brown fox jumps over the lazy dog.'
161
+ );
162
+ fireEvent.press( screen.getByLabelText( 'Align text' ) );
163
+ fireEvent.press( screen.getByLabelText( 'Align text center' ) );
164
+
165
+ // Assert
166
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
167
+ "<!-- wp:paragraph {"align":"center"} -->
168
+ <p class="has-text-align-center">A quick brown fox jumps over the lazy dog.</p>
169
+ <!-- /wp:paragraph -->"
170
+ ` );
171
+ } );
172
+
173
+ it( 'should right align text', async () => {
174
+ // Arrange
175
+ const screen = await initializeEditor();
176
+ await addBlock( screen, 'Paragraph' );
177
+
178
+ // Act
179
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
180
+ fireEvent.press( paragraphBlock );
181
+ const paragraphTextInput =
182
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
183
+ changeTextOfRichText(
184
+ paragraphTextInput,
185
+ 'A quick brown fox jumps over the lazy dog.'
186
+ );
187
+ fireEvent.press( screen.getByLabelText( 'Align text' ) );
188
+ fireEvent.press( screen.getByLabelText( 'Align text right' ) );
189
+
190
+ // Assert
191
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
192
+ "<!-- wp:paragraph {"align":"right"} -->
193
+ <p class="has-text-align-right">A quick brown fox jumps over the lazy dog.</p>
194
+ <!-- /wp:paragraph -->"
195
+ ` );
196
+ } );
197
+
198
+ it( 'should preserve alignment when split', async () => {
199
+ // Arrange
200
+ const screen = await initializeEditor();
201
+ await addBlock( screen, 'Paragraph' );
202
+
203
+ // Act
204
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
205
+ fireEvent.press( paragraphBlock );
206
+ fireEvent.press( screen.getByLabelText( 'Align text' ) );
207
+ fireEvent.press( screen.getByLabelText( 'Align text center' ) );
208
+ const paragraphTextInput =
209
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
210
+ const string = 'A quick brown fox jumps over the lazy dog.';
211
+ changeAndSelectTextOfRichText( paragraphTextInput, string, {
212
+ selectionStart: string.length / 2,
213
+ selectionEnd: string.length / 2,
214
+ } );
215
+ fireEvent( paragraphTextInput, 'onKeyDown', {
216
+ nativeEvent: {},
217
+ preventDefault() {},
218
+ keyCode: ENTER,
219
+ } );
220
+
221
+ // Assert
222
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
223
+ "<!-- wp:paragraph {"align":"center"} -->
224
+ <p class="has-text-align-center">A quick brown fox jum</p>
225
+ <!-- /wp:paragraph -->
226
+
227
+ <!-- wp:paragraph {"align":"center"} -->
228
+ <p class="has-text-align-center">ps over the lazy dog.</p>
229
+ <!-- /wp:paragraph -->"
230
+ ` );
231
+ } );
232
+
233
+ it( 'should link text without selection', async () => {
234
+ // Arrange
235
+ const screen = await initializeEditor();
236
+ await addBlock( screen, 'Paragraph' );
237
+
238
+ // Act
239
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
240
+ fireEvent.press( paragraphBlock );
241
+ // Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
242
+ await act( () => fireEvent.press( screen.getByLabelText( 'Link' ) ) );
243
+ // Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
244
+ await act( () =>
245
+ fireEvent.press(
246
+ screen.getByLabelText( 'Link to, Search or type URL' )
247
+ )
248
+ );
249
+ fireEvent.changeText(
250
+ screen.getByPlaceholderText( 'Search or type URL' ),
251
+ 'wordpress.org'
252
+ );
253
+ fireEvent.changeText(
254
+ screen.getByPlaceholderText( 'Add link text' ),
255
+ 'WordPress'
256
+ );
257
+ jest.useFakeTimers();
258
+ fireEvent.press( screen.getByLabelText( 'Apply' ) );
259
+ // Await link picker navigation delay
260
+ act( () => jest.runOnlyPendingTimers() );
261
+
262
+ // Assert
263
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
264
+ "<!-- wp:paragraph -->
265
+ <p><a href="http://wordpress.org">WordPress</a></p>
266
+ <!-- /wp:paragraph -->"
267
+ ` );
268
+
269
+ jest.useRealTimers();
270
+ } );
271
+
272
+ it( 'should link text with selection', async () => {
273
+ // Arrange
274
+ const screen = await initializeEditor();
275
+ await addBlock( screen, 'Paragraph' );
276
+
277
+ // Act
278
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
279
+ fireEvent.press( paragraphBlock );
280
+ const paragraphTextInput =
281
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
282
+ changeAndSelectTextOfRichText(
283
+ paragraphTextInput,
284
+ 'A quick brown fox jumps over the lazy dog.',
285
+ {
286
+ selectionStart: 2,
287
+ selectionEnd: 7,
288
+ }
289
+ );
290
+ // Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
291
+ await act( () => fireEvent.press( screen.getByLabelText( 'Link' ) ) );
292
+ // Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
293
+ await act( () =>
294
+ fireEvent.press(
295
+ screen.getByLabelText( 'Link to, Search or type URL' )
296
+ )
297
+ );
298
+ fireEvent.changeText(
299
+ screen.getByPlaceholderText( 'Search or type URL' ),
300
+ 'wordpress.org'
301
+ );
302
+ jest.useFakeTimers();
303
+ fireEvent.press( screen.getByLabelText( 'Apply' ) );
304
+ // Await link picker navigation delay
305
+ act( () => jest.runOnlyPendingTimers() );
306
+
307
+ // Assert
308
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
309
+ "<!-- wp:paragraph -->
310
+ <p>A <a href="http://wordpress.org">quick</a> brown fox jumps over the lazy dog.</p>
311
+ <!-- /wp:paragraph -->"
312
+ ` );
313
+
314
+ jest.useRealTimers();
315
+ } );
316
+
317
+ it( 'should link text with clipboard contents', async () => {
318
+ // Arrange
319
+ Clipboard.getString.mockResolvedValue( 'https://wordpress.org' );
320
+ const screen = await initializeEditor();
321
+ await addBlock( screen, 'Paragraph' );
322
+
323
+ // Act
324
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
325
+ fireEvent.press( paragraphBlock );
326
+ const paragraphTextInput =
327
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
328
+ changeAndSelectTextOfRichText(
329
+ paragraphTextInput,
330
+ 'A quick brown fox jumps over the lazy dog.',
331
+ {
332
+ selectionStart: 2,
333
+ selectionEnd: 7,
334
+ }
335
+ );
336
+ // Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
337
+ await act( () => fireEvent.press( screen.getByLabelText( 'Link' ) ) );
338
+ // Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
339
+ await act( () =>
340
+ fireEvent.press(
341
+ screen.getByLabelText( 'Link to, Search or type URL' )
342
+ )
343
+ );
344
+
345
+ // Assert
346
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
347
+ "<!-- wp:paragraph -->
348
+ <p>A <a href="https://wordpress.org">quick</a> brown fox jumps over the lazy dog.</p>
349
+ <!-- /wp:paragraph -->"
350
+ ` );
351
+
352
+ Clipboard.getString.mockReset();
353
+ } );
354
+
355
+ it( 'should not remove leading or trailing whitespace when formatting', async () => {
356
+ // Arrange
357
+ const screen = await initializeEditor();
358
+ await addBlock( screen, 'Paragraph' );
359
+
360
+ // Act
361
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
362
+ fireEvent.press( paragraphBlock );
363
+ const paragraphTextInput =
364
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
365
+ changeAndSelectTextOfRichText(
366
+ paragraphTextInput,
367
+ ' some text ',
368
+ {
369
+ selectionStart: 5,
370
+ selectionEnd: 14,
371
+ }
372
+ );
373
+ fireEvent.press( screen.getByLabelText( 'Italic' ) );
374
+
375
+ // Assert
376
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
377
+ "<!-- wp:paragraph -->
378
+ <p> <em>some text</em> </p>
379
+ <!-- /wp:paragraph -->"
380
+ ` );
381
+ } );
27
382
  } );
@@ -64,6 +64,5 @@
64
64
  }
65
65
  }
66
66
  },
67
- "editorStyle": "wp-block-post-author-editor",
68
67
  "style": "wp-block-post-author"
69
68
  }
@@ -25,11 +25,11 @@ function render_block_core_post_excerpt( $attributes, $content, $block ) {
25
25
  * wp_trim_words is used instead.
26
26
  */
27
27
  $excerpt_length = $attributes['excerptLength'];
28
+ $excerpt = get_the_excerpt( $block->context['postId'] );
28
29
  if ( isset( $excerpt_length ) ) {
29
- $excerpt = wp_trim_words( get_the_excerpt(), $excerpt_length );
30
- } else {
31
- $excerpt = get_the_excerpt();
30
+ $excerpt = wp_trim_words( $excerpt, $excerpt_length );
32
31
  }
32
+
33
33
  $more_text = ! empty( $attributes['moreText'] ) ? '<a class="wp-block-post-excerpt__more-link" href="' . esc_url( get_the_permalink( $block->context['postId'] ) ) . '">' . wp_kses_post( $attributes['moreText'] ) . '</a>' : '';
34
34
  $filter_excerpt_more = function( $more ) use ( $more_text ) {
35
35
  return empty( $more_text ) ? $more : '';
@@ -1,4 +1,5 @@
1
- .wp-block-post-excerpt {
1
+ // Lowest specificity on wrapper margins to avoid overriding layout styles.
2
+ :where(.wp-block-post-excerpt) {
2
3
  margin-top: var(--wp--style--block-gap);
3
4
  margin-bottom: var(--wp--style--block-gap);
4
5
  }
@@ -70,6 +70,10 @@ const DimensionControls = ( {
70
70
  } );
71
71
  };
72
72
  const scaleLabel = _x( 'Scale', 'Image scaling options' );
73
+
74
+ const showScaleControl =
75
+ height || ( aspectRatio && aspectRatio !== 'auto' );
76
+
73
77
  return (
74
78
  <InspectorControls group="dimensions">
75
79
  <ToolsPanelItem
@@ -170,7 +174,7 @@ const DimensionControls = ( {
170
174
  units={ units }
171
175
  />
172
176
  </ToolsPanelItem>
173
- { ( height || aspectRatio ) && (
177
+ { showScaleControl && (
174
178
  <ToolsPanelItem
175
179
  hasValue={ () => !! scale && scale !== DEFAULT_SCALE }
176
180
  label={ scaleLabel }
@@ -183,10 +183,15 @@ export default function PostFeaturedImageEdit( {
183
183
  let image;
184
184
 
185
185
  /**
186
- * A post featured image block placed in a query loop
187
- * does not have image replacement or upload options.
186
+ * A Post Featured Image block should not have image replacement
187
+ * or upload options in the following cases:
188
+ * - Is placed in a Query Loop. This is a consious decision to
189
+ * prevent content editing of different posts in Query Loop, and
190
+ * this could change in the future.
191
+ * - Is in a context where it does not have a postId (for example
192
+ * in a template or template part).
188
193
  */
189
- if ( ! featuredImage && isDescendentOfQueryLoop ) {
194
+ if ( ! featuredImage && ( isDescendentOfQueryLoop || ! postId ) ) {
190
195
  return (
191
196
  <>
192
197
  { controls }
@@ -202,32 +207,6 @@ export default function PostFeaturedImageEdit( {
202
207
  );
203
208
  }
204
209
 
205
- /**
206
- * A post featured image placed in a block template, outside a query loop,
207
- * does not have a postId and will always be a placeholder image.
208
- * It does not have image replacement, upload, or link options.
209
- */
210
- if ( ! featuredImage && ! postId ) {
211
- return (
212
- <>
213
- <DimensionControls
214
- clientId={ clientId }
215
- attributes={ attributes }
216
- setAttributes={ setAttributes }
217
- imageSizeOptions={ imageSizeOptions }
218
- />
219
- <div { ...blockProps }>
220
- { placeholder() }
221
- <Overlay
222
- attributes={ attributes }
223
- setAttributes={ setAttributes }
224
- clientId={ clientId }
225
- />
226
- </div>
227
- </>
228
- );
229
- }
230
-
231
210
  const label = __( 'Add a featured image' );
232
211
  const imageStyles = {
233
212
  ...borderProps.style,
@@ -14,6 +14,13 @@
14
14
  }
15
15
  },
16
16
  "supports": {
17
+ "color": {
18
+ "gradients": true,
19
+ "__experimentalDefaultControls": {
20
+ "background": true,
21
+ "text": true
22
+ }
23
+ },
17
24
  "html": false,
18
25
  "spacing": {
19
26
  "margin": true,
@@ -93,7 +93,7 @@ function PostTimeToReadEdit( { attributes, setAttributes, context } ) {
93
93
  } }
94
94
  />
95
95
  </BlockControls>
96
- <p { ...blockProps }>{ minutesToReadString }</p>
96
+ <div { ...blockProps }>{ minutesToReadString }</div>
97
97
  </>
98
98
  );
99
99
  }
@@ -32,8 +32,8 @@ function render_block_core_post_time_to_read( $attributes, $content, $block ) {
32
32
  $minutes_to_read = max( 1, (int) round( wp_word_count( $content, $word_count_type ) / $average_reading_rate ) );
33
33
 
34
34
  $minutes_to_read_string = sprintf(
35
- /* translators: %d is the number of minutes the post will take to read. */
36
- _n( '%d minute', '%d minutes', $minutes_to_read ),
35
+ /* translators: %s is the number of minutes the post will take to read. */
36
+ _n( '%s minute', '%s minutes', $minutes_to_read ),
37
37
  $minutes_to_read
38
38
  );
39
39
 
@@ -42,7 +42,7 @@ function render_block_core_post_time_to_read( $attributes, $content, $block ) {
42
42
  $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $align_class_name ) );
43
43
 
44
44
  return sprintf(
45
- '<p %1$s>%2$s</p>',
45
+ '<div %1$s>%2$s</div>',
46
46
  $wrapper_attributes,
47
47
  $minutes_to_read_string
48
48
  );