diff --git a/.changeset/unlucky-berries-press.md b/.changeset/unlucky-berries-press.md new file mode 100644 index 000000000..e4d714036 --- /dev/null +++ b/.changeset/unlucky-berries-press.md @@ -0,0 +1,5 @@ +--- +"nextjs-website": patch +--- + +VideoImage: Show title and subtitle when displaying image and add image for mobile breakpoint diff --git a/apps/nextjs-website/react-components/components/VideoImage/VideoImage.helpers.tsx b/apps/nextjs-website/react-components/components/VideoImage/VideoImage.helpers.tsx index 37df502e0..f6026b20a 100644 --- a/apps/nextjs-website/react-components/components/VideoImage/VideoImage.helpers.tsx +++ b/apps/nextjs-website/react-components/components/VideoImage/VideoImage.helpers.tsx @@ -19,9 +19,8 @@ export const renderVideo = ({ fallback, onClick, onVideoEnd, - isMobileDevice + isMobileDevice, }: RenderVideoProps) => { - // Define styles for mobile and non-mobile devices const mobileStyle = { overflow: 'hidden', width: '100vw', @@ -55,17 +54,18 @@ export const renderVideo = ({ onClick={onClick} style={isMobileDevice ? mobileStyle : nonMobileStyle} > - setError(true)} - /> + setError(true)} /> ); }; -// Refactored renderImage function -export const renderImage = ({ src, alt, isMobileDevice }: RenderImageProps) => { - // Define styles for mobile and non-mobile devices +export const renderImage = ({ + src, + alt, + mobileSrc, + mobileAlt, + isMobileDevice, +}: RenderImageProps) => { const mobileStyle = { overflow: 'hidden', width: '100vw', @@ -80,10 +80,13 @@ export const renderImage = ({ src, alt, isMobileDevice }: RenderImageProps) => { height: '100%', }; + const imageSrc = isMobileDevice && mobileSrc ? mobileSrc : src; + const imageAlt = isMobileDevice && mobileAlt ? mobileAlt : alt; + return ( {alt} { + const textColor = TextColor(theme); + return ( + <> + {title && ( + + {title} + + )} + {subtitle && ( + + {subtitle} + + )} + + ); +}; + export const VideoCaption = ({ caption, isCentered }: VideoCaptionProps) => { const { palette } = useTheme(); return ( diff --git a/apps/nextjs-website/react-components/components/VideoImage/VideoImage.tsx b/apps/nextjs-website/react-components/components/VideoImage/VideoImage.tsx index e0b729b36..4c980e6b6 100644 --- a/apps/nextjs-website/react-components/components/VideoImage/VideoImage.tsx +++ b/apps/nextjs-website/react-components/components/VideoImage/VideoImage.tsx @@ -1,6 +1,3 @@ -// Disable rule below since we'll be determining whether image or video exist based on mediaState -/* eslint-disable @typescript-eslint/no-non-null-assertion */ - import { useEffect, useRef, useState } from 'react'; import { useIsVisible } from '@react-components/types/common/Common.types'; import { VideoImageProps } from '@react-components/types'; @@ -11,6 +8,7 @@ import { renderVideo, VideoCaption, VideoText, + ImageText, } from './VideoImage.helpers'; const VideoImage = ({ @@ -20,12 +18,11 @@ const VideoImage = ({ isCentered, theme, image, + mobileImage, video, sectionID, }: VideoImageProps) => { if (!image && !video) { - // Disable lint for this case because we want the build to fail if user input nothing - // eslint-disable-next-line throw new Error(); } @@ -41,7 +38,6 @@ const VideoImage = ({ useEffect(() => { setIsMobileDevice(window.innerWidth <= 768); - const handleResize = () => { setIsMobileDevice(window.innerWidth <= 768); }; @@ -52,7 +48,6 @@ const VideoImage = ({ useEffect(() => { if (mediaState === 'image') return; if (!isVisible) return; - const startVideoWhenVisible = async () => { if (video?.autoplay && isVisible) play(); }; @@ -61,18 +56,14 @@ const VideoImage = ({ const play = (e?: React.MouseEvent) => { e?.preventDefault(); - if (mediaState === 'image') return; - if (videoRef.current) { videoRef.current .play() .then(() => { setMediaState('play'); }) - .catch(() => { - // Handle play error - }); + .catch(() => {}); } }; @@ -95,7 +86,7 @@ const VideoImage = ({ position: 'relative', overflow: 'hidden', }} - {...sectionID && { id: sectionID }} + {...(sectionID && { id: sectionID })} > {video?.showControls && (mediaState === 'stop' || mediaState === 'pause') && ( @@ -165,29 +156,78 @@ const VideoImage = ({ )} + {mediaState === 'image' ? ( + <> +
+ {renderImage({ + src: image!.src, + alt: image!.alt, + mobileSrc: mobileImage!.src, + mobileAlt: mobileImage!.alt, + isMobileDevice, + })} + + {(title || subtitle) && ( + <> +
- {mediaState === 'image' - ? renderImage({ - src: image!.src, - alt: image!.alt, - isMobileDevice, - }) - : renderVideo({ - videoRef, - error, - setError, - src: video!.src, - loop: video!.loop, - autoplay: video!.autoplay, - fallback: video!.fallback, - onVideoEnd: handleVideoEnd, - onClick: pause, - isMobileDevice, - })} +
+ +
+ + )} +
+ + ) : ( + renderVideo({ + videoRef, + error, + setError, + src: video!.src, + loop: video!.loop, + autoplay: video!.autoplay, + fallback: video!.fallback, + onVideoEnd: handleVideoEnd, + onClick: pause, + isMobileDevice, + }) + )} - {caption && ( - - )} + {caption && } ); }; diff --git a/apps/nextjs-website/react-components/types/VideoImage/VideoImage.types.ts b/apps/nextjs-website/react-components/types/VideoImage/VideoImage.types.ts index 8fbd74621..e7e20456a 100644 --- a/apps/nextjs-website/react-components/types/VideoImage/VideoImage.types.ts +++ b/apps/nextjs-website/react-components/types/VideoImage/VideoImage.types.ts @@ -1,10 +1,17 @@ import { SectionProps, Theme } from '../common/Common.types'; -export interface VideoImageProps extends SectionProps, VideoTextProps, VideoCaptionProps { +export interface VideoImageProps + extends SectionProps, + VideoTextProps, + VideoCaptionProps { image?: { src: string; alt: string; }; + mobileImage?: { + src: string; + alt: string; + }; video?: { src: string; autoplay: boolean; @@ -43,5 +50,7 @@ export interface RenderVideoProps { export interface RenderImageProps { src: string; alt: string; + mobileSrc: string; + mobileAlt: string; isMobileDevice: boolean; } diff --git a/apps/nextjs-website/src/components/VideoImage.tsx b/apps/nextjs-website/src/components/VideoImage.tsx index c40dfd74a..b5441dea0 100644 --- a/apps/nextjs-website/src/components/VideoImage.tsx +++ b/apps/nextjs-website/src/components/VideoImage.tsx @@ -8,6 +8,7 @@ const makeVideoImageProps = ({ subtitle, caption, image, + mobileImage, video, ...rest }: VideoImageSection): VideoImageProps => ({ @@ -15,7 +16,6 @@ const makeVideoImageProps = ({ ...(title && { title }), ...(subtitle && { subtitle }), ...(caption && { caption }), - // If user uploaded a video, use it ...(video && video.src.data && { video: { @@ -28,27 +28,19 @@ const makeVideoImageProps = ({ pausedPlayButtonLabel: video.pausedPlayButtonLabel, }, }), - // If user did not upload a video, check if they input a URL - ...(video && - !video.src.data && - video.srcURL && { - video: { - src: video.srcURL, - autoplay: video.autoplay, - loop: video.loop, - showControls: video.showControls, - fallback: video.fallback, - playButtonLabel: video.playButtonLabel, - pausedPlayButtonLabel: video.pausedPlayButtonLabel, - }, - }), - // If user did not input any video source, check if they uploaded an image ...((!video || (!video.srcURL && !video.src.data)) && image.data && { image: { src: image.data.attributes.url, alt: image.data.attributes.alternativeText ?? '', }, + mobileImage: { + src: mobileImage?.data?.attributes?.url ?? image.data.attributes.url, + alt: + mobileImage?.data?.attributes?.alternativeText ?? + image.data.attributes.alternativeText ?? + '', + }, }), }); diff --git a/apps/nextjs-website/src/lib/fetch/types/PageSection.ts b/apps/nextjs-website/src/lib/fetch/types/PageSection.ts index 544c63c5e..9867dd53c 100644 --- a/apps/nextjs-website/src/lib/fetch/types/PageSection.ts +++ b/apps/nextjs-website/src/lib/fetch/types/PageSection.ts @@ -260,6 +260,7 @@ const VideoImageSectionCodec = t.strict({ caption: t.union([t.string, t.null]), isCentered: t.boolean, image: StrapiImageSchema, + mobileImage: StrapiImageSchema, video: t.union([VideoCodec, t.null]), }); diff --git a/apps/nextjs-website/stories/VideoImage/dark.stories.tsx b/apps/nextjs-website/stories/VideoImage/dark.stories.tsx index f2d658689..a0c8c5dc6 100644 --- a/apps/nextjs-website/stories/VideoImage/dark.stories.tsx +++ b/apps/nextjs-website/stories/VideoImage/dark.stories.tsx @@ -110,14 +110,22 @@ VideoFullNoTitleNoSubtitleWithCaption.args = { }, }; -export const ImageNoTitleNoCaption: StoryFn = - VideoImageTemplate.bind({}); -ImageNoTitleNoCaption.args = { +export const ImageFull: StoryFn = VideoImageTemplate.bind( + {} +); +ImageFull.args = { ...defaultsDark, + title: 'Sample Image Title', + subtitle: 'Sample Image Subtitle', image: { src: 'https://notifichedigitali.pagopa.it/static/images/hero-enti-background.png', alt: 'Sample Image Alt', }, + mobileImage: { + src: 'https://notifichedigitali.pagopa.it/static/images/pi-hero-background.png', + alt: 'Sample Mobile Image Alt', + }, + caption: 'Sample Video Caption', }; export const ImageNoTextWithCaptionCentered: StoryFn = @@ -128,6 +136,10 @@ ImageNoTextWithCaptionCentered.args = { src: 'https://notifichedigitali.pagopa.it/static/images/hero-enti-background.png', alt: 'Sample Image Alt', }, + mobileImage: { + src: 'https://notifichedigitali.pagopa.it/static/images/pi-hero-background.png', + alt: 'Sample Mobile Image Alt', + }, caption: 'Sample Video Caption', isCentered: true, }; @@ -140,13 +152,16 @@ ImageNoTextWithCaptionLeft.args = { src: 'https://notifichedigitali.pagopa.it/static/images/hero-enti-background.png', alt: 'Sample Image Alt', }, + mobileImage: { + src: 'https://notifichedigitali.pagopa.it/static/images/pi-hero-background.png', + alt: 'Sample Mobile Image Alt', + }, caption: 'Sample Video Caption', }; -export const ImageFull: StoryFn = VideoImageTemplate.bind( - {} -); -ImageFull.args = { +export const ImageNoCaptionWithTextCenter: StoryFn = + VideoImageTemplate.bind({}); +ImageNoCaptionWithTextCenter.args = { ...defaultsDark, title: 'Sample Image Title', subtitle: 'Sample Image Subtitle', @@ -154,5 +169,39 @@ ImageFull.args = { src: 'https://notifichedigitali.pagopa.it/static/images/hero-enti-background.png', alt: 'Sample Image Alt', }, - caption: 'Sample Video Caption', + mobileImage: { + src: 'https://notifichedigitali.pagopa.it/static/images/pi-hero-background.png', + alt: 'Sample Mobile Image Alt', + }, + isCentered: true, +}; + +export const ImageNoCaptionWithTextLeft: StoryFn = + VideoImageTemplate.bind({}); +ImageNoCaptionWithTextLeft.args = { + ...defaultsDark, + title: 'Sample Image Title', + subtitle: 'Sample Image Subtitle', + image: { + src: 'https://notifichedigitali.pagopa.it/static/images/hero-enti-background.png', + alt: 'Sample Image Alt', + }, + mobileImage: { + src: 'https://notifichedigitali.pagopa.it/static/images/pi-hero-background.png', + alt: 'Sample Mobile Image Alt', + }, +}; + +export const ImageNoTitleNoCaption: StoryFn = + VideoImageTemplate.bind({}); +ImageNoTitleNoCaption.args = { + ...defaultsDark, + image: { + src: 'https://notifichedigitali.pagopa.it/static/images/hero-enti-background.png', + alt: 'Sample Image Alt', + }, + mobileImage: { + src: 'https://notifichedigitali.pagopa.it/static/images/pi-hero-background.png', + alt: 'Sample Mobile Image Alt', + }, };