diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index dc6c97cfe9f..1d57306813d 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -1,18 +1,17 @@ import { css } from '@emotion/react'; import { isUndefined } from '@guardian/libs'; import { between, from, space, until } from '@guardian/source/foundations'; -import { Hide, Link, SvgCamera } from '@guardian/source/react-components'; +import { Hide } from '@guardian/source/react-components'; import { ArticleDesign, type ArticleFormat, ArticleSpecial, } from '../../lib/articleFormat'; import { isMediaCard } from '../../lib/cardHelpers'; -import { isWithinTwelveHours, secondsToDuration } from '../../lib/formatTime'; +import { secondsToDuration } from '../../lib/formatTime'; import { appendLinkNameMedia } from '../../lib/getDataLinkName'; import { getZIndex } from '../../lib/getZIndex'; import { getOphanComponents } from '../../lib/labs'; -import { DISCUSSION_ID_DATA_ATTRIBUTE } from '../../lib/useCommentCount'; import { BETA_CONTAINERS } from '../../model/enhanceCollections'; import { palette } from '../../palette'; import type { Branding } from '../../types/branding'; @@ -31,7 +30,6 @@ import type { MainMedia } from '../../types/mainMedia'; import type { OnwardsSource } from '../../types/onwards'; import { Avatar } from '../Avatar'; import { BrandingLabel } from '../BrandingLabel'; -import { CardCommentCount } from '../CardCommentCount.importable'; import { CardHeadline, type ResponsiveFontSize } from '../CardHeadline'; import type { Loading } from '../CardPicture'; import { CardPicture } from '../CardPicture'; @@ -50,8 +48,7 @@ import { SupportingKeyStoriesContent } from '../SupportingKeyStoriesContent'; import { SvgMediaControlsPlay } from '../SvgMediaControlsPlay'; import { YoutubeBlockComponent } from '../YoutubeBlockComponent.importable'; import { AvatarContainer } from './components/AvatarContainer'; -import { CardAge } from './components/CardAge'; -import { CardFooter } from './components/CardFooter'; +import { CardFooter } from './components/CardFooterNew'; import { CardLayout, decideAvatarPosition, @@ -337,14 +334,6 @@ const decideSublinkPosition = ( return alignment === 'vertical' ? 'inner' : 'outer'; }; -const liveBulletStyles = css` - width: 9px; - height: 9px; - border-radius: 50%; - background-color: ${palette('--pill-bullet')}; - margin-right: ${space[1]}px; -`; - export const Card = ({ linkTo, format, @@ -431,143 +420,6 @@ export const Card = ({ */ const isVideoArticle = format.design === ArticleDesign.Video; - const isLabs = format.theme === ArticleSpecial.Labs; - - const decideAge = () => { - if (!webPublicationDate) return undefined; - const withinTwelveHours = isWithinTwelveHours(webPublicationDate); - - const shouldShowAge = - isStorylines || - isTagPage || - !!onwardsSource || - (showAge && withinTwelveHours); - - if (!shouldShowAge) return undefined; - - return ( - - ); - }; - - const CommentCount = () => - !!discussionId && ( - - - - - - ); - - const MediaOrNewsletterPill = () => ( -
- {/* Usually, we either display the pill or the footer, - but if the card appears in the storylines section on tag pages - then we do want to display the date on these cards as well as the media pill. - */} - {isStorylines && ( - } - cardBranding={ - isOnwardContent ? : undefined - } - showLivePlayable={showLivePlayable} - /> - )} - - {mainMedia?.type === 'YoutubeVideo' && isVideoArticle && ( - <> - {mainMedia.isLive ? ( - } - /> - ) : ( - } - prefix="Video" - /> - )} - - )} - {mainMedia?.type === 'Audio' && ( - } - prefix="Podcast" - /> - )} - {mainMedia?.type === 'Gallery' && ( - } - prefix="Gallery" - /> - )} - {mainMedia?.type === 'SelfHostedVideo' && - (format.design === ArticleDesign.Video ? ( - } - prefix="Video" - /> - ) : format.design === ArticleDesign.Audio ? ( - } - prefix="Podcast" - /> - ) : format.design === ArticleDesign.Gallery ? ( - } prefix="Gallery" /> - ) : null)} - {isNewsletter && } -
- ); - if (snapData?.embedHtml) { return ( @@ -873,7 +725,7 @@ export const Card = ({ alignment="end" ophanComponentLink={dataAttributes?.ophanComponentLink} ophanComponentName={dataAttributes?.ophanComponentName} - isLabs={isLabs} + isLabs={format.theme === ArticleSpecial.Labs} dataTestId="card-branding-logo" /> @@ -896,7 +748,7 @@ export const Card = ({ alignment="end" ophanComponentLink={dataAttributes?.ophanComponentLink} ophanComponentName={dataAttributes?.ophanComponentName} - isLabs={isLabs} + isLabs={format.theme === ArticleSpecial.Labs} /> @@ -1300,33 +1152,28 @@ export const Card = ({ /> )} + {/** Footer rendered inside the card boundary */} {!isOpinionCardWithAvatar && ( - <> - {showPill ? ( - <> - {!!branding && - format.theme === - ArticleSpecial.Labs && - isOnwardContent && ( - - )} - - - ) : ( - } - cardBranding={ - isOnwardContent ? ( - - ) : undefined - } - showLivePlayable={showLivePlayable} - /> - )} - + } + mainMedia={mainMedia} + isNewsletter={isNewsletter} + showPill={showPill} + isStorylines={isStorylines} + onwardsSource={onwardsSource} + webPublicationDate={webPublicationDate} + showClock={showClock} + serverTime={serverTime} + isTagPage={isTagPage} + discussionId={discussionId} + discussionApiUrl={discussionApiUrl} + linkTo={linkTo} + /> )} + {showLivePlayable && liveUpdatesPosition === 'inner' && ( } showLivePlayable={showLivePlayable} + showAge={showAge} + cardBranding={} + mainMedia={mainMedia} + isNewsletter={isNewsletter} shouldReserveSpace={{ mobile: avatarPosition.mobile === 'bottom', desktop: avatarPosition.desktop === 'bottom', }} + showPill={showPill} + isStorylines={isStorylines} + onwardsSource={onwardsSource} + webPublicationDate={webPublicationDate} + showClock={showClock} + serverTime={serverTime} + isTagPage={isTagPage} + discussionId={discussionId} + discussionApiUrl={discussionApiUrl} + linkTo={linkTo} /> )} - - {!isOnwardContent && format.theme === ArticleSpecial.Labs && ( - - )} ); }; diff --git a/dotcom-rendering/src/components/Card/components/CardFooterNew.tsx b/dotcom-rendering/src/components/Card/components/CardFooterNew.tsx new file mode 100644 index 00000000000..327e488249c --- /dev/null +++ b/dotcom-rendering/src/components/Card/components/CardFooterNew.tsx @@ -0,0 +1,291 @@ +import { css } from '@emotion/react'; +import { + from, + palette as sourcePalette, + space, + textSansBold12, +} from '@guardian/source/foundations'; +import { Link, SvgCamera } from '@guardian/source/react-components'; +import { + ArticleDesign, + type ArticleFormat, + ArticleSpecial, +} from '../../../lib/articleFormat'; +import { + isWithinTwelveHours, + secondsToDuration, +} from '../../../lib/formatTime'; +import { getZIndex } from '../../../lib/getZIndex'; +import { DISCUSSION_ID_DATA_ATTRIBUTE } from '../../../lib/useCommentCount'; +import { palette } from '../../../palette'; +import type { MainMedia } from '../../../types/mainMedia'; +import type { OnwardsSource } from '../../../types/onwards'; +import { CardCommentCount } from '../../CardCommentCount.importable'; +import { Island } from '../../Island'; +import { Pill } from '../../Pill'; +import { SvgMediaControlsPlay } from '../../SvgMediaControlsPlay'; +import { CardAge } from './CardAge'; + +const contentStyles = css` + margin-top: auto; + padding-top: ${space[1]}px; + display: flex; + justify-content: 'flex-start'; + width: fit-content; + align-items: center; + ${textSansBold12} +`; + +const dividerStyles = css` + > { + /* The dividing line is applied only to the second child. This ensures that no + dividing line is added when there is only one child in the container. */ + :nth-child(2) { + ::before { + content: ''; + display: block; + width: 1px; + height: 12px; + position: absolute; + bottom: 0; + left: 0; + background-color: ${sourcePalette.neutral[60]}; + margin-right: ${space[1]}px; + } + margin-left: ${space[1]}px; + padding-left: ${space[1]}px; + } + } +`; + +const reserveSpaceStyles = (mobile: boolean, desktop: boolean) => css` + min-height: ${mobile ? '14px' : 0}; + + ${from.tablet} { + min-height: ${desktop ? '14px' : 0}; + } +`; + +type Props = { + format: ArticleFormat; + showLivePlayable: boolean; + cardBranding?: JSX.Element; + mainMedia?: MainMedia; + isNewsletter?: boolean; + shouldReserveSpace?: { mobile: boolean; desktop: boolean }; + showAge?: boolean; + showPill?: boolean; + isStorylines: boolean; + onwardsSource?: OnwardsSource; + webPublicationDate?: string; + showClock?: boolean; + serverTime?: number; + isTagPage: boolean; + discussionId?: string; + discussionApiUrl: string; + linkTo: string; +}; + +const basePillStyles = css` + margin-top: auto; + display: flex; +`; +const storylinesPillStyles = css` + flex-direction: column; + gap: ${space[1]}px; + align-items: flex-start; +`; +const liveBulletStyles = css` + width: 9px; + height: 9px; + border-radius: 50%; + background-color: ${palette('--pill-bullet')}; + margin-right: ${space[1]}px; +`; + +const MediaOrNewsletterPill = ({ + format, + mainMedia, + isNewsletter, + isStorylines, +}: Pick) => ( +
+ {mainMedia?.type === 'YoutubeVideo' && + format.design === ArticleDesign.Video && ( + <> + {mainMedia.duration === 0 ? ( + } + /> + ) : ( + + {secondsToDuration(mainMedia.duration)} + + } + icon={} + prefix="Video" + /> + )} + + )} + + {mainMedia?.type === 'Audio' && ( + {mainMedia.duration}} + icon={} + prefix="Podcast" + /> + )} + + {mainMedia?.type === 'Gallery' && ( + } + prefix="Gallery" + /> + )} + + {mainMedia?.type === 'SelfHostedVideo' && + (format.design === ArticleDesign.Video ? ( + } + prefix="Video" + /> + ) : format.design === ArticleDesign.Audio ? ( + } + prefix="Podcast" + /> + ) : format.design === ArticleDesign.Gallery ? ( + } prefix="Gallery" /> + ) : null)} + + {isNewsletter && } +
+); + +export const CardFooter = ({ + format, + showLivePlayable, + showAge, + cardBranding, + mainMedia, + isNewsletter, + shouldReserveSpace, + showPill, + isStorylines, + onwardsSource, + webPublicationDate, + showClock, + serverTime, + isTagPage, + discussionId, + discussionApiUrl, + linkTo, +}: Props) => { + // We don't show the footer for live playable cards + if (showLivePlayable) return undefined; + + const canShowBranding = + !!cardBranding && format.theme === ArticleSpecial.Labs; + + const getAge = () => { + if (!webPublicationDate) return undefined; + const withinTwelveHours = isWithinTwelveHours(webPublicationDate); + + const shouldShowAge = + isStorylines || + isTagPage || + (!!onwardsSource && !canShowBranding) || + (showAge && withinTwelveHours); + + if (!shouldShowAge) return undefined; + + return ( + + ); + }; + + const getCommentCount = () => + !!discussionId && ( + + + + + + ); + + return ( + + ); +};