diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json
index 7c8b2c2715c99a..fad94a62e5a755 100644
--- a/packages/block-library/src/image/block.json
+++ b/packages/block-library/src/image/block.json
@@ -64,10 +64,10 @@
"__experimentalRole": "content"
},
"width": {
- "type": "number"
+ "type": "string"
},
"height": {
- "type": "number"
+ "type": "string"
},
"aspectRatio": {
"type": "string"
diff --git a/packages/block-library/src/image/deprecated.js b/packages/block-library/src/image/deprecated.js
index edbabd7fb2d835..d159009b173739 100644
--- a/packages/block-library/src/image/deprecated.js
+++ b/packages/block-library/src/image/deprecated.js
@@ -740,4 +740,211 @@ const v6 = {
},
};
-export default [ v6, v5, v4, v3, v2, v1 ];
+/**
+ * Deprecation for converting to string width and height block attributes and
+ * removing the width and height img element attributes which are not needed
+ * as they get added by the TODO hook.
+ *
+ * @see https://github.com/WordPress/gutenberg/pull/53274
+ */
+const v7 = {
+ attributes: {
+ align: {
+ type: 'string',
+ },
+ url: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'img',
+ attribute: 'src',
+ __experimentalRole: 'content',
+ },
+ alt: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'img',
+ attribute: 'alt',
+ default: '',
+ __experimentalRole: 'content',
+ },
+ caption: {
+ type: 'string',
+ source: 'html',
+ selector: 'figcaption',
+ __experimentalRole: 'content',
+ },
+ title: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'img',
+ attribute: 'title',
+ __experimentalRole: 'content',
+ },
+ href: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'figure > a',
+ attribute: 'href',
+ __experimentalRole: 'content',
+ },
+ rel: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'figure > a',
+ attribute: 'rel',
+ },
+ linkClass: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'figure > a',
+ attribute: 'class',
+ },
+ id: {
+ type: 'number',
+ __experimentalRole: 'content',
+ },
+ width: {
+ type: 'number',
+ },
+ height: {
+ type: 'number',
+ },
+ aspectRatio: {
+ type: 'string',
+ },
+ scale: {
+ type: 'string',
+ },
+ sizeSlug: {
+ type: 'string',
+ },
+ linkDestination: {
+ type: 'string',
+ },
+ linkTarget: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'figure > a',
+ attribute: 'target',
+ },
+ },
+ supports: {
+ anchor: true,
+ behaviors: {
+ lightbox: true,
+ },
+ color: {
+ text: false,
+ background: false,
+ },
+ filter: {
+ duotone: true,
+ },
+ __experimentalBorder: {
+ color: true,
+ radius: true,
+ width: true,
+ __experimentalSkipSerialization: true,
+ __experimentalDefaultControls: {
+ color: true,
+ radius: true,
+ width: true,
+ },
+ },
+ },
+ migrate( { width, height, ...attributes } ) {
+ return {
+ ...attributes,
+ width: `${ width }px`,
+ height: `${ height }px`,
+ };
+ },
+ save( { attributes } ) {
+ const {
+ url,
+ alt,
+ caption,
+ align,
+ href,
+ rel,
+ linkClass,
+ width,
+ height,
+ aspectRatio,
+ scale,
+ id,
+ linkTarget,
+ sizeSlug,
+ title,
+ } = attributes;
+
+ const newRel = ! rel ? undefined : rel;
+ const borderProps = getBorderClassesAndStyles( attributes );
+
+ const classes = classnames( {
+ [ `align${ align }` ]: align,
+ [ `size-${ sizeSlug }` ]: sizeSlug,
+ 'is-resized': width || height,
+ 'has-custom-border':
+ !! borderProps.className ||
+ ( borderProps.style &&
+ Object.keys( borderProps.style ).length > 0 ),
+ } );
+
+ const imageClasses = classnames( borderProps.className, {
+ [ `wp-image-${ id }` ]: !! id,
+ } );
+
+ const image = (
+
+ );
+
+ const figure = (
+ <>
+ { href ? (
+
+ { image }
+
+ ) : (
+ image
+ ) }
+ { ! RichText.isEmpty( caption ) && (
+
+ ) }
+ >
+ );
+
+ return (
+
+ );
+ },
+};
+
+export default [ v7, v6, v5, v4, v3, v2, v1 ];
diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js
index 5fe5c93f4199b4..a880c20ed59ab9 100644
--- a/packages/block-library/src/image/image.js
+++ b/packages/block-library/src/image/image.js
@@ -114,6 +114,11 @@ export default function Image( {
linkTarget,
sizeSlug,
} = attributes;
+
+ // The only supported unit is px, so we can parseInt to strip the px here.
+ const numericWidth = width ? parseInt( width, 10 ) : undefined;
+ const numericHeight = height ? parseInt( height, 10 ) : undefined;
+
const imageRef = useRef();
const prevCaption = usePrevious( caption );
const [ showCaption, setShowCaption ] = useState( !! caption );
@@ -473,23 +478,14 @@ export default function Image( {
) }
{
// Rebuilding the object forces setting `undefined`
// for values that are removed since setAttributes
// doesn't do anything with keys that aren't set.
setAttributes( {
- width:
- newValue.width &&
- parseInt( newValue.width, 10 ),
- height:
- newValue.height &&
- parseInt( newValue.height, 10 ),
+ width: newValue.width,
+ height: newValue.height,
scale: newValue.scale,
aspectRatio: newValue.aspectRatio,
} );
@@ -587,8 +583,8 @@ export default function Image( {
{ img };
} else {
+ const numericRatio = aspectRatio && evalAspectRatio( aspectRatio );
+ const customRatio = numericWidth / numericHeight;
const ratio =
- ( aspectRatio && evalAspectRatio( aspectRatio ) ) ||
- ( width && height && width / height ) ||
- naturalWidth / naturalHeight ||
- 1;
-
- const currentWidth = ! width && height ? height * ratio : width;
- const currentHeight = ! height && width ? width / ratio : height;
+ numericRatio || customRatio || naturalWidth / naturalHeight || 1;
+ const currentWidth =
+ ! numericWidth && numericHeight
+ ? numericHeight * ratio
+ : numericWidth;
+ const currentHeight =
+ ! numericHeight && numericWidth
+ ? numericWidth / ratio
+ : numericHeight;
const minWidth =
naturalWidth < naturalHeight ? MIN_SIZE : MIN_SIZE * ratio;
@@ -687,10 +687,14 @@ export default function Image( {
onResizeStart={ onResizeStart }
onResizeStop={ ( event, direction, elt ) => {
onResizeStop();
+ // Since the aspect ratio is locked when resizing, we can
+ // use the width of the resized element to calculate the
+ // height in CSS to prevent stretching when the max-width
+ // is reached.
setAttributes( {
- width: elt.offsetWidth,
- height: elt.offsetHeight,
- aspectRatio: undefined,
+ width: `${ elt.offsetWidth }px`,
+ height: 'auto',
+ aspectRatio: `${ ratio }`,
} );
} }
resizeRatio={ align === 'center' ? 2 : 1 }
diff --git a/packages/block-library/src/image/save.js b/packages/block-library/src/image/save.js
index 6fa8c6b2342f32..81565af09ababf 100644
--- a/packages/block-library/src/image/save.js
+++ b/packages/block-library/src/image/save.js
@@ -61,8 +61,6 @@ export default function save( { attributes } ) {
width,
height,
} }
- width={ width }
- height={ height }
title={ title }
/>
);
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html
index 7ce56e11fa75e9..9a66da6c018989 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html
@@ -1,3 +1,3 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html
index 7ce56e11fa75e9..9a66da6c018989 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html
@@ -1,3 +1,3 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html
index cfdc52e3cbb6ea..99da2155bce88f 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html
@@ -1,3 +1,3 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html
index ad70fe65f35827..807ba3abc9f9ce 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html
@@ -1,3 +1,3 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html
new file mode 100644
index 00000000000000..7210aee3564b5f
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json
new file mode 100644
index 00000000000000..b49c515f8e5ab6
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json
@@ -0,0 +1,15 @@
+[
+ {
+ "name": "core/image",
+ "isValid": true,
+ "attributes": {
+ "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
+ "alt": "",
+ "caption": "",
+ "sizeSlug": "large",
+ "width": "164px",
+ "height": "164px"
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json
new file mode 100644
index 00000000000000..fbd8627807b3da
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json
@@ -0,0 +1,15 @@
+[
+ {
+ "blockName": "core/image",
+ "attrs": {
+ "width": 164,
+ "height": 164,
+ "sizeSlug": "large"
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n\n",
+ "innerContent": [
+ "\n\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html
new file mode 100644
index 00000000000000..5392d261af89d6
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html
@@ -0,0 +1,3 @@
+
+
+