diff --git a/dotcom-rendering/src/components/ArticleHeadline.tsx b/dotcom-rendering/src/components/ArticleHeadline.tsx index bcfd9d920b0..babb0ff536b 100644 --- a/dotcom-rendering/src/components/ArticleHeadline.tsx +++ b/dotcom-rendering/src/components/ArticleHeadline.tsx @@ -18,6 +18,7 @@ import { until, } from '@guardian/source/foundations'; import { grid } from '../../src/grid'; +import { type LayoutType } from '../layouts/lib/articleArrangements'; import { interactiveLegacyClasses } from '../layouts/lib/interactiveLegacyStyling'; import { getAgeWarning } from '../lib/age-warning'; import { @@ -38,6 +39,7 @@ import { StarRating } from './StarRating/StarRating'; type Props = { headlineString: string; format: ArticleFormat; + layoutType?: LayoutType; byline?: string; tags: TagType[]; webPublicationDateDeprecated: string; @@ -393,14 +395,18 @@ const galleryStyles = css` export const ArticleHeadline = ({ headlineString, format, + layoutType, tags, byline, webPublicationDateDeprecated, hasAvatar, isMatch, starRating, - isInverted, }: Props) => { + const isInverted = + format.display === ArticleDisplay.Immersive && + format.design === ArticleDesign.Standard && + layoutType === 'immersiveLandscapeDefault'; switch (format.display) { case ArticleDisplay.Immersive: { switch (format.design) { diff --git a/dotcom-rendering/src/components/ArticleMeta.apps.tsx b/dotcom-rendering/src/components/ArticleMeta.apps.tsx index de5333c68e5..0eae4a9da8c 100644 --- a/dotcom-rendering/src/components/ArticleMeta.apps.tsx +++ b/dotcom-rendering/src/components/ArticleMeta.apps.tsx @@ -4,6 +4,7 @@ import { from, space, until } from '@guardian/source/foundations'; import { StraightLines } from '@guardian/source-development-kitchen/react-components'; import type { ReactNode } from 'react'; import { grid } from '../grid'; +import type { LayoutType } from '../layouts/lib/articleArrangements'; import { interactiveLegacyClasses } from '../layouts/lib/interactiveLegacyStyling'; import { ArticleDesign, @@ -36,6 +37,7 @@ import { PodcastMeta } from './PodcastMeta'; type Props = { format: ArticleFormat; + layoutType?: LayoutType; byline?: string; tags: TagType[]; primaryDateline: string; @@ -242,6 +244,7 @@ const galleryMetaContainer = css` export const ArticleMetaApps = ({ branding, format, + layoutType, byline, tags, primaryDateline, @@ -255,7 +258,7 @@ export const ArticleMetaApps = ({ }: Props) => { const soleContributor = getSoleContributor(tags, byline); const authorName = soleContributor?.title ?? 'Author Image'; - const avatarUrl = shouldShowAvatar(format) + const avatarUrl = shouldShowAvatar(format, layoutType) ? soleContributor?.bylineLargeImageUrl : undefined; @@ -328,7 +331,7 @@ export const ArticleMetaApps = ({ )} - {shouldShowContributor(format) && !!byline && ( + {shouldShowContributor(format, layoutType) && !!byline && ( - hasButton - ? { - marginBottom: 8, - } - : {}; + hasButton ? { marginBottom: 8 } : {}; const stretchLines = css` display: block; @@ -152,11 +150,7 @@ const metaExtras = (isPictureContent: boolean) => css` `; const preferredSourceMetaExtras = (hasButton: boolean): CSSProperties => - hasButton - ? { - paddingTop: 8, - } - : {}; + hasButton ? { paddingTop: 8 } : {}; const metaNumbers = (isPictureContent: boolean) => css` border-top: 1px solid ${themePalette('--article-border')}; @@ -249,6 +243,25 @@ const MetaAvatarContainer = ({ children }: { children: React.ReactNode }) => ( ); +const ImmersiveMetaAvatarContainer = ({ + children, +}: { + children: React.ReactNode; +}) => ( +
+ {children} +
+); + const RowBelowLeftCol = ({ children }: { children: React.ReactNode }) => (
- <> + {isImmersive && isPortraitOrLandscapeImmersive ? ( +
{!!avatarUrl && ( - + - + )}
{isAudio && podcast && seriesTag && ( @@ -372,7 +399,7 @@ export const ArticleMeta = ({ /> )} - {shouldShowContributor(format) && ( + {shouldShowContributor(format, layoutType) && ( )}
- - +
+ ) : ( + + <> + {!!avatarUrl && ( + + + + )} +
+ {isAudio && podcast && seriesTag && ( + + )} + {shouldShowContributor(format, layoutType) && ( + + )} + + {crossword?.creator && ( + + )} + + {!isUndefined(webPublicationDate) && + isFilterArticle ? ( + + ) : ( + + )} +
+ +
+ )}
); } - // Immersives show nothing at all if there's no series tag + if ( + layoutType === 'immersivePortraitDefault' || + layoutType === 'immersivePortraitFeature' || + layoutType === 'immersiveLandscapeDefault' || + layoutType === 'immersiveLandscapeFeature' + ) { + return ( + <> + + {sectionLabel} + + + ); + } + // Other types of immersives show nothing at all if there's no series tag return null; } if (tag) { diff --git a/dotcom-rendering/src/layouts/StandardLayout.tsx b/dotcom-rendering/src/layouts/StandardLayout.tsx index 95318a5727e..0635b64a37d 100644 --- a/dotcom-rendering/src/layouts/StandardLayout.tsx +++ b/dotcom-rendering/src/layouts/StandardLayout.tsx @@ -62,6 +62,8 @@ export const StandardLayout = (props: WebProps | AppProps) => { const isWeb = renderingTarget === 'Web'; const isApps = renderingTarget === 'Apps'; + const contentLayoutName = `${ArticleDisplay[format.display]}Layout`; + // TODO: // 1) Read 'forceEpic' value from URL parameter and use it to force the slot to render // 2) Otherwise, ensure slot only renders if `article.config.shouldHideReaderRevenue` equals false. @@ -159,7 +161,7 @@ export const StandardLayout = (props: WebProps | AppProps) => { )} -
+
{isApps && renderAds && ( @@ -170,10 +172,12 @@ export const StandardLayout = (props: WebProps | AppProps) => {
+ {isWeb && renderAds && !isLabs && (
{ /> - {renderAds && ( - - )} + )} diff --git a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx index dca96029476..17dcaeb422d 100644 --- a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx +++ b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx @@ -98,10 +98,12 @@ const GridItem = ({ export const StandardLayoutArticleGrid = ({ article, + contentLayoutName, format, renderingTarget, }: { article: ArticleDeprecated; + contentLayoutName: string; format: ArticleFormat; renderingTarget: RenderingTarget; }) => { @@ -134,6 +136,8 @@ export const StandardLayoutArticleGrid = ({ const isImmersive = format.display === ArticleDisplay.Immersive; const isFeature = format.design === ArticleDesign.Feature; + const isVideo = format.design === ArticleDesign.Video; + const footballMatchUrl = article.matchType === 'FootballMatchType' ? article.matchUrl @@ -157,10 +161,9 @@ export const StandardLayoutArticleGrid = ({ isImmersive, isFeature, orientation: mainMediaOrientation, - isMedia, + isVideo, isShowcase, }); - const contentLayoutName = `${ArticleDisplay[format.display]}Layout`; const isImmersivePortrait = layoutType === 'immersivePortraitDefault' || @@ -203,6 +206,30 @@ export const StandardLayoutArticleGrid = ({ grid-template-rows: 0.25fr 1fr auto; } `, + isImmersiveLandscape && + css` + ${from.desktop} { + grid-template-rows: auto auto ${ageWarning + ? '130px' + : '90px'} auto auto auto auto auto; + ${grid.centreRule( + isImmersivePortrait + ? 4 + : isImmersiveLandscape + ? layoutType === + 'immersiveLandscapeFeature' + ? 3 + : 4 + : 1, + )} + } + `, + isImmersivePortrait && + css` + ${from.desktop} { + grid-template-rows: 0.25fr 1fr auto; + } + `, isImmersiveLandscape && css` ${from.desktop} { @@ -261,6 +288,7 @@ export const StandardLayoutArticleGrid = ({ > - {format.design !== ArticleDesign.Audio && - layoutType !== 'immersivePortraitDefault' && ( + {layoutType !== 'immersivePortraitDefault' && + layoutType !== 'immersivePortraitFeature' && + layoutType !== 'immersiveLandscapeDefault' && + layoutType !== 'immersiveLandscapeFeature' && (
{isWeb && format.theme === ArticleSpecial.Labs && @@ -371,6 +402,7 @@ export const StandardLayoutArticleGrid = ({ { if (isImmersive) { @@ -404,7 +404,7 @@ export const getLayoutType = ({ ? 'immersiveLandscapeFeature' : 'immersiveLandscapeDefault'; } - if (isMedia) return 'media'; + if (isVideo) return 'media'; if (isShowcase) return 'showcase'; return 'standard'; }; diff --git a/dotcom-rendering/src/lib/articleMeta.test.ts b/dotcom-rendering/src/lib/articleMeta.test.ts index e6b8603d849..49c7206577d 100644 --- a/dotcom-rendering/src/lib/articleMeta.test.ts +++ b/dotcom-rendering/src/lib/articleMeta.test.ts @@ -24,6 +24,28 @@ describe('shouldShowContributor', () => { display: ArticleDisplay.NumberedList, }; + const immersive = { + ...standardFormat, + display: ArticleDisplay.Immersive, + }; + + const immersivePortraitDefault = { + ...immersive, + layoutType: 'immersivePortraitDefault', + }; + const immersivePortraitFeature = { + ...immersive, + layoutType: 'immersivePortraitFeature', + }; + const immersiveLandscapeDefault = { + ...immersive, + layoutType: 'immersiveLandscapeDefault', + }; + const immersiveLandscapeFeature = { + ...immersive, + layoutType: 'immersiveLandscapeFeature', + }; + it('should return true if Standard display and Standard design', () => { expect(shouldShowContributor(standardFormat)).toBe(true); }); @@ -43,4 +65,24 @@ describe('shouldShowContributor', () => { it('should return true if Numbered list display', () => { expect(shouldShowContributor(numberedList)).toBe(true); }); + + it('should return false if Immersive display', () => { + expect(shouldShowContributor(immersive)).toBe(false); + }); + + it('should return true if Immersive display and immersivePortraitDefault layout', () => { + expect(shouldShowContributor(immersivePortraitDefault)).toBe(true); + }); + + it('should return true if Immersive display and immersivePortraitFeature layout', () => { + expect(shouldShowContributor(immersivePortraitFeature)).toBe(true); + }); + + it('should return true if Immersive display and immersiveLandscapeDefault layout', () => { + expect(shouldShowContributor(immersiveLandscapeDefault)).toBe(true); + }); + + it('should return true if Immersive display and immersiveLandscapeFeature layout', () => { + expect(shouldShowContributor(immersiveLandscapeFeature)).toBe(true); + }); }); diff --git a/dotcom-rendering/src/lib/articleMeta.ts b/dotcom-rendering/src/lib/articleMeta.ts index cda9d7de818..6f95ffe66be 100644 --- a/dotcom-rendering/src/lib/articleMeta.ts +++ b/dotcom-rendering/src/lib/articleMeta.ts @@ -1,3 +1,4 @@ +import type { LayoutType } from '../layouts/lib/articleArrangements'; import type { Podcast, TagType } from '../types/tag'; import { ArticleDesign, @@ -5,10 +6,21 @@ import { type ArticleFormat, } from './articleFormat'; -export const shouldShowAvatar = (format: ArticleFormat): boolean => { +export const shouldShowAvatar = ( + format: ArticleFormat, + layoutType?: LayoutType, +): boolean => { switch (format.display) { case ArticleDisplay.Immersive: - return false; + switch (layoutType) { + case 'immersivePortraitDefault': + case 'immersivePortraitFeature': + case 'immersiveLandscapeDefault': + case 'immersiveLandscapeFeature': + return true; + default: + return false; + } case ArticleDisplay.Showcase: case ArticleDisplay.NumberedList: case ArticleDisplay.Standard: { @@ -27,11 +39,23 @@ export const shouldShowAvatar = (format: ArticleFormat): boolean => { } }; -export const shouldShowContributor = (format: ArticleFormat): boolean => { +export const shouldShowContributor = ( + format: ArticleFormat, + layoutType?: LayoutType, +): boolean => { switch (format.display) { case ArticleDisplay.NumberedList: return true; case ArticleDisplay.Immersive: + switch (layoutType) { + case 'immersivePortraitDefault': + case 'immersivePortraitFeature': + case 'immersiveLandscapeDefault': + case 'immersiveLandscapeFeature': + return true; + default: + return false; + } case ArticleDisplay.Showcase: case ArticleDisplay.Standard: { switch (format.design) {