From abe92989d5b3128a793fee20a606ee295c85fb94 Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Mon, 20 Jan 2025 17:46:13 +0000 Subject: [PATCH 01/10] Add media icons to pills --- dotcom-rendering/src/paletteDeclarations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index f29d1d03c2..b3c7c2e627 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -5510,7 +5510,7 @@ const highlightsCardKickerText: PaletteFunction = (format) => { }; const highlightsCardAudioIcon: PaletteFunction = () => sourcePalette.brand[100]; const highlightsCardAudioText: PaletteFunction = () => - sourcePalette.neutral[20]; + sourcePalette.neutral[10]; const pinnedPostBorderLight: PaletteFunction = ({ theme }) => { switch (theme) { From fcfd6af6cab7eac90578cb96c70d87f1ff083359 Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Tue, 21 Jan 2025 17:50:06 +0000 Subject: [PATCH 02/10] update audio text colour --- dotcom-rendering/src/paletteDeclarations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index b3c7c2e627..f29d1d03c2 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -5510,7 +5510,7 @@ const highlightsCardKickerText: PaletteFunction = (format) => { }; const highlightsCardAudioIcon: PaletteFunction = () => sourcePalette.brand[100]; const highlightsCardAudioText: PaletteFunction = () => - sourcePalette.neutral[10]; + sourcePalette.neutral[20]; const pinnedPostBorderLight: PaletteFunction = ({ theme }) => { switch (theme) { From 6cb75b2d1e094d95a86e3ca10c49273addae4a7e Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Wed, 22 Jan 2025 18:05:59 +0000 Subject: [PATCH 03/10] add standalone video pill to bottom of card --- dotcom-rendering/src/components/Card/Card.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index a8f9636dc7..643e3f47d1 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -36,7 +36,7 @@ import type { Loading } from '../CardPicture'; import { CardPicture } from '../CardPicture'; import { Island } from '../Island'; import { LatestLinks } from '../LatestLinks.importable'; -import { MediaDuration } from '../MediaDuration'; +import { MediaDuration, secondsToDuration } from '../MediaDuration'; import { MediaMeta } from '../MediaMeta'; import { Pill } from '../Pill'; import { Slideshow } from '../Slideshow'; @@ -485,6 +485,14 @@ export const Card = ({ margin-top: auto; `} > + {mainMedia?.type === 'Video' && + format.design === ArticleDesign.Video && ( + } + iconSize={'small'} + /> + )} {mainMedia?.type === 'Audio' && ( Date: Thu, 23 Jan 2025 13:43:02 +0000 Subject: [PATCH 04/10] Display video pill in bottom left of card content if video article --- dotcom-rendering/src/components/Card/Card.tsx | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 643e3f47d1..28456a9087 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -426,6 +426,14 @@ export const Card = ({ const isBetaContainer = BETA_CONTAINERS.includes(containerType ?? ''); + /** A video article is standalone video content and is considered a media card. */ + const isVideoArticle = + mainMedia?.type === 'Video' && format.design === ArticleDesign.Video; + + /** Article that have video as the main media but are not a video article are not considered a media card and are styled differenently as a consequence. */ + const isVideoMainMedia = + mainMedia?.type === 'Video' && format.design !== ArticleDesign.Video; + const decideAge = () => { if (!webPublicationDate) return undefined; const withinTwelveHours = isWithinTwelveHours(webPublicationDate); @@ -485,14 +493,13 @@ export const Card = ({ margin-top: auto; `} > - {mainMedia?.type === 'Video' && - format.design === ArticleDesign.Video && ( - } - iconSize={'small'} - /> - )} + {isVideoArticle && ( + } + iconSize={'small'} + /> + )} {mainMedia?.type === 'Audio' && ( - {mainMedia?.type === 'Video' && - mainMedia.duration > 0 && ( - 0 && ( +
+ } + iconSize={'small'} /> - )} +
+ )} )} {media.type === 'crossword' && ( From de78f2f201b9076f8393cc9ff18d66759356beec Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 23 Jan 2025 13:43:43 +0000 Subject: [PATCH 05/10] Use pill component for timestamp on youtube video overlay --- .../YoutubeAtom/YoutubeAtomOverlay.tsx | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx index ea970ceb01..b94eb9a765 100644 --- a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx +++ b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx @@ -20,6 +20,8 @@ import { PlayIcon } from '../Card/components/PlayIcon'; import { FormatBoundary } from '../FormatBoundary'; import { Kicker } from '../Kicker'; import { YoutubeAtomPicture } from './YoutubeAtomPicture'; +import { Pill } from '../Pill'; +import { SvgMediaControlsPlay } from '../SvgMediaControlsPlay'; type Props = { uniqueId: string; @@ -72,16 +74,6 @@ const pillStyles = css` position: absolute; top: ${space[2]}px; right: ${space[2]}px; - ${textSansBold12}; - color: ${palette('--pill-text')}; -`; - -const durationPillStyles = css` - background-color: rgba(0, 0, 0, 0.7); - border-radius: ${space[3]}px; - padding: ${space[1]}px ${space[3]}px; - display: inline-flex; - line-height: ${space[4]}px; `; const livePillStyles = css` @@ -186,10 +178,14 @@ export const YoutubeAtomOverlay = ({ ? css` display: none; ` - : [pillStyles, durationPillStyles] + : pillStyles } > - {secondsToDuration(duration)} + } + iconSize={'small'} + /> )} Date: Thu, 23 Jan 2025 14:00:04 +0000 Subject: [PATCH 06/10] Use pill for live indicator --- .../YoutubeAtom/YoutubeAtomOverlay.tsx | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx index b94eb9a765..d0bd837b29 100644 --- a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx +++ b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx @@ -76,26 +76,12 @@ const pillStyles = css` right: ${space[2]}px; `; -const livePillStyles = css` - border-radius: ${space[10]}px; - padding: ${space[1]}px ${space[2]}px; - gap: ${space[2]}px; - background-color: ${palette('--pill-background')}; - display: flex; - align-items: center; -`; - const liveBulletStyles = css` - ::before { - content: ''; - width: 9px; - height: 9px; - border-radius: 50%; - background-color: ${palette('--pill-bullet')}; - display: inline-block; - position: relative; - margin-right: 0.1875rem; - } + width: 9px; + height: 9px; + border-radius: 50%; + background-color: ${palette('--pill-bullet')}; + margin-right: ${space[1]}px; `; const textOverlayStyles = css` @@ -167,8 +153,20 @@ export const YoutubeAtomOverlay = ({ /> )} {isLiveStream && ( -
- Live +
+ } + iconSize={'small'} + />
)} {hasDuration && ( From d906ffefda459ebe49f42a4f49145dcb9210d34b Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Mon, 27 Jan 2025 12:18:42 +0000 Subject: [PATCH 07/10] Switch to new video pill placment for beta containers --- dotcom-rendering/src/components/Card/Card.tsx | 49 ++++++++++++------- .../src/components/FeatureCard.tsx | 10 +--- dotcom-rendering/src/lib/formatTime.ts | 8 +++ 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 28456a9087..5b059492fc 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -36,8 +36,8 @@ import type { Loading } from '../CardPicture'; import { CardPicture } from '../CardPicture'; import { Island } from '../Island'; import { LatestLinks } from '../LatestLinks.importable'; -import { MediaDuration, secondsToDuration } from '../MediaDuration'; import { MediaMeta } from '../MediaMeta'; +import { isWithinTwelveHours, secondsToDuration } from '../../lib/formatTime'; import { Pill } from '../Pill'; import { Slideshow } from '../Slideshow'; import { SlideshowCarousel } from '../SlideshowCarousel.importable'; @@ -342,13 +342,13 @@ const getHeadlinePosition = ({ return 'inner'; }; -export const isWithinTwelveHours = (webPublicationDate: string): boolean => { - const timeDiffMs = Math.abs( - new Date().getTime() - new Date(webPublicationDate).getTime(), - ); - const timeDiffHours = timeDiffMs / (1000 * 60 * 60); - return timeDiffHours <= 12; -}; +const liveBulletStyles = css` + width: 9px; + height: 9px; + border-radius: 50%; + background-color: ${palette('--pill-bullet')}; + margin-right: ${space[1]}px; +`; export const Card = ({ linkTo, @@ -494,16 +494,28 @@ export const Card = ({ `} > {isVideoArticle && ( - } - iconSize={'small'} - /> + <> + {mainMedia.duration === 0 ? ( + } + iconSize={'small'} + /> + ) : ( + } + iconSize={'small'} + /> + )} + )} + {mainMedia?.type === 'Audio' && ( } + iconSize={'small'} /> )} {mainMedia?.type === 'Gallery' && ( @@ -529,10 +541,7 @@ export const Card = ({ * Check media type to determine if pill, or article metadata & icon shown. * Currently pills are only shown within beta containers. */ - const showPill = - isBetaContainer && - mainMedia && - (mainMedia.type === 'Audio' || mainMedia.type === 'Gallery'); + const showPill = isBetaContainer && !!mainMedia; const media = getMedia({ imageUrl: image?.src, @@ -870,7 +879,11 @@ export const Card = ({ } index={index} duration={ - media.mainMedia.duration + isBetaContainer && + isVideoArticle + ? undefined + : media.mainMedia + .duration } posterImage={ media.mainMedia.images diff --git a/dotcom-rendering/src/components/FeatureCard.tsx b/dotcom-rendering/src/components/FeatureCard.tsx index f9b8deb8f2..cbfd5b6f24 100644 --- a/dotcom-rendering/src/components/FeatureCard.tsx +++ b/dotcom-rendering/src/components/FeatureCard.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import { space } from '@guardian/source/foundations'; import { Link, SvgMediaControlsPlay } from '@guardian/source/react-components'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; -import { secondsToDuration } from '../lib/formatTime'; +import { isWithinTwelveHours, secondsToDuration } from '../lib/formatTime'; import { getZIndex } from '../lib/getZIndex'; import { DISCUSSION_ID_DATA_ATTRIBUTE } from '../lib/useCommentCount'; import { palette } from '../palette'; @@ -218,14 +218,6 @@ const getMedia = ({ return undefined; }; -export const isWithinTwelveHours = (webPublicationDate: string): boolean => { - const timeDiffMs = Math.abs( - new Date().getTime() - new Date(webPublicationDate).getTime(), - ); - const timeDiffHours = timeDiffMs / (1000 * 60 * 60); - return timeDiffHours <= 12; -}; - const CardAge = ({ showClock, absoluteServerTimes, diff --git a/dotcom-rendering/src/lib/formatTime.ts b/dotcom-rendering/src/lib/formatTime.ts index 3121f879e5..b426d92eb7 100644 --- a/dotcom-rendering/src/lib/formatTime.ts +++ b/dotcom-rendering/src/lib/formatTime.ts @@ -44,3 +44,11 @@ export const secondsToDuration = (secs?: number): string => { } return duration.join(':'); }; + +export const isWithinTwelveHours = (webPublicationDate: string): boolean => { + const timeDiffMs = Math.abs( + new Date().getTime() - new Date(webPublicationDate).getTime(), + ); + const timeDiffHours = timeDiffMs / (1000 * 60 * 60); + return timeDiffHours <= 12; +}; From 19f52e83c1ec1ee8d8902fe46af6ad75ecfe97da Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Mon, 27 Jan 2025 13:48:20 +0000 Subject: [PATCH 08/10] Fix linting --- .../src/components/YoutubeAtom/YoutubeAtomOverlay.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx index d0bd837b29..306d2e2237 100644 --- a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx +++ b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomOverlay.tsx @@ -6,7 +6,6 @@ import { headlineMedium20, palette as sourcePalette, space, - textSansBold12, } from '@guardian/source/foundations'; import type { ArticleFormat } from '../../lib/articleFormat'; import { secondsToDuration } from '../../lib/formatTime'; @@ -19,9 +18,9 @@ import type { import { PlayIcon } from '../Card/components/PlayIcon'; import { FormatBoundary } from '../FormatBoundary'; import { Kicker } from '../Kicker'; -import { YoutubeAtomPicture } from './YoutubeAtomPicture'; import { Pill } from '../Pill'; import { SvgMediaControlsPlay } from '../SvgMediaControlsPlay'; +import { YoutubeAtomPicture } from './YoutubeAtomPicture'; type Props = { uniqueId: string; @@ -129,7 +128,7 @@ export const YoutubeAtomOverlay = ({ const id = `youtube-overlay-${uniqueId}`; const hasDuration = !isUndefined(duration) && duration > 0; //** We infer that a video is a livestream if the duration is set to 0. This is a soft contract with Editorial who manual set the duration of videos */ - const isLiveStream = duration === 0; + const isLiveStream = !isUndefined(duration) && duration === 0; const image = overrideImage ?? posterImage; const hidePillOnMobile = imagePositionOnMobile === 'right' || imagePositionOnMobile === 'left'; From 0dd150fd830cef5cbf80ddcf27bad2237461b557 Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Mon, 27 Jan 2025 13:51:09 +0000 Subject: [PATCH 09/10] improve comments for video cards --- dotcom-rendering/src/components/Card/Card.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 5b059492fc..21f89cc74a 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -426,11 +426,17 @@ export const Card = ({ const isBetaContainer = BETA_CONTAINERS.includes(containerType ?? ''); - /** A video article is standalone video content and is considered a media card. */ + /** + * A "video article" refers to standalone video content presented as the main focus of the article. + * It is treated as a media card in the design system. + */ const isVideoArticle = mainMedia?.type === 'Video' && format.design === ArticleDesign.Video; - /** Article that have video as the main media but are not a video article are not considered a media card and are styled differenently as a consequence. */ + /** + * Articles with a video as the main media but not classified as "video articles" + * are styled differently and are not treated as media cards. + */ const isVideoMainMedia = mainMedia?.type === 'Video' && format.design !== ArticleDesign.Video; From 83cf9c85b8089778456ee90ada98cd2af77035b1 Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Mon, 27 Jan 2025 13:52:56 +0000 Subject: [PATCH 10/10] Reorder imports for linter --- dotcom-rendering/src/components/Card/Card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 21f89cc74a..74f82d37fb 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -12,6 +12,7 @@ import { ArticleSpecial, } from '../../lib/articleFormat'; import { isMediaCard as isAMediaCard } from '../../lib/cardHelpers'; +import { isWithinTwelveHours, secondsToDuration } from '../../lib/formatTime'; import { getZIndex } from '../../lib/getZIndex'; import { DISCUSSION_ID_DATA_ATTRIBUTE } from '../../lib/useCommentCount'; import { palette } from '../../palette'; @@ -37,7 +38,6 @@ import { CardPicture } from '../CardPicture'; import { Island } from '../Island'; import { LatestLinks } from '../LatestLinks.importable'; import { MediaMeta } from '../MediaMeta'; -import { isWithinTwelveHours, secondsToDuration } from '../../lib/formatTime'; import { Pill } from '../Pill'; import { Slideshow } from '../Slideshow'; import { SlideshowCarousel } from '../SlideshowCarousel.importable';