diff --git a/dotcom-rendering/src/components/MultiImageBlockComponent.stories.tsx b/dotcom-rendering/src/components/MultiImageBlockComponent.stories.tsx index 5c068242595..b3d110d1785 100644 --- a/dotcom-rendering/src/components/MultiImageBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/MultiImageBlockComponent.stories.tsx @@ -144,3 +144,20 @@ export const GridOfFourWithCaption = () => { ); }; GridOfFourWithCaption.storyName = 'grid of four with caption'; + +export const Slideshow = () => { + return ( +
+ +
+ ); +}; +Slideshow.storyName = 'slideshow'; diff --git a/dotcom-rendering/src/components/MultiImageBlockComponent.tsx b/dotcom-rendering/src/components/MultiImageBlockComponent.tsx index c4c1b89927b..88de111dcef 100644 --- a/dotcom-rendering/src/components/MultiImageBlockComponent.tsx +++ b/dotcom-rendering/src/components/MultiImageBlockComponent.tsx @@ -5,11 +5,15 @@ import type { ImageBlockElement } from '../types/content'; import { Caption } from './Caption'; import { GridItem } from './GridItem'; import { ImageComponent } from './ImageComponent'; +import { SlideshowCarousel } from './SlideshowCarousel.importable'; +import { getLargest, getMaster } from '../lib/image'; +import { Island } from './Island'; type Props = { images: ImageBlockElement[]; format: ArticleFormat; caption?: string; + presentation?: 'slideshow' | 'side-by-side' | 'stacked'; }; const ieFallback = css` @@ -102,6 +106,14 @@ const GridOfFour = ({ children }: { children: React.ReactNode }) => ( ); +const removeLastFullStop = (text?: string) => { + if (!text) return text; + if (text.endsWith('.')) { + return text.slice(0, -1); + } + return text; +}; + const OneImage = ({ images, format, @@ -110,23 +122,26 @@ const OneImage = ({ images: [ImageBlockElement]; format: ArticleFormat; caption?: string; -}) => ( -
- - {!!caption && ( - { + const captionToUse = caption || removeLastFullStop(images[0].data.caption); + return ( +
+ - )} -
-); + {!!captionToUse && ( + + )} +
+ ); +}; const TwoImage = ({ images, @@ -136,35 +151,44 @@ const TwoImage = ({ images: [ImageBlockElement, ImageBlockElement]; format: ArticleFormat; caption?: string; -}) => ( -
- - - { + const captionLeft = + images[0].data.caption && + `${removeLastFullStop(images[0].data.caption)} (above left). `; + const captionRight = + images[1].data.caption && + `${removeLastFullStop(images[1].data.caption)} (above right). `; + const captionToUse = caption || `${captionLeft ?? ''}${captionRight ?? ''}`; + return ( +
+ + + + + + + + + {!!captionToUse && ( + - - - - - - {!!caption && ( - - )} -
-); + )} +
+ ); +}; const ThreeImage = ({ images, @@ -174,43 +198,59 @@ const ThreeImage = ({ images: [ImageBlockElement, ImageBlockElement, ImageBlockElement]; format: ArticleFormat; caption?: string; -}) => ( -
- - - { + const captionTop = + images[0].data.caption && + `${removeLastFullStop(images[0].data.caption)} (top). `; + const captionBottomLeft = + images[1].data.caption && + `${removeLastFullStop(images[1].data.caption)} (bottom left). `; + const captionBottomRight = + images[2].data.caption && + `${removeLastFullStop(images[2].data.caption)} (bottom right). `; + const captionToUse = + caption || + `${captionTop ?? ''}${captionBottomLeft ?? ''}${ + captionBottomRight ?? '' + }`; + return ( +
+ + + + + + + + + + + + {!!captionToUse && ( + - - - - - - - - - {!!caption && ( - - )} -
-); + )} +
+ ); +}; const FourImage = ({ images, @@ -225,88 +265,153 @@ const FourImage = ({ ]; format: ArticleFormat; caption?: string; -}) => ( -
- - - - - - { + const captionTopLeft = + images[0].data.caption && + `${removeLastFullStop(images[0].data.caption)} (top left). `; + const captionTopRight = + images[1].data.caption && + `${removeLastFullStop(images[1].data.caption)} (top right). `; + const captionBottomLeft = + images[2].data.caption && + `${removeLastFullStop(images[2].data.caption)} (bottom left). `; + const captionBottomRight = + images[3].data.caption && + `${removeLastFullStop(images[3].data.caption)} (bottom right). `; + const captionToUse = + caption || + `${captionTopLeft ?? ''}${captionTopRight ?? ''}${ + captionBottomLeft ?? '' + }${captionBottomRight ?? ''}`; + return ( +
+ + + + + + + + + + + + + + + {!!captionToUse && ( + - - - - - - + ); +}; + +const Slideshow = ({ + images, + format, + caption, +}: { + images: ImageBlockElement[]; + format: ArticleFormat; + caption?: string; +}) => { + const imagesForSlideshow = images.map((element) => { + /** Legacy images do not have a master so we fallback to the largest available */ + const image = + getMaster(element.media.allImages) ?? + getLargest(element.media.allImages); + return { + imageSrc: image?.url ?? '', + imageCaption: element.data.caption, + }; + }); + return ( + <> + + - - - {!!caption && ( - - )} -
-); + + + ); +}; export const MultiImageBlockComponent = ({ images, format, caption, + presentation, }: Props) => { - const [one, two, three, four] = images; + if (presentation && presentation === 'slideshow') { + return ; + } else { + const [one, two, three, four] = images; - if (one && two && three && four) { - return ( - - ); - } + if (one && two && three && four) { + return ( + + ); + } - if (one && two && three) { - return ( - - ); - } + if (one && two && three) { + return ( + + ); + } - if (one && two) { - return ( - - ); - } + if (one && two) { + return ( + + ); + } - if (one) { - return ; - } + if (one) { + return ( + + ); + } - return null; + return null; + } }; diff --git a/dotcom-rendering/src/frontend/schemas/feArticle.json b/dotcom-rendering/src/frontend/schemas/feArticle.json index ba05d3e81f5..10868ad50cb 100644 --- a/dotcom-rendering/src/frontend/schemas/feArticle.json +++ b/dotcom-rendering/src/frontend/schemas/feArticle.json @@ -2935,6 +2935,14 @@ }, "role": { "$ref": "#/definitions/RoleType" + }, + "presentation": { + "enum": [ + "side-by-side", + "slideshow", + "stacked" + ], + "type": "string" } }, "required": [ diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index a04b2026e0a..1d39554cce3 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -568,6 +568,7 @@ export const renderElement = ({ key={index} images={element.images} caption={element.caption} + presentation={element.presentation} /> ); case 'model.dotcomrendering.pageElements.NewsletterSignupBlockElement': diff --git a/dotcom-rendering/src/lib/thrift/nativeConnection.ts b/dotcom-rendering/src/lib/thrift/nativeConnection.ts index 3b1ab0659f5..97e112b9508 100644 --- a/dotcom-rendering/src/lib/thrift/nativeConnection.ts +++ b/dotcom-rendering/src/lib/thrift/nativeConnection.ts @@ -124,7 +124,7 @@ export class NativeConnection extends ThriftConnection { resolve: res, reject: rej, timeoutId: setTimeout(function () { - connection.reset(id); + // connection.reset(id); }, ACTION_TIMEOUT_MS), }); const message: NativeMessage = { diff --git a/dotcom-rendering/src/model/block-schema.json b/dotcom-rendering/src/model/block-schema.json index 97c8187254a..0dfe9cba990 100644 --- a/dotcom-rendering/src/model/block-schema.json +++ b/dotcom-rendering/src/model/block-schema.json @@ -2423,6 +2423,14 @@ }, "role": { "$ref": "#/definitions/RoleType" + }, + "presentation": { + "enum": [ + "side-by-side", + "slideshow", + "stacked" + ], + "type": "string" } }, "required": [ diff --git a/dotcom-rendering/src/model/enhance-images.ts b/dotcom-rendering/src/model/enhance-images.ts index 44024de18db..36a4ed9b3b8 100644 --- a/dotcom-rendering/src/model/enhance-images.ts +++ b/dotcom-rendering/src/model/enhance-images.ts @@ -454,7 +454,7 @@ const enhance = .addMultiImageElements() // If any MultiImageBlockElement is followed by a ul/l caption, delete the special caption // element and use the value for the multi image `caption` prop - .addCaptionsToMultis() + // .addCaptionsToMultis() .addImagePositions(imagesForLightbox).elements ); }; diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index 3fbd75dcc98..5fef2a8a838 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -464,6 +464,7 @@ export interface MultiImageBlockElement { images: ImageBlockElement[]; caption?: string; role?: RoleType; + presentation?: 'slideshow' | 'side-by-side' | 'stacked'; } export interface NewsletterSignupBlockElement {