From db5fd3a72e24e621356eb0a9dae64774f65efd8b Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Mon, 24 Nov 2025 13:21:48 +0100 Subject: [PATCH 1/7] Only include the original image in the Image component if the image does not have all the scales present. Fixed and update tests. --- .../manage/Blocks/Image/ImageSidebar.test.jsx | 1 + .../LeadImage/LeadImageSidebar.test.jsx | 1 + .../TemplateChooser/TemplateChooser.test.jsx | 1 + .../manage/Toolbar/PersonalTools.test.jsx | 15 + .../Widgets/RegistryImageWidget.test.jsx | 1 + .../src/components/theme/Image/Image.jsx | 19 +- .../src/components/theme/Image/Image.test.jsx | 393 +++++++++++------- .../Image/__snapshots__/Image.test.jsx.snap | 74 ---- .../theme/Widgets/ImageWidget.test.jsx | 42 +- pnpm-lock.yaml | 2 +- 10 files changed, 312 insertions(+), 237 deletions(-) delete mode 100644 packages/volto/src/components/theme/Image/__snapshots__/Image.test.jsx.snap diff --git a/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.test.jsx b/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.test.jsx index 7ff76617814..c4f7acb7ac8 100644 --- a/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.test.jsx +++ b/packages/volto/src/components/manage/Blocks/Image/ImageSidebar.test.jsx @@ -15,6 +15,7 @@ it('renders an Image Block Sidebar component', () => { create: {}, data: {}, }, + site: { data: { 'plone.image_scales': { preview: {}, listing: {} } } }, intl: { locale: 'en', messages: {}, diff --git a/packages/volto/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx b/packages/volto/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx index 477ebd89b50..04d0c652791 100644 --- a/packages/volto/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +++ b/packages/volto/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx @@ -16,6 +16,7 @@ test('renders a Lead Image block Sidebar component', () => { locale: 'en', messages: {}, }, + site: { data: { 'plone.image_scales': { preview: {}, listing: {} } } }, }); const component = renderer.create( diff --git a/packages/volto/src/components/manage/TemplateChooser/TemplateChooser.test.jsx b/packages/volto/src/components/manage/TemplateChooser/TemplateChooser.test.jsx index 577124a2c08..b2f436823ee 100644 --- a/packages/volto/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +++ b/packages/volto/src/components/manage/TemplateChooser/TemplateChooser.test.jsx @@ -13,6 +13,7 @@ test('renders a TemplateChooser component', () => { locale: 'en', messages: { templateid: 'Template default translation' }, }, + site: { data: { 'plone.image_scales': { preview: {}, listing: {} } } }, }); const component = renderer.create( diff --git a/packages/volto/src/components/manage/Toolbar/PersonalTools.test.jsx b/packages/volto/src/components/manage/Toolbar/PersonalTools.test.jsx index 4dd3122e7c7..72dcff0d0cd 100644 --- a/packages/volto/src/components/manage/Toolbar/PersonalTools.test.jsx +++ b/packages/volto/src/components/manage/Toolbar/PersonalTools.test.jsx @@ -23,6 +23,11 @@ describe('Toolbar Personal Tools component', () => { userSession: { token: jwt.sign({ sub: 'admin' }, 'secret'), }, + site: { + data: { + 'plone.image_scales': { preview: {}, listing: {} }, + }, + }, content: { data: { '@type': 'Folder', @@ -74,6 +79,11 @@ describe('Toolbar Personal Tools component', () => { userSession: { token: jwt.sign({ sub: 'admin' }, 'secret'), }, + site: { + data: { + 'plone.image_scales': { preview: {}, listing: {} }, + }, + }, content: { data: { '@type': 'Folder', @@ -126,6 +136,11 @@ describe('Toolbar Personal Tools component', () => { userSession: { token: jwt.sign({ sub: 'admin' }, 'secret'), }, + site: { + data: { + 'plone.image_scales': { preview: {}, listing: {} }, + }, + }, content: { data: { '@type': 'Folder', diff --git a/packages/volto/src/components/manage/Widgets/RegistryImageWidget.test.jsx b/packages/volto/src/components/manage/Widgets/RegistryImageWidget.test.jsx index 60d8b6198ac..df560c206a4 100644 --- a/packages/volto/src/components/manage/Widgets/RegistryImageWidget.test.jsx +++ b/packages/volto/src/components/manage/Widgets/RegistryImageWidget.test.jsx @@ -20,6 +20,7 @@ const createStore = () => locale: 'en', messages: {}, }, + site: { data: { 'plone.image_scales': { preview: {}, listing: {} } } }, }); describe('RegistryImageWidget', () => { diff --git a/packages/volto/src/components/theme/Image/Image.jsx b/packages/volto/src/components/theme/Image/Image.jsx index d454d476aa1..9415a61d722 100644 --- a/packages/volto/src/components/theme/Image/Image.jsx +++ b/packages/volto/src/components/theme/Image/Image.jsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import cx from 'classnames'; +import { useSelector } from 'react-redux'; import { flattenToAppURL, flattenScales, @@ -26,6 +27,9 @@ export default function Image({ className = '', ...imageProps }) { + const site = useSelector((state) => state.site.data); + const siteImageScales = site?.['plone.image_scales'] || {}; + if (!item && !src) return null; // TypeScript hints for editor autocomplete :) @@ -61,11 +65,16 @@ export default function Image({ if (!isSvg && image.scales && Object.keys(image.scales).length > 0) { const sortedScales = Object.values({ ...image.scales, - original: { - download: `${image.download}`, - width: image.width, - height: image.height, - }, + ...(Object.keys(siteImageScales).length > + Object.keys(image.scales).length + ? { + original: { + download: `${image.download}`, + width: image.width, + height: image.height, + }, + } + : {}), }).sort((a, b) => { if (a.width > b.width) return 1; else if (a.width < b.width) return -1; diff --git a/packages/volto/src/components/theme/Image/Image.test.jsx b/packages/volto/src/components/theme/Image/Image.test.jsx index b45d08140cf..1d6a09b6abf 100644 --- a/packages/volto/src/components/theme/Image/Image.test.jsx +++ b/packages/volto/src/components/theme/Image/Image.test.jsx @@ -1,165 +1,266 @@ import React from 'react'; -import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; +import { render, screen } from '@testing-library/react'; + import Image from './Image'; -test('renders an image component with fetchpriority high', () => { - const component = renderer.create( - alt text, - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); -}); + }, + ], + }, +}; -test('renders an image component with lazy loading', () => { - const component = renderer.create( - alt text, + }, + ], + }, +}; + +const renderImage = (props, { siteData = defaultSiteData } = {}) => { + const store = mockStore({ + intl: { locale: 'en', messages: {} }, + site: { data: siteData }, + }); + + return render( + + + , ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); -}); +}; + +describe('Image', () => { + it('renders an image component with fetchpriority high', () => { + renderImage({ item: itemWithImage, imageField: 'image', alt: 'alt text' }); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute('fetchpriority', 'high'); + expect(img).toHaveAttribute('width', '1080'); + expect(img).toHaveAttribute('height', '920'); + expect(img).toHaveAttribute('src', '/image/@@images/image.png'); + expect(img).toHaveAttribute( + 'srcset', + '/image/@@images/image-400.png 400w, /image/@@images/image.png 1080w', + ); + }); -test('renders an image component with responsive class', () => { - const component = renderer.create( - { + renderImage({ + item: itemWithImage, + imageField: 'image', + alt: 'alt text', + loading: 'lazy', + }); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute('loading', 'lazy'); + expect(img).toHaveAttribute('decoding', 'async'); + expect(img).not.toHaveAttribute('fetchpriority'); + }); + + it('renders an image component with responsive class', () => { + renderImage({ + item: { + ...itemWithImage, image: { + ...itemWithImage.image, download: 'http://localhost:3000/image/@@images/image-1200.png', - width: 400, - height: 400, - scales: { - preview: { - download: 'http://localhost:3000/image/@@images/image-400.png', - width: 400, - height: 400, - }, - }, }, - }} - imageField="image" - alt="alt text" - responsive={true} - />, - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); -}); + }, + imageField: 'image', + alt: 'alt text', + responsive: true, + }); -test('renders an image component from a catalog brain', () => { - const component = renderer.create( - alt text, - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); -}); + const img = screen.getByAltText('alt text'); + + expect(img).toHaveClass('responsive'); + expect(img).toHaveAttribute('fetchpriority', 'high'); + }); + + it('renders an image component from a catalog brain', () => { + renderImage({ item: catalogBrain, imageField: 'image', alt: 'alt text' }); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute('src', '/image/@@images/image.png'); + expect(img).toHaveAttribute( + 'srcset', + '/image/@@images/image-400.png 400w, /image/@@images/image.png 400w', + ); + }); -test('renders an image component from a catalog brain using `preview_image_link`', () => { - const component = renderer.create( - { + renderImage({ + item: previewImageLink, + imageField: 'preview_image_link', + alt: 'alt text', + }); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute('src', '/image.png/@@images/image.png'); + expect(img).toHaveAttribute( + 'srcset', + '/image.png/@@images/image-400.png 400w, /image.png/@@images/image.png 400w', + ); + }); + + it('includes the original scale in srcset when site defines more scales than the image', () => { + renderImage( + { + item: previewImageLink, + imageField: 'preview_image_link', + alt: 'alt text', + }, + { siteData: { 'plone.image_scales': { preview: {}, listing: {} } } }, + ); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute( + 'srcset', + '/image.png/@@images/image-400.png 400w, /image.png/@@images/image.png 400w', + ); + }); + + it('does not include the original scale when site scales are not greater than the image scales', () => { + renderImage( + { + item: previewImageLink, + imageField: 'preview_image_link', + alt: 'alt text', + }, + { siteData: { 'plone.image_scales': { preview: {} } } }, + ); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute( + 'srcset', + '/image.png/@@images/image-400.png 400w', + ); + }); + + it('does not render when no item or src is provided', () => { + const { container } = renderImage( + { alt: 'missing image' }, + { siteData: {} }, + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('renders an svg image without srcset', () => { + renderImage({ + item: { + ...itemWithImage, + image: { + ...itemWithImage.image, + 'content-type': 'image/svg+xml', }, - }} - imageField="preview_image_link" - alt="alt text" - />, - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); -}); + }, + imageField: 'image', + alt: 'svg image', + }); -test('renders an image component from a string src', () => { - const component = renderer.create( - alt text, - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); -}); + const img = screen.getByAltText('svg image'); -test('should not render empty class attribute in img tag', () => { - const component = renderer.create( - no class attribute, - ); - const json = component.toJSON(); - expect(json).toMatchSnapshot(); + expect(img).not.toHaveAttribute('srcset'); + expect(img).toHaveAttribute('src', '/image/@@images/image.png'); + }); + + it('merges custom className with responsive flag', () => { + renderImage({ + item: itemWithImage, + imageField: 'image', + alt: 'with class', + responsive: true, + className: 'custom-class', + }); + + const img = screen.getByAltText('with class'); + + expect(img).toHaveClass('custom-class'); + expect(img).toHaveClass('responsive'); + }); + + it('renders an image component from a string src', () => { + renderImage({ + src: 'http://localhost:3000/image/@@images/image/image.png', + alt: 'alt text', + }); + + const img = screen.getByAltText('alt text'); + + expect(img).toHaveAttribute( + 'src', + 'http://localhost:3000/image/@@images/image/image.png', + ); + expect(img).toHaveAttribute('fetchpriority', 'high'); + }); + + it('should not render empty class attribute in img tag', () => { + renderImage({ src: '/image.png', alt: 'no class attribute' }); + + const img = screen.getByAltText('no class attribute'); + + expect(img).not.toHaveAttribute('class'); + }); }); diff --git a/packages/volto/src/components/theme/Image/__snapshots__/Image.test.jsx.snap b/packages/volto/src/components/theme/Image/__snapshots__/Image.test.jsx.snap deleted file mode 100644 index 251b073417f..00000000000 --- a/packages/volto/src/components/theme/Image/__snapshots__/Image.test.jsx.snap +++ /dev/null @@ -1,74 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`renders an image component from a catalog brain 1`] = ` -alt text -`; - -exports[`renders an image component from a catalog brain using \`preview_image_link\` 1`] = ` -alt text -`; - -exports[`renders an image component from a string src 1`] = ` -alt text -`; - -exports[`renders an image component with fetchpriority high 1`] = ` -alt text -`; - -exports[`renders an image component with lazy loading 1`] = ` -alt text -`; - -exports[`renders an image component with responsive class 1`] = ` -alt text -`; - -exports[`should not render empty class attribute in img tag 1`] = ` -no class attribute -`; diff --git a/packages/volto/src/components/theme/Widgets/ImageWidget.test.jsx b/packages/volto/src/components/theme/Widgets/ImageWidget.test.jsx index c98fd13ffb5..61a9d442ab6 100644 --- a/packages/volto/src/components/theme/Widgets/ImageWidget.test.jsx +++ b/packages/volto/src/components/theme/Widgets/ImageWidget.test.jsx @@ -1,17 +1,35 @@ import React from 'react'; import renderer from 'react-test-renderer'; import ImageWidget from './ImageWidget'; +import { Provider } from 'react-intl-redux'; +import configureStore from 'redux-mock-store'; + +const mockStore = configureStore(); describe('ImageWidget', () => { + const store = mockStore({ + intl: { locale: 'en', messages: {} }, + site: { data: { 'plone.image_scales': { preview: {}, listing: {} } } }, + }); + it('renders an empty image view widget component', () => { - const component = renderer.create(); + const component = renderer.create( + + + , + ); const json = component.toJSON(); expect(json).toMatchSnapshot(); }); it('renders an image view widget component', () => { const component = renderer.create( - , + + + , ); const json = component.toJSON(); expect(json).toMatchSnapshot(); @@ -19,15 +37,17 @@ describe('ImageWidget', () => { it('renders an image view widget component with children', () => { const component = renderer.create( - - {(child) => {child}} - , + + + {(child) => {child}} + + , ); const json = component.toJSON(); expect(json).toMatchSnapshot(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1b666ac86d..13e34a9fd69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21008,7 +21008,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@24.9.1)(@vitest/ui@2.1.9)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.30.1)(sass@1.91.0)(terser@5.43.1) + vitest: 2.1.9(@types/node@24.9.1)(@vitest/ui@2.1.9)(jsdom@21.1.2)(less@3.11.1)(lightningcss@1.30.1)(sass@1.91.0)(terser@5.43.1) optional: true '@vitest/ui@2.1.9(vitest@3.2.4)': From 7a5460da7843577ab592f82603ba646183b518e1 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Mon, 24 Nov 2025 13:33:43 +0100 Subject: [PATCH 2/7] Documentation and changelog --- docs/source/upgrade-guide/index.md | 12 ++++++++++++ packages/volto/news/7655.bugfix | 1 + 2 files changed, 13 insertions(+) create mode 100644 packages/volto/news/7655.bugfix diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index 160ec05cf1e..d2305879eb9 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -333,6 +333,18 @@ We pinned the version of `sass` to `1.32.0`, which is the one before they introd It is unlikely that using this version will cause problems since no real new features were added in later versions that are relevant for Volto developers. In case that you need a later version of `sass` in your project or add-on, you can override it in your project's {file}`package.json` file. +### The image component now includes the original image only if necessary +```{versionadded} Volto 19.0.0-alpha.18 +``` + +The `Image` component has been optimized to include the original image URL only when necessary. +Now it is only included if the image does not have all the defined scales present, which could happen if the image is smaller than the defined scales. +In other scenarios where all the scales are present, including the original image forced the browser to choose it over the scaled versions, impacting performance. +This happened specially in high-density screens where the `large` scale was not enough for the browser to pick a scaled version. +It is a breaking change for projects that relied on the original image always being present, for example, those projects that wanted to use the original image always in big screens (not necessarily high-density, eg TV screens, etc). +We are adding an additional scale to cover that use cases, enough to cover the highest density screens at the largest common resolutions. +This is a breaking change, because if your project relied on the original image being always present, you need to add an additional scale to cover your use case, or run the upgrade steps defined in `plone.volto>=6.0.0a0` or in Plone 6.2 to use the new image scale named `humongous` + (upgrading-to-volto-18-x-x)= ## Upgrading to Volto 18.x.x diff --git a/packages/volto/news/7655.bugfix b/packages/volto/news/7655.bugfix new file mode 100644 index 00000000000..7362a46a0b8 --- /dev/null +++ b/packages/volto/news/7655.bugfix @@ -0,0 +1 @@ +Only include the original image in the Image component if the image does not have all the scales present. Fixed and update tests. @sneridagh From 1f22a5d778b36ca37dabc5e67ee8318e377e7e96 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Mon, 24 Nov 2025 16:55:17 +0100 Subject: [PATCH 3/7] Add sizes to Image and Teaser --- .../volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx | 1 + packages/volto/src/components/theme/View/ImageView.jsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx index f1d601b78df..c7c34defce9 100644 --- a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx +++ b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx @@ -64,6 +64,7 @@ const TeaserDefaultTemplate = (props) => { alt="" loading="lazy" responsive={true} + sizes="(max-width: 940px) 100vw, 940px" /> ) diff --git a/packages/volto/src/components/theme/View/ImageView.jsx b/packages/volto/src/components/theme/View/ImageView.jsx index 2144b359d86..2542f4f3685 100644 --- a/packages/volto/src/components/theme/View/ImageView.jsx +++ b/packages/volto/src/components/theme/View/ImageView.jsx @@ -38,6 +38,7 @@ const ImageView = ({ content }) => { imageField="image" alt={content.title} responsive={true} + sizes="(max-width: 940px) 100vw, 940px" />
Date: Tue, 25 Nov 2025 09:41:30 +0100 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Steve Piercy --- docs/source/upgrade-guide/index.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index d2305879eb9..b78e0c0d97a 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -340,10 +340,12 @@ In case that you need a later version of `sass` in your project or add-on, you c The `Image` component has been optimized to include the original image URL only when necessary. Now it is only included if the image does not have all the defined scales present, which could happen if the image is smaller than the defined scales. In other scenarios where all the scales are present, including the original image forced the browser to choose it over the scaled versions, impacting performance. -This happened specially in high-density screens where the `large` scale was not enough for the browser to pick a scaled version. -It is a breaking change for projects that relied on the original image always being present, for example, those projects that wanted to use the original image always in big screens (not necessarily high-density, eg TV screens, etc). -We are adding an additional scale to cover that use cases, enough to cover the highest density screens at the largest common resolutions. -This is a breaking change, because if your project relied on the original image being always present, you need to add an additional scale to cover your use case, or run the upgrade steps defined in `plone.volto>=6.0.0a0` or in Plone 6.2 to use the new image scale named `humongous` +This happened especially in high-density resolution screens where the `large` scale was not enough for the browser to pick a scaled version. + +This is a breaking change for projects that relied on the original image always being present, for example, in those projects where the original image was always included for large displays, such as televisions or wide-screen displays. +An additional scale was added to cover those use cases, enough to cover the highest density screens at the largest common resolutions. +Additionally, if your project relied on the original image to always be present, then you need to either add an additional scale to cover your use case, run the upgrade steps defined in `plone.volto>=6.0.0a0`, or, in Plone 6.2, to use the new image scale named `humongous`. + (upgrading-to-volto-18-x-x)= From 9f6b2b31dd8fb81984ba1564ca2c6e52c0e17845 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 26 Nov 2025 09:14:48 +0100 Subject: [PATCH 5/7] Fix linkcheck --- .github/workflows/readme-link-check.yml | 1 + docs/source/conf.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/readme-link-check.yml b/.github/workflows/readme-link-check.yml index c00124272f7..4b77d0e8026 100644 --- a/.github/workflows/readme-link-check.yml +++ b/.github/workflows/readme-link-check.yml @@ -26,5 +26,6 @@ jobs: --exclude https://my-server-DNS-name.tld/api --exclude 2021.ploneconf.org --exclude https://www.lanku.eus/ + --exclude https://medium.com/ '**/README.md' 'PACKAGES.md' diff --git a/docs/source/conf.py b/docs/source/conf.py index ba61efbd13f..466b3440234 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -94,6 +94,7 @@ r"https://browsersl.ist/#", r"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors#Identifying_the_issue", r"https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0", + r"https://medium.com", ] linkcheck_anchors = True linkcheck_timeout = 5 From b2f8cb42172756cd2a2b6e2e9818d9294346ce15 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 26 Nov 2025 09:57:36 +0100 Subject: [PATCH 6/7] Use auto whenever possible, check all Image instances --- .../src/components/manage/Blocks/Teaser/DefaultBody.jsx | 2 +- .../volto/src/components/manage/Widgets/ImageWidget.jsx | 8 +++++++- packages/volto/src/components/theme/View/ImageView.jsx | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx index c7c34defce9..e057d5efb04 100644 --- a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx +++ b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx @@ -64,7 +64,7 @@ const TeaserDefaultTemplate = (props) => { alt="" loading="lazy" responsive={true} - sizes="(max-width: 940px) 100vw, 940px" + sizes="auto, (max-width: 940px) 100vw, 940px" /> ) diff --git a/packages/volto/src/components/manage/Widgets/ImageWidget.jsx b/packages/volto/src/components/manage/Widgets/ImageWidget.jsx index 8abd5cf9857..7bd422ff6b6 100644 --- a/packages/volto/src/components/manage/Widgets/ImageWidget.jsx +++ b/packages/volto/src/components/manage/Widgets/ImageWidget.jsx @@ -296,7 +296,13 @@ const UnconnectedImageInput = (props) => { {selected && } {/* If it's relation choice (preview_image_link) */} {isRelationChoice ? ( - + ) : ( { imageField="image" alt={content.title} responsive={true} - sizes="(max-width: 940px) 100vw, 940px" + sizes="auto, (max-width: 940px) 100vw, 940px" />
Date: Wed, 26 Nov 2025 10:01:50 +0100 Subject: [PATCH 7/7] Amended docs --- docs/source/upgrade-guide/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index b78e0c0d97a..ddf5db45007 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -338,13 +338,13 @@ In case that you need a later version of `sass` in your project or add-on, you c ``` The `Image` component has been optimized to include the original image URL only when necessary. -Now it is only included if the image does not have all the defined scales present, which could happen if the image is smaller than the defined scales. -In other scenarios where all the scales are present, including the original image forced the browser to choose it over the scaled versions, impacting performance. -This happened especially in high-density resolution screens where the `large` scale was not enough for the browser to pick a scaled version. +Now it is only included if the image does not have all the defined scales present, which could happen if the image uploaded originally is smaller than the defined scales. +In other scenarios where all the scales are present, including the original image could lead the browser to choose it over the scaled versions, impacting performance. +This happened especially in high-density resolution screens where the largest scale available was not enough for the browser to pick a scaled version. This is a breaking change for projects that relied on the original image always being present, for example, in those projects where the original image was always included for large displays, such as televisions or wide-screen displays. -An additional scale was added to cover those use cases, enough to cover the highest density screens at the largest common resolutions. -Additionally, if your project relied on the original image to always be present, then you need to either add an additional scale to cover your use case, run the upgrade steps defined in `plone.volto>=6.0.0a0`, or, in Plone 6.2, to use the new image scale named `humongous`. +A pair of additional scales were added to cover those use cases, enough to cover the highest density screens at the largest common resolutions. +Additionally, if your project relied on the original image to always be present, then you need to either add an additional scale to cover your use case, run the upgrade steps defined in `plone.volto>=6.0.0a0`, or, in Plone 6.2, to use the new image scales named `2k` and `4k`. (upgrading-to-volto-18-x-x)=