diff --git a/src/PreviewImage.js b/src/PreviewImage.js index 8f56fc83..4bb23f6b 100644 --- a/src/PreviewImage.js +++ b/src/PreviewImage.js @@ -1,16 +1,11 @@ // TODO: see if possible to replace with Volto's PreviewImage component import React from 'react'; +import { getImageScaleParams } from '@eeacms/volto-listing-block/helpers'; import { Image, Label } from 'semantic-ui-react'; -import { flattenToAppURL } from '@plone/volto/helpers'; import DefaultImageSVG from './default-image.svg'; -const getSrc = (item, size) => - flattenToAppURL( - `${item['@id']}/@@images/${item.image_field || 'image'}/${size}`, - ); - // TODO: do we still need volto-depiction compatibility? // import DefaultImageSVG from '@plone/volto/components/manage/Blocks/Listing/default-image.svg'; // const makeImage = (item, style) => ( @@ -42,10 +37,10 @@ function PreviewImage(props) { label, ...rest } = props; - const src = preview_image?.[0] - ? getSrc(preview_image[0], size) - : item.image_field - ? getSrc(item, size) + const params = preview_image?.[0] + ? getImageScaleParams(preview_image?.[0], size) + : item + ? getImageScaleParams(item, size) : DefaultImageSVG; return ( @@ -55,7 +50,13 @@ function PreviewImage(props) { {label.text} ) : null} - {item.title} + {item.title} ); } diff --git a/src/blocks/Item/Edit.jsx b/src/blocks/Item/Edit.jsx index d84a5c0d..d6ab244e 100644 --- a/src/blocks/Item/Edit.jsx +++ b/src/blocks/Item/Edit.jsx @@ -27,6 +27,7 @@ const Edit = (props) => { properties, onChangeBlock, onSelectBlock, + id, } = props; const { description } = data; const schema = React.useMemo(() => getSchema(props), [props]); @@ -47,7 +48,7 @@ const Edit = (props) => { return ( <> - + {assetType === 'image' && image && ( )} {assetType === 'icon' && icon && ( diff --git a/src/blocks/Item/View.jsx b/src/blocks/Item/View.jsx index 9357ba61..29855e41 100644 --- a/src/blocks/Item/View.jsx +++ b/src/blocks/Item/View.jsx @@ -9,10 +9,11 @@ export const serializeText = (text) => { }; const View = (props) => { - const { data = {} } = props; + const { data = {}, id } = props; return ( ); diff --git a/src/blocks/Item/schema.js b/src/blocks/Item/schema.js index 89c750e3..696eda8a 100644 --- a/src/blocks/Item/schema.js +++ b/src/blocks/Item/schema.js @@ -35,6 +35,7 @@ const ItemBlockSchema = ({ data }) => { image: { title: 'Image', widget: 'attachedimage', + selectedItemAttrs: ['image_field', 'image_scales', '@type'], }, imageSize: { title: 'Image size', diff --git a/src/blocks/Listing/item-templates/ItemTemplates.test.jsx b/src/blocks/Listing/item-templates/ItemTemplates.test.jsx index ccdc9ab0..90ce7207 100644 --- a/src/blocks/Listing/item-templates/ItemTemplates.test.jsx +++ b/src/blocks/Listing/item-templates/ItemTemplates.test.jsx @@ -14,6 +14,7 @@ describe('ItemTemplates', () => { description: 'Default listing description', meta: 'Default listing meta', EffectiveDate: '2023-10-05T08:21:00+02:00', + url: '/my-item-url', }; const itemModel = { diff --git a/src/blocks/Listing/item-templates/SearchItemTemplate.test.jsx b/src/blocks/Listing/item-templates/SearchItemTemplate.test.jsx index 71520fac..3e218d12 100644 --- a/src/blocks/Listing/item-templates/SearchItemTemplate.test.jsx +++ b/src/blocks/Listing/item-templates/SearchItemTemplate.test.jsx @@ -2,12 +2,13 @@ import React from 'react'; import renderer from 'react-test-renderer'; import { SearchItemLayout } from './SearchItemTemplate'; -describe('SimpleItemTemplates', () => { +describe('SearchItemTemplates', () => { it('renders correctly', () => { const item = { title: 'Search listing title', description: 'Search listing description', meta: 'Search listing meta', + url: '/my-item-url', }; const itemModel = { diff --git a/src/blocks/Listing/item-templates/__snapshots__/ItemTemplates.test.jsx.snap b/src/blocks/Listing/item-templates/__snapshots__/ItemTemplates.test.jsx.snap index 306b94ba..b6080602 100644 --- a/src/blocks/Listing/item-templates/__snapshots__/ItemTemplates.test.jsx.snap +++ b/src/blocks/Listing/item-templates/__snapshots__/ItemTemplates.test.jsx.snap @@ -16,7 +16,7 @@ exports[`ItemTemplates renders correctly 1`] = ` Default listing title
@@ -21,7 +21,7 @@ exports[`SimpleItemTemplates renders correctly 1`] = ` Search listing title
{ - config.blocks.blocksConfig = { - ...config.blocks.blocksConfig, - teaser: { - ...config.blocks.blocksConfig.teaser, - imageScale: 'test_scale', - }, - }; -}); - -jest.mock('@plone/volto/helpers', () => ({ - isInternalURL: jest.fn(), -})); - -describe('test', () => { - it('should return the scaled image URL if image is provided and is an internal URL', () => { - const image = { - '@id': 'internalURL', - }; - isInternalURL.mockReturnValue(true); - const result = getTeaserImageURL('', image); - - expect(isInternalURL).toHaveBeenCalledWith('internalURL'); - expect(result).toBe(`internalURL/@@images/image/test_scale`); - }); - - it('should return the image URL if image is provided and is not an internal URL', () => { - const image = { - '@id': 'internalURL', - }; - isInternalURL.mockReturnValue(false); - const result = getTeaserImageURL('', image); - - expect(isInternalURL).toHaveBeenCalledWith('internalURL'); - expect(result).toBe('internalURL'); - }); - - it('should return the default image URL if no image is provided without image_field', () => { - const href = { - '@id': 'https://example.com/item', - image_field: null, - }; - const result = getTeaserImageURL(href, ''); - - expect(result).toBe( - 'https://example.com/item/@@images/preview_image/test_scale', - ); - }); - - it('should return the default image URL if no image is provided with image_field', () => { - const href = { - '@id': 'https://example.com/item', - image_field: 'mock_image', - }; - const result = getTeaserImageURL(href, ''); - - expect(result).toBe( - 'https://example.com/item/@@images/mock_image/test_scale', - ); - }); -}); diff --git a/src/helpers.js b/src/helpers.js index 0954929f..d4a9fdce 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,6 +1,7 @@ import isArray from 'lodash/isArray'; import isObject from 'lodash/isObject'; import isString from 'lodash/isString'; +import config from '@plone/volto/registry'; import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers'; export const getFieldURL = (data) => { @@ -17,3 +18,57 @@ export const getFieldURL = (data) => { if (isString(url) && isInternalURL(url)) return flattenToAppURL(url); return url; }; + +export function getImageScaleParams(image, size) { + const imageScale = + config.blocks.blocksConfig['teaser']?.imageScale || size || 'preview'; + + if (isString(image)) + return isInternalURL(image) + ? { download: flattenToAppURL(`${image}/@@images/image/${imageScale}`) } + : { download: image }; + + if (image) { + if (isInternalURL(getFieldURL(image))) { + if (image?.image_scales?.[image?.image_field]) { + const scale = + image.image_scales[image.image_field]?.[0].scales?.[imageScale] || + image.image_scales[image.image_field]?.[0]; + + const download = flattenToAppURL( + `${getFieldURL(image)}/${scale?.download}`, + ); + const width = scale?.width; + const height = scale?.height; + + return { + download, + width, + height, + }; + } else if (image?.image?.scales) { + const scale = image.image?.scales?.[imageScale] || image.image; + const download = flattenToAppURL(scale?.download); + const width = scale?.width; + const height = scale?.height; + + return { + download, + width, + height, + }; + } else { + //fallback if we do not have scales + return { + download: flattenToAppURL( + `${getFieldURL(image)}/@@images/${ + image.image_field || 'image' + }/${imageScale}`, + ), + }; + } + } else { + return { download: getFieldURL(image) }; + } + } +} diff --git a/src/helpers.test.js b/src/helpers.test.js new file mode 100644 index 00000000..a223f8d5 --- /dev/null +++ b/src/helpers.test.js @@ -0,0 +1,107 @@ +import { getImageScaleParams } from './helpers'; +import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers'; +import config from '@plone/volto/registry'; + +beforeAll(() => { + config.blocks.blocksConfig = { + ...config.blocks.blocksConfig, + teaser: { + ...config.blocks.blocksConfig.teaser, + imageScale: 'test_scale', + }, + }; +}); + +jest.mock('@plone/volto/helpers', () => ({ + isInternalURL: jest.fn(), + flattenToAppURL: jest.fn(), +})); + +describe('test', () => { + it('should return the scaled image URL if image scales are provided and is an internal URL', () => { + const image = { + '@id': 'internalURL', + image_field: 'test_field', + image_scales: { + test_field: [ + { + download: '/en/@@images/test_field-example.jpg', + scales: { + test_scale: { + download: '/en/@@images/test_field-example-123.jpg', + }, + }, + }, + ], + }, + }; + isInternalURL.mockReturnValue(true); + flattenToAppURL.mockReturnValue('/en/@@images/test_field-example-123.jpg'); + const result = getImageScaleParams(image); + + expect(isInternalURL).toHaveBeenCalledWith('internalURL'); + expect(result.download).toBe(`/en/@@images/test_field-example-123.jpg`); + }); + + it('should return the default scaled image URL if image scales are not provided', () => { + const image = { + '@id': 'internalURL', + image_field: 'test_field', + image_scales: { + test_field: [ + { + download: '/en/@@images/test_field-example.jpg', + }, + ], + }, + }; + isInternalURL.mockReturnValue(true); + flattenToAppURL.mockReturnValue('/en/@@images/test_field-example.jpg'); + const result = getImageScaleParams(image); + + expect(isInternalURL).toHaveBeenCalledWith('internalURL'); + expect(result.download).toBe(`/en/@@images/test_field-example.jpg`); + }); + + it('should return the image URL if image is provided and is not an internal URL', () => { + const image = { + '@id': 'internalURL', + }; + isInternalURL.mockReturnValue(false); + const result = getImageScaleParams(image); + + expect(isInternalURL).toHaveBeenCalledWith('internalURL'); + expect(result.download).toBe('internalURL'); + }); + + // ensure backward compatibilty tests + it('should return the default image URL if no image is provided without image_field', () => { + const image = { + '@id': 'internalURL', + image_field: null, + }; + isInternalURL.mockReturnValue(true); + flattenToAppURL.mockReturnValue( + 'internalURL/@@images/preview_image/test_scale', + ); + const result = getImageScaleParams(image); + + expect(result.download).toBe( + 'internalURL/@@images/preview_image/test_scale', + ); + }); + + it('should return the default image URL if no image scales are provided with image_field', () => { + const image = { + '@id': 'internalURL', + image_field: 'mock_image', + image_scales: null, + }; + isInternalURL.mockReturnValue(true); + const result = getImageScaleParams(image); + + expect(result.download).toBe( + 'internalURL/@@images/preview_image/test_scale', + ); + }); +});