@wordpress/block-library 9.45.1-next.v.202605131032.0 → 9.47.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 (182) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/button/edit.cjs +7 -4
  3. package/build/button/edit.cjs.map +3 -3
  4. package/build/columns/edit.cjs +4 -10
  5. package/build/columns/edit.cjs.map +2 -2
  6. package/build/comments/edit/placeholder.cjs +1 -1
  7. package/build/comments/edit/placeholder.cjs.map +2 -2
  8. package/build/cover/edit/inspector-controls.cjs +4 -2
  9. package/build/cover/edit/inspector-controls.cjs.map +2 -2
  10. package/build/freeform/migration-notice.cjs +1 -1
  11. package/build/freeform/migration-notice.cjs.map +1 -1
  12. package/build/home-link/block.json +7 -0
  13. package/build/home-link/edit.cjs +167 -24
  14. package/build/home-link/edit.cjs.map +3 -3
  15. package/build/html/edit.cjs +2 -4
  16. package/build/html/edit.cjs.map +2 -2
  17. package/build/html/modal.cjs +0 -4
  18. package/build/html/modal.cjs.map +2 -2
  19. package/build/image/block.json +4 -0
  20. package/build/image/deprecated.cjs +202 -4
  21. package/build/image/deprecated.cjs.map +3 -3
  22. package/build/image/image.cjs +80 -27
  23. package/build/image/image.cjs.map +2 -2
  24. package/build/image/index.cjs +23 -4
  25. package/build/image/index.cjs.map +2 -2
  26. package/build/image/save.cjs +25 -10
  27. package/build/image/save.cjs.map +2 -2
  28. package/build/image/transforms.cjs +15 -3
  29. package/build/image/transforms.cjs.map +2 -2
  30. package/build/image/use-open-image-media-editor-modal.cjs +29 -12
  31. package/build/image/use-open-image-media-editor-modal.cjs.map +2 -2
  32. package/build/list-item/hooks/use-enter.cjs +8 -4
  33. package/build/list-item/hooks/use-enter.cjs.map +3 -3
  34. package/build/list-item/hooks/use-space.cjs +8 -4
  35. package/build/list-item/hooks/use-space.cjs.map +3 -3
  36. package/build/navigation-link/edit.cjs +2 -1
  37. package/build/navigation-link/edit.cjs.map +2 -2
  38. package/build/navigation-link/shared/use-handle-link-change.cjs +19 -3
  39. package/build/navigation-link/shared/use-handle-link-change.cjs.map +3 -3
  40. package/build/navigation-submenu/edit.cjs +8 -22
  41. package/build/navigation-submenu/edit.cjs.map +2 -2
  42. package/build/paragraph/use-enter.cjs +8 -4
  43. package/build/paragraph/use-enter.cjs.map +3 -3
  44. package/build/post-date/edit.cjs +9 -1
  45. package/build/post-date/edit.cjs.map +2 -2
  46. package/build/post-featured-image/edit.cjs +0 -1
  47. package/build/post-featured-image/edit.cjs.map +2 -2
  48. package/build/site-logo/edit.cjs +1 -1
  49. package/build/site-logo/edit.cjs.map +2 -2
  50. package/build/social-link/edit.cjs.map +3 -3
  51. package/build/tab-list/edit.cjs +2 -0
  52. package/build/tab-list/edit.cjs.map +2 -2
  53. package/build/tab-panels/edit.cjs +5 -1
  54. package/build/tab-panels/edit.cjs.map +2 -2
  55. package/build/table/edit.cjs +1 -0
  56. package/build/table/edit.cjs.map +2 -2
  57. package/build/tabs/edit.cjs +1 -36
  58. package/build/tabs/edit.cjs.map +2 -2
  59. package/build-module/button/edit.mjs +12 -5
  60. package/build-module/button/edit.mjs.map +2 -2
  61. package/build-module/columns/edit.mjs +4 -10
  62. package/build-module/columns/edit.mjs.map +2 -2
  63. package/build-module/comments/edit/placeholder.mjs +1 -1
  64. package/build-module/comments/edit/placeholder.mjs.map +2 -2
  65. package/build-module/cover/edit/inspector-controls.mjs +4 -3
  66. package/build-module/cover/edit/inspector-controls.mjs.map +2 -2
  67. package/build-module/freeform/migration-notice.mjs +1 -1
  68. package/build-module/freeform/migration-notice.mjs.map +1 -1
  69. package/build-module/home-link/block.json +7 -0
  70. package/build-module/home-link/edit.mjs +181 -26
  71. package/build-module/home-link/edit.mjs.map +2 -2
  72. package/build-module/html/edit.mjs +2 -4
  73. package/build-module/html/edit.mjs.map +2 -2
  74. package/build-module/html/modal.mjs +0 -4
  75. package/build-module/html/modal.mjs.map +2 -2
  76. package/build-module/image/block.json +4 -0
  77. package/build-module/image/deprecated.mjs +204 -5
  78. package/build-module/image/deprecated.mjs.map +2 -2
  79. package/build-module/image/image.mjs +81 -27
  80. package/build-module/image/image.mjs.map +2 -2
  81. package/build-module/image/index.mjs +23 -4
  82. package/build-module/image/index.mjs.map +2 -2
  83. package/build-module/image/save.mjs +25 -10
  84. package/build-module/image/save.mjs.map +2 -2
  85. package/build-module/image/transforms.mjs +15 -3
  86. package/build-module/image/transforms.mjs.map +2 -2
  87. package/build-module/image/use-open-image-media-editor-modal.mjs +29 -12
  88. package/build-module/image/use-open-image-media-editor-modal.mjs.map +2 -2
  89. package/build-module/list-item/hooks/use-enter.mjs +12 -5
  90. package/build-module/list-item/hooks/use-enter.mjs.map +2 -2
  91. package/build-module/list-item/hooks/use-space.mjs +12 -5
  92. package/build-module/list-item/hooks/use-space.mjs.map +2 -2
  93. package/build-module/navigation-link/edit.mjs +2 -1
  94. package/build-module/navigation-link/edit.mjs.map +2 -2
  95. package/build-module/navigation-link/shared/use-handle-link-change.mjs +19 -3
  96. package/build-module/navigation-link/shared/use-handle-link-change.mjs.map +2 -2
  97. package/build-module/navigation-submenu/edit.mjs +9 -23
  98. package/build-module/navigation-submenu/edit.mjs.map +2 -2
  99. package/build-module/paragraph/use-enter.mjs +12 -5
  100. package/build-module/paragraph/use-enter.mjs.map +2 -2
  101. package/build-module/post-date/edit.mjs +9 -1
  102. package/build-module/post-date/edit.mjs.map +2 -2
  103. package/build-module/post-featured-image/edit.mjs +0 -1
  104. package/build-module/post-featured-image/edit.mjs.map +2 -2
  105. package/build-module/site-logo/edit.mjs +1 -1
  106. package/build-module/site-logo/edit.mjs.map +2 -2
  107. package/build-module/social-link/edit.mjs +2 -2
  108. package/build-module/social-link/edit.mjs.map +2 -2
  109. package/build-module/tab-list/edit.mjs +2 -0
  110. package/build-module/tab-list/edit.mjs.map +2 -2
  111. package/build-module/tab-panels/edit.mjs +5 -1
  112. package/build-module/tab-panels/edit.mjs.map +2 -2
  113. package/build-module/table/edit.mjs +1 -0
  114. package/build-module/table/edit.mjs.map +2 -2
  115. package/build-module/tabs/edit.mjs +2 -37
  116. package/build-module/tabs/edit.mjs.map +2 -2
  117. package/build-style/breadcrumbs/style-rtl.css +1 -1
  118. package/build-style/breadcrumbs/style.css +1 -1
  119. package/build-style/editor-rtl.css +0 -11
  120. package/build-style/editor.css +0 -11
  121. package/build-style/gallery/editor-rtl.css +0 -11
  122. package/build-style/gallery/editor.css +0 -11
  123. package/build-style/style-rtl.css +1 -1
  124. package/build-style/style.css +1 -1
  125. package/package.json +42 -42
  126. package/src/block/edit-title.native.js +3 -3
  127. package/src/block/edit.native.js +2 -2
  128. package/src/breadcrumbs/style.scss +1 -1
  129. package/src/button/edit.js +14 -5
  130. package/src/columns/edit.js +3 -9
  131. package/src/comments/edit/placeholder.js +1 -1
  132. package/src/cover/controls.native.js +2 -2
  133. package/src/cover/edit/inspector-controls.js +8 -7
  134. package/src/cover/edit.native.js +6 -4
  135. package/src/cover/focal-point-settings-button.native.js +2 -2
  136. package/src/cover/test/edit.js +32 -31
  137. package/src/embed/embed-no-preview.native.js +7 -3
  138. package/src/embed/embed-placeholder.native.js +2 -2
  139. package/src/file/edit.native.js +2 -2
  140. package/src/freeform/migration-notice.js +1 -1
  141. package/src/gallery/editor.scss +0 -14
  142. package/src/home-link/block.json +7 -0
  143. package/src/home-link/edit.js +185 -22
  144. package/src/home-link/index.php +14 -2
  145. package/src/html/edit.js +14 -12
  146. package/src/html/modal.js +0 -5
  147. package/src/image/block.json +4 -0
  148. package/src/image/deprecated.js +236 -4
  149. package/src/image/edit.native.js +2 -2
  150. package/src/image/image.js +116 -41
  151. package/src/image/index.js +20 -1
  152. package/src/image/index.php +1 -1
  153. package/src/image/save.js +39 -12
  154. package/src/image/test/use-open-image-media-editor-modal.js +60 -0
  155. package/src/image/transforms.js +21 -5
  156. package/src/image/use-open-image-media-editor-modal.js +34 -16
  157. package/src/latest-posts/edit.native.js +2 -2
  158. package/src/list-item/hooks/use-enter.js +15 -5
  159. package/src/list-item/hooks/use-space.js +15 -5
  160. package/src/list-item/list-style-type.native.js +2 -2
  161. package/src/media-text/media-container.native.js +7 -3
  162. package/src/missing/edit.native.js +4 -4
  163. package/src/missing/test/edit.native.js +3 -3
  164. package/src/navigation/test/use-navigation-menu.js +8 -2
  165. package/src/navigation-link/edit.js +1 -0
  166. package/src/navigation-link/shared/test/use-handle-link-change.test.js +212 -0
  167. package/src/navigation-link/shared/use-handle-link-change.js +36 -9
  168. package/src/navigation-link/test/__snapshots__/hooks.js.snap +134 -45
  169. package/src/navigation-submenu/edit.js +11 -28
  170. package/src/navigation-submenu/index.php +13 -0
  171. package/src/paragraph/use-enter.js +19 -5
  172. package/src/post-date/edit.js +7 -3
  173. package/src/post-featured-image/edit.js +0 -1
  174. package/src/search/edit.native.js +2 -2
  175. package/src/search/test/edit.native.js +2 -2
  176. package/src/site-logo/edit.js +2 -1
  177. package/src/social-link/edit.js +2 -2
  178. package/src/tab-list/edit.js +3 -0
  179. package/src/tab-panels/edit.js +10 -1
  180. package/src/table/edit.js +1 -0
  181. package/src/tabs/edit.js +14 -42
  182. package/src/video/edit.native.js +3 -3
@@ -11,8 +11,14 @@ import {
11
11
  useBlockProps,
12
12
  __experimentalGetElementClassName,
13
13
  __experimentalGetBorderClassesAndStyles as getBorderClassesAndStyles,
14
+ __experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles,
14
15
  } from '@wordpress/block-editor';
15
16
 
17
+ /**
18
+ * Internal dependencies
19
+ */
20
+ import { mediaPosition } from './utils';
21
+
16
22
  /**
17
23
  * Deprecation for adding the `wp-image-${id}` class to the image block for
18
24
  * responsive images.
@@ -854,11 +860,12 @@ const v7 = {
854
860
  },
855
861
  },
856
862
  },
857
- migrate( { width, height, ...attributes } ) {
863
+ migrate( attributes ) {
864
+ const { width, height } = attributes;
858
865
  return {
859
866
  ...attributes,
860
- width: `${ width }px`,
861
- height: `${ height }px`,
867
+ width: typeof width === 'number' ? `${ width }px` : width,
868
+ height: typeof height === 'number' ? `${ height }px` : height,
862
869
  };
863
870
  },
864
871
  save( { attributes } ) {
@@ -1166,4 +1173,229 @@ const v8 = {
1166
1173
  },
1167
1174
  };
1168
1175
 
1169
- export default [ v8, v7, v6, v5, v4, v3, v2, v1 ];
1176
+ /**
1177
+ * Deprecation for adding height: auto when only one dimension is explicitly
1178
+ * set, to prevent theme CSS from squishing images.
1179
+ *
1180
+ * @see https://github.com/WordPress/gutenberg/pull/70575
1181
+ */
1182
+ const v9 = {
1183
+ attributes: {
1184
+ blob: {
1185
+ type: 'string',
1186
+ role: 'local',
1187
+ },
1188
+ url: {
1189
+ type: 'string',
1190
+ source: 'attribute',
1191
+ selector: 'img',
1192
+ attribute: 'src',
1193
+ role: 'content',
1194
+ },
1195
+ alt: {
1196
+ type: 'string',
1197
+ source: 'attribute',
1198
+ selector: 'img',
1199
+ attribute: 'alt',
1200
+ default: '',
1201
+ role: 'content',
1202
+ },
1203
+ caption: {
1204
+ type: 'rich-text',
1205
+ source: 'rich-text',
1206
+ selector: 'figcaption',
1207
+ role: 'content',
1208
+ },
1209
+ lightbox: {
1210
+ type: 'object',
1211
+ enabled: {
1212
+ type: 'boolean',
1213
+ },
1214
+ },
1215
+ title: {
1216
+ type: 'string',
1217
+ source: 'attribute',
1218
+ selector: 'img',
1219
+ attribute: 'title',
1220
+ role: 'content',
1221
+ },
1222
+ href: {
1223
+ type: 'string',
1224
+ source: 'attribute',
1225
+ selector: 'figure > a',
1226
+ attribute: 'href',
1227
+ role: 'content',
1228
+ },
1229
+ rel: {
1230
+ type: 'string',
1231
+ source: 'attribute',
1232
+ selector: 'figure > a',
1233
+ attribute: 'rel',
1234
+ },
1235
+ linkClass: {
1236
+ type: 'string',
1237
+ source: 'attribute',
1238
+ selector: 'figure > a',
1239
+ attribute: 'class',
1240
+ },
1241
+ id: {
1242
+ type: 'number',
1243
+ role: 'content',
1244
+ },
1245
+ width: {
1246
+ type: 'string',
1247
+ },
1248
+ height: {
1249
+ type: 'string',
1250
+ },
1251
+ aspectRatio: {
1252
+ type: 'string',
1253
+ },
1254
+ scale: {
1255
+ type: 'string',
1256
+ },
1257
+ focalPoint: {
1258
+ type: 'object',
1259
+ },
1260
+ sizeSlug: {
1261
+ type: 'string',
1262
+ },
1263
+ linkDestination: {
1264
+ type: 'string',
1265
+ },
1266
+ linkTarget: {
1267
+ type: 'string',
1268
+ source: 'attribute',
1269
+ selector: 'figure > a',
1270
+ attribute: 'target',
1271
+ },
1272
+ },
1273
+ supports: {
1274
+ interactivity: true,
1275
+ align: [ 'left', 'center', 'right', 'wide', 'full' ],
1276
+ anchor: true,
1277
+ color: {
1278
+ text: false,
1279
+ background: false,
1280
+ },
1281
+ filter: {
1282
+ duotone: true,
1283
+ },
1284
+ spacing: {
1285
+ margin: true,
1286
+ },
1287
+ __experimentalBorder: {
1288
+ color: true,
1289
+ radius: true,
1290
+ width: true,
1291
+ __experimentalSkipSerialization: true,
1292
+ __experimentalDefaultControls: {
1293
+ color: true,
1294
+ radius: true,
1295
+ width: true,
1296
+ },
1297
+ },
1298
+ shadow: {
1299
+ __experimentalSkipSerialization: true,
1300
+ },
1301
+ },
1302
+ save( { attributes } ) {
1303
+ const {
1304
+ url,
1305
+ alt,
1306
+ caption,
1307
+ align,
1308
+ href,
1309
+ rel,
1310
+ linkClass,
1311
+ width,
1312
+ height,
1313
+ aspectRatio,
1314
+ scale,
1315
+ focalPoint,
1316
+ id,
1317
+ linkTarget,
1318
+ sizeSlug,
1319
+ title,
1320
+ metadata: { bindings = {} } = {},
1321
+ } = attributes;
1322
+
1323
+ const newRel = ! rel ? undefined : rel;
1324
+ const borderProps = getBorderClassesAndStyles( attributes );
1325
+ const shadowProps = getShadowClassesAndStyles( attributes );
1326
+
1327
+ const classes = clsx( {
1328
+ alignnone: 'none' === align,
1329
+ [ `size-${ sizeSlug }` ]: sizeSlug,
1330
+ 'is-resized': width || height,
1331
+ 'has-custom-border':
1332
+ !! borderProps.className ||
1333
+ ( borderProps.style &&
1334
+ Object.keys( borderProps.style ).length > 0 ),
1335
+ } );
1336
+
1337
+ const imageClasses = clsx( borderProps.className, {
1338
+ [ `wp-image-${ id }` ]: !! id,
1339
+ } );
1340
+
1341
+ const image = (
1342
+ <img
1343
+ src={ url }
1344
+ alt={ alt }
1345
+ className={ imageClasses || undefined }
1346
+ style={ {
1347
+ ...borderProps.style,
1348
+ ...shadowProps.style,
1349
+ aspectRatio,
1350
+ objectFit: scale,
1351
+ objectPosition:
1352
+ focalPoint && scale
1353
+ ? mediaPosition( focalPoint )
1354
+ : undefined,
1355
+ width,
1356
+ height,
1357
+ } }
1358
+ title={ title }
1359
+ />
1360
+ );
1361
+
1362
+ const displayCaption =
1363
+ ! RichText.isEmpty( caption ) ||
1364
+ bindings.caption ||
1365
+ bindings?.__default?.source === 'core/pattern-overrides';
1366
+
1367
+ const figure = (
1368
+ <>
1369
+ { href ? (
1370
+ <a
1371
+ className={ linkClass }
1372
+ href={ href }
1373
+ target={ linkTarget }
1374
+ rel={ newRel }
1375
+ >
1376
+ { image }
1377
+ </a>
1378
+ ) : (
1379
+ image
1380
+ ) }
1381
+ { displayCaption && (
1382
+ <RichText.Content
1383
+ className={ __experimentalGetElementClassName(
1384
+ 'caption'
1385
+ ) }
1386
+ tagName="figcaption"
1387
+ value={ caption }
1388
+ />
1389
+ ) }
1390
+ </>
1391
+ );
1392
+
1393
+ return (
1394
+ <figure { ...useBlockProps.save( { className: classes } ) }>
1395
+ { figure }
1396
+ </figure>
1397
+ );
1398
+ },
1399
+ };
1400
+
1401
+ export default [ v9, v8, v7, v6, v5, v4, v3, v2, v1 ];
@@ -22,7 +22,7 @@ import {
22
22
  setFeaturedImage,
23
23
  } from '@wordpress/react-native-bridge';
24
24
  import {
25
- Icon,
25
+ Icon as WCIcon,
26
26
  PanelBody,
27
27
  ToolbarButton,
28
28
  ToolbarGroup,
@@ -514,7 +514,7 @@ export class ImageEdit extends Component {
514
514
 
515
515
  getPlaceholderIcon() {
516
516
  return (
517
- <Icon
517
+ <WCIcon
518
518
  icon={ placeholderIcon }
519
519
  { ...this.props.getStylesFromColorScheme(
520
520
  styles.iconPlaceholder,
@@ -9,6 +9,7 @@ import {
9
9
  Spinner,
10
10
  TextareaControl,
11
11
  TextControl,
12
+ CheckboxControl,
12
13
  ToolbarButton,
13
14
  ToolbarGroup,
14
15
  __experimentalToolsPanel as ToolsPanel,
@@ -295,6 +296,7 @@ export default function Image( {
295
296
  sizeSlug,
296
297
  lightbox,
297
298
  metadata,
299
+ isDecorative,
298
300
  } = attributes;
299
301
  const [ imageElement, setImageElement ] = useState();
300
302
  const [ resizeDelta, setResizeDelta ] = useState( null );
@@ -513,6 +515,7 @@ export default function Image( {
513
515
  if ( enable && ! lightboxSetting?.enabled ) {
514
516
  setAttributes( {
515
517
  lightbox: { enabled: true },
518
+ isDecorative: false,
516
519
  } );
517
520
  } else if ( ! enable && lightboxSetting?.enabled ) {
518
521
  setAttributes( {
@@ -551,6 +554,20 @@ export default function Image( {
551
554
  setAttributes( { alt: newAlt } );
552
555
  }
553
556
 
557
+ function updateIsDecorative( value ) {
558
+ setAttributes( {
559
+ isDecorative: value || undefined,
560
+ ...( value && {
561
+ alt: '',
562
+ caption: undefined,
563
+ href: undefined,
564
+ linkDestination: undefined,
565
+ linkTarget: undefined,
566
+ rel: undefined,
567
+ } ),
568
+ } );
569
+ }
570
+
554
571
  const imperativeFocalPointPreview = ( value ) => {
555
572
  if ( imageElement ) {
556
573
  imageElement.style.setProperty(
@@ -794,7 +811,8 @@ export default function Image( {
794
811
  isSingleSelected &&
795
812
  ! isEditingImage &&
796
813
  ! lockHrefControls &&
797
- ! lockUrlControls;
814
+ ! lockUrlControls &&
815
+ ! isDecorative;
798
816
 
799
817
  const showCoverControls =
800
818
  isSingleSelected && canInsertCover && ! isContentOnlyMode;
@@ -896,7 +914,10 @@ export default function Image( {
896
914
  <InspectorControls group="content">
897
915
  <ToolsPanel
898
916
  label={ __( 'Media' ) }
899
- resetAll={ () => onSelectImage( undefined ) }
917
+ resetAll={ () => {
918
+ onSelectImage( undefined );
919
+ setAttributes( { isDecorative: false } );
920
+ } }
900
921
  dropdownMenuProps={ dropdownMenuProps }
901
922
  >
902
923
  { ! lockUrlControls && (
@@ -926,24 +947,24 @@ export default function Image( {
926
947
  />
927
948
  </ToolsPanelItem>
928
949
  ) }
929
- <ToolsPanelItem
930
- label={ __( 'Alternative text' ) }
931
- isShownByDefault
932
- hasValue={ () => !! alt }
933
- onDeselect={ () =>
934
- setAttributes( { alt: undefined } )
935
- }
936
- >
937
- <TextareaControl
950
+ { ! isDecorative && (
951
+ <ToolsPanelItem
938
952
  label={ __( 'Alternative text' ) }
939
- value={ alt || '' }
940
- onChange={ updateAlt }
941
- readOnly={ lockAltControls }
942
- help={
943
- lockAltControls ? (
944
- <>{ lockAltControlsMessage }</>
945
- ) : (
946
- <>
953
+ isShownByDefault
954
+ hasValue={ () => !! alt }
955
+ onDeselect={ () =>
956
+ setAttributes( { alt: undefined } )
957
+ }
958
+ >
959
+ <TextareaControl
960
+ label={ __( 'Alternative text' ) }
961
+ value={ alt || '' }
962
+ onChange={ updateAlt }
963
+ readOnly={ lockAltControls }
964
+ help={
965
+ lockAltControls ? (
966
+ <>{ lockAltControlsMessage }</>
967
+ ) : (
947
968
  <ExternalLink
948
969
  href={
949
970
  // translators: Localized tutorial, if one exists. W3C Web Accessibility Initiative link has list of existing translations.
@@ -956,15 +977,31 @@ export default function Image( {
956
977
  'Describe the purpose of the image.'
957
978
  ) }
958
979
  </ExternalLink>
959
- <br />
960
- { __(
961
- 'Leave empty if decorative.'
962
- ) }
963
- </>
964
- )
980
+ )
981
+ }
982
+ />
983
+ </ToolsPanelItem>
984
+ ) }
985
+
986
+ { ! lockAltControls && ! lightboxChecked && (
987
+ <ToolsPanelItem
988
+ label={ __( 'Mark as decorative' ) }
989
+ isShownByDefault
990
+ hasValue={ () => !! isDecorative }
991
+ onDeselect={ () =>
992
+ setAttributes( { isDecorative: false } )
965
993
  }
966
- />
967
- </ToolsPanelItem>
994
+ >
995
+ <CheckboxControl
996
+ label={ __( 'Mark as decorative' ) }
997
+ checked={ !! isDecorative }
998
+ onChange={ updateIsDecorative }
999
+ help={ __(
1000
+ 'Hidden from assistive technologies.'
1001
+ ) }
1002
+ />
1003
+ </ToolsPanelItem>
1004
+ ) }
968
1005
  </ToolsPanel>
969
1006
  </InspectorControls>
970
1007
  ) }
@@ -1054,7 +1091,17 @@ export default function Image( {
1054
1091
  const filename = getFilename( url );
1055
1092
  let defaultedAlt;
1056
1093
 
1057
- if ( alt ) {
1094
+ if ( isDecorative ) {
1095
+ defaultedAlt = filename
1096
+ ? sprintf(
1097
+ /* translators: %s: file name */
1098
+ __(
1099
+ 'This image has been marked as decorative; its file name is %s'
1100
+ ),
1101
+ filename
1102
+ )
1103
+ : __( 'This image has been marked as decorative.' );
1104
+ } else if ( alt ) {
1058
1105
  defaultedAlt = alt;
1059
1106
  } else if ( filename ) {
1060
1107
  defaultedAlt = sprintf(
@@ -1102,7 +1149,33 @@ export default function Image( {
1102
1149
  height:
1103
1150
  pixelSize.height + resizeDelta.height,
1104
1151
  }
1105
- : { width, height } ),
1152
+ : ( () => {
1153
+ const style = {};
1154
+ if ( width === 'auto' ) {
1155
+ style.width = 'auto';
1156
+ } else if (
1157
+ width !== undefined &&
1158
+ width !== null
1159
+ ) {
1160
+ style.width =
1161
+ typeof width === 'number'
1162
+ ? `${ width }px`
1163
+ : width;
1164
+ }
1165
+ if (
1166
+ height === 'auto' ||
1167
+ height === undefined ||
1168
+ height === null
1169
+ ) {
1170
+ style.height = 'auto';
1171
+ } else {
1172
+ style.height =
1173
+ typeof height === 'number'
1174
+ ? `${ height }px`
1175
+ : height;
1176
+ }
1177
+ return style;
1178
+ } )() ),
1106
1179
  objectFit: scale,
1107
1180
  objectPosition:
1108
1181
  focalPoint && scale
@@ -1309,18 +1382,20 @@ export default function Image( {
1309
1382
  { img }
1310
1383
  { resizableBox }
1311
1384
 
1312
- <Caption
1313
- attributes={ attributes }
1314
- setAttributes={ setAttributes }
1315
- isSelected={ isSingleSelected }
1316
- insertBlocksAfter={ insertBlocksAfter }
1317
- label={ __( 'Image caption text' ) }
1318
- showToolbarButton={
1319
- isSingleSelected &&
1320
- ( hasNonContentControls || isContentOnlyMode ) &&
1321
- ! hideCaptionControls
1322
- }
1323
- />
1385
+ { ! isDecorative && (
1386
+ <Caption
1387
+ attributes={ attributes }
1388
+ setAttributes={ setAttributes }
1389
+ isSelected={ isSingleSelected }
1390
+ insertBlocksAfter={ insertBlocksAfter }
1391
+ label={ __( 'Image caption text' ) }
1392
+ showToolbarButton={
1393
+ isSingleSelected &&
1394
+ ( hasNonContentControls || isContentOnlyMode ) &&
1395
+ ! hideCaptionControls
1396
+ }
1397
+ />
1398
+ ) }
1324
1399
  </>
1325
1400
  );
1326
1401
  }
@@ -108,21 +108,40 @@ if ( window.__experimentalContentOnlyInspectorFields ) {
108
108
  rel: value.rel,
109
109
  linkTarget: value.linkTarget,
110
110
  } ),
111
+ isVisible: ( item ) => ! item.isDecorative,
111
112
  },
112
113
  {
113
114
  id: 'caption',
114
115
  label: __( 'Caption' ),
115
116
  type: 'text',
116
117
  Edit: 'rich-text', // TODO: replace with custom component
118
+ isVisible: ( item ) => ! item.isDecorative,
117
119
  },
118
120
  {
119
121
  id: 'alt',
120
122
  label: __( 'Alt text' ),
121
123
  type: 'text',
124
+ isVisible: ( item ) => ! item.isDecorative,
125
+ },
126
+ {
127
+ id: 'isDecorative',
128
+ label: __( 'Mark as decorative' ),
129
+ type: 'boolean',
130
+ setValue: ( { value } ) => ( {
131
+ isDecorative: value || undefined,
132
+ ...( value && {
133
+ alt: '',
134
+ caption: undefined,
135
+ href: undefined,
136
+ linkDestination: undefined,
137
+ linkTarget: undefined,
138
+ rel: undefined,
139
+ } ),
140
+ } ),
122
141
  },
123
142
  ];
124
143
  settings[ formKey ] = {
125
- fields: [ 'image', 'link', 'caption', 'alt' ],
144
+ fields: [ 'image', 'link', 'caption', 'alt', 'isDecorative' ],
126
145
  };
127
146
  }
128
147
 
@@ -250,7 +250,7 @@ function block_core_image_render_lightbox( $block_content, $block, $block_instan
250
250
  'galleryId' => $block_instance->context['galleryId'] ?? null,
251
251
  'customAriaLabel' => $custom_aria_label ?? null,
252
252
  'navigationButtonType' => $block_instance->context['navigationButtonType'] ?? 'icon',
253
- 'triggerButtonAriaLabel' => null,
253
+ 'triggerButtonAriaLabel' => __( 'Enlarge' ),
254
254
  ),
255
255
  ),
256
256
  )
package/src/image/save.js CHANGED
@@ -37,6 +37,7 @@ export default function save( { attributes } ) {
37
37
  linkTarget,
38
38
  sizeSlug,
39
39
  title,
40
+ isDecorative,
40
41
  metadata: { bindings = {} } = {},
41
42
  } = attributes;
42
43
 
@@ -65,19 +66,45 @@ export default function save( { attributes } ) {
65
66
  src={ url }
66
67
  alt={ alt }
67
68
  className={ imageClasses || undefined }
68
- style={ {
69
- ...borderProps.style,
70
- ...shadowProps.style,
71
- aspectRatio,
72
- objectFit: scale,
73
- objectPosition:
74
- focalPoint && scale
75
- ? mediaPosition( focalPoint )
76
- : undefined,
77
- width,
78
- height,
79
- } }
69
+ style={ ( () => {
70
+ const style = {
71
+ ...borderProps.style,
72
+ ...shadowProps.style,
73
+ aspectRatio,
74
+ objectFit: scale,
75
+ objectPosition:
76
+ focalPoint && scale
77
+ ? mediaPosition( focalPoint )
78
+ : undefined,
79
+ };
80
+ // Only apply dimension styles when a width or height is provided.
81
+ if ( width !== undefined || height !== undefined ) {
82
+ // Only apply width when explicitly provided.
83
+ if ( width === 'auto' ) {
84
+ style.width = 'auto';
85
+ } else if ( width !== undefined && width !== null ) {
86
+ style.width =
87
+ typeof width === 'number' ? `${ width }px` : width;
88
+ }
89
+ // Force height to auto when unspecified to prevent
90
+ // theme CSS from squishing the image.
91
+ if (
92
+ height === 'auto' ||
93
+ height === undefined ||
94
+ height === null
95
+ ) {
96
+ style.height = 'auto';
97
+ } else {
98
+ style.height =
99
+ typeof height === 'number'
100
+ ? `${ height }px`
101
+ : height;
102
+ }
103
+ }
104
+ return style;
105
+ } )() }
80
106
  title={ title }
107
+ role={ isDecorative ? 'none' : undefined }
81
108
  />
82
109
  );
83
110
 
@@ -317,6 +317,66 @@ describe( 'useOpenImageMediaEditorModal', () => {
317
317
  } );
318
318
  } );
319
319
 
320
+ it( 'updates back to the previous attachment from the original modal callback', async () => {
321
+ const originalAttachment = {
322
+ id: 1,
323
+ alt_text: '',
324
+ caption: { raw: '' },
325
+ };
326
+ const croppedAttachment = {
327
+ id: 2,
328
+ alt_text: '',
329
+ caption: { raw: '' },
330
+ };
331
+ const deferredAttachment = createDeferred();
332
+ const registry = createRegistry( {
333
+ getEntityRecord: ( kind, name, attachmentId ) =>
334
+ attachmentId === 1 ? originalAttachment : undefined,
335
+ resolveGetEntityRecord: ( kind, name, attachmentId ) =>
336
+ attachmentId === 2 ? deferredAttachment.promise : undefined,
337
+ } );
338
+ useRegistry.mockReturnValue( registry );
339
+ const setAttributes = jest.fn();
340
+ const openMediaEditorModal = jest.fn();
341
+ mockMediaEditorModalSetting( openMediaEditorModal );
342
+ const { result } = renderHook(
343
+ ( { attributes } ) =>
344
+ useOpenImageMediaEditorModal( { attributes, setAttributes } ),
345
+ {
346
+ initialProps: {
347
+ attributes: {
348
+ id: 1,
349
+ url: 'original.jpg',
350
+ alt: '',
351
+ caption: '',
352
+ },
353
+ },
354
+ }
355
+ );
356
+
357
+ await act( async () => {
358
+ await result.current();
359
+ } );
360
+ const onUpdate = openMediaEditorModal.mock.calls[ 0 ][ 0 ].onUpdate;
361
+ let updatePromise;
362
+ await act( async () => {
363
+ updatePromise = onUpdate( { id: 2, url: 'cropped.jpg' } );
364
+ } );
365
+ await act( async () => {
366
+ await onUpdate( { id: 1, url: 'original.jpg' } );
367
+ } );
368
+ await act( async () => {
369
+ deferredAttachment.resolve( croppedAttachment );
370
+ await updatePromise;
371
+ } );
372
+
373
+ expect( setAttributes ).toHaveBeenCalledTimes( 1 );
374
+ expect( setAttributes ).toHaveBeenCalledWith( {
375
+ id: 1,
376
+ url: 'original.jpg',
377
+ } );
378
+ } );
379
+
320
380
  it( 'resolves fresh metadata when the new attachment id has an incomplete cached record', async () => {
321
381
  const originalAttachment = {
322
382
  id: 1,