From bfde07d3c4ded4949fb1e9226052a7e1e8fcf558 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Wed, 21 Jan 2026 14:19:15 +0000 Subject: [PATCH] Sketch out interactive layout using grid --- .../src/layouts/FullPageInteractiveLayout.tsx | 2 +- .../src/layouts/InteractiveLayout.tsx | 898 +++++------------- .../layouts/InteractiveLayoutDeprecated.tsx | 845 ++++++++++++++++ 3 files changed, 1094 insertions(+), 651 deletions(-) create mode 100644 dotcom-rendering/src/layouts/InteractiveLayoutDeprecated.tsx diff --git a/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx b/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx index 60854f86399..20482ac89cb 100644 --- a/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx +++ b/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx @@ -27,7 +27,7 @@ import type { ArticleDeprecated } from '../types/article'; import type { ServerSideTests, Switches } from '../types/config'; import type { FEElement } from '../types/content'; import type { RenderingTarget } from '../types/renderingTarget'; -import { temporaryBodyCopyColourOverride } from './InteractiveLayout'; +import { temporaryBodyCopyColourOverride } from './InteractiveLayoutDeprecated'; import { interactiveGlobalStyles } from './lib/interactiveLegacyStyling'; import { BannerWrapper, Stuck } from './lib/stickiness'; diff --git a/dotcom-rendering/src/layouts/InteractiveLayout.tsx b/dotcom-rendering/src/layouts/InteractiveLayout.tsx index 4bffeb8d5f8..465591dd0b2 100644 --- a/dotcom-rendering/src/layouts/InteractiveLayout.tsx +++ b/dotcom-rendering/src/layouts/InteractiveLayout.tsx @@ -1,28 +1,15 @@ import { css, Global } from '@emotion/react'; -import { isUndefined } from '@guardian/libs'; -import { - from, - palette as sourcePalette, - until, -} from '@guardian/source/foundations'; -import { Hide } from '@guardian/source/react-components'; +import { from, palette as sourcePalette } from '@guardian/source/foundations'; import { StraightLines } from '@guardian/source-development-kitchen/react-components'; -import React from 'react'; -import { AdPortals } from '../components/AdPortals.importable'; import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; import { AppsFooter } from '../components/AppsFooter.importable'; import { ArticleBody } from '../components/ArticleBody'; -import { ArticleContainer } from '../components/ArticleContainer'; import { ArticleHeadline } from '../components/ArticleHeadline'; import { ArticleMetaApps } from '../components/ArticleMeta.apps'; import { ArticleMeta } from '../components/ArticleMeta.web'; import { ArticleTitle } from '../components/ArticleTitle'; -import { Border } from '../components/Border'; -import { Carousel } from '../components/Carousel.importable'; import { DecideLines } from '../components/DecideLines'; -import { DiscussionLayout } from '../components/DiscussionLayout'; import { Footer } from '../components/Footer'; -import { GridItem } from '../components/GridItem'; import { HeaderAdSlot } from '../components/HeaderAdSlot'; import { InteractivesDisableArticleSwipe } from '../components/InteractivesDisableArticleSwipe.importable'; import { InteractivesNativePlatformWrapper } from '../components/InteractivesNativePlatformWrapper.importable'; @@ -30,184 +17,26 @@ import { Island } from '../components/Island'; import { LabsHeader } from '../components/LabsHeader'; import { MainMedia } from '../components/MainMedia'; import { Masthead } from '../components/Masthead/Masthead'; -import { MostViewedFooterData } from '../components/MostViewedFooterData.importable'; -import { MostViewedFooterLayout } from '../components/MostViewedFooterLayout'; -import { OnwardsUpper } from '../components/OnwardsUpper.importable'; import { Section } from '../components/Section'; import { SlotBodyEnd } from '../components/SlotBodyEnd.importable'; import { Standfirst } from '../components/Standfirst'; -import { StarRatingDeprecated } from '../components/StarRating/StarRatingDeprecated'; import { StickyBottomBanner } from '../components/StickyBottomBanner.importable'; import { SubMeta } from '../components/SubMeta'; import { SubNav } from '../components/SubNav.importable'; +import { grid } from '../grid'; import { type ArticleFormat, ArticleSpecial } from '../lib/articleFormat'; import { canRenderAds } from '../lib/canRenderAds'; import { getContributionsServiceUrl } from '../lib/contributions'; -import { decideStoryPackageTrails } from '../lib/decideTrail'; +import type { EditionId } from '../lib/edition'; import type { NavType } from '../model/extract-nav'; import { palette as themePalette } from '../palette'; import type { ArticleDeprecated } from '../types/article'; +import type { Branding } from '../types/branding'; +import type { ConfigType } from '../types/config'; import type { RenderingTarget } from '../types/renderingTarget'; -import { - interactiveGlobalStyles, - interactiveLegacyClasses, -} from './lib/interactiveLegacyStyling'; +import { temporaryBodyCopyColourOverride } from './InteractiveLayoutDeprecated'; import { BannerWrapper, Stuck } from './lib/stickiness'; -const InteractiveGrid = ({ children }: { children: React.ReactNode }) => ( -
- {children} -
-); - -const maxWidth = css` - ${from.desktop} { - max-width: 620px; - } -`; - -const stretchLines = css` - ${until.phablet} { - margin-left: -20px; - margin-right: -20px; - } - ${until.mobileLandscape} { - margin-left: -10px; - margin-right: -10px; - } -`; - -const starWrapper = css` - margin-bottom: 18px; - margin-top: 6px; - background-color: ${themePalette('--star-rating-background')}; - color: ${themePalette('--star-rating-fill')}; - display: inline-block; - - ${until.phablet} { - padding-left: 20px; - margin-left: -20px; - } - ${until.leftCol} { - padding-left: 0px; - margin-left: -0px; - } - - padding-left: 10px; - margin-left: -10px; -`; - -export const temporaryBodyCopyColourOverride = css` - .content__main-column--interactive p { - /* stylelint-disable-next-line declaration-no-important */ - color: ${themePalette('--article-text')} !important; - } -`; - interface CommonProps { article: ArticleDeprecated; format: ArticleFormat; @@ -225,16 +54,16 @@ interface AppsProps extends CommonProps { } export const InteractiveLayout = (props: WebProps | AppsProps) => { - const { article, format, renderingTarget, serverTime } = props; + const { article, format, renderingTarget } = props; const { - config: { isPaidContent, host, hasSurveyAd }, + config: { host, hasSurveyAd }, editionId, } = article; const isApps = renderingTarget === 'Apps'; const isWeb = renderingTarget === 'Web'; - const showComments = article.isCommentable && !isPaidContent; + // const showComments = article.isCommentable && !isPaidContent; const { branding } = article.commercialProperties[article.editionId]; @@ -255,43 +84,16 @@ export const InteractiveLayout = (props: WebProps | AppsProps) => { )} - {article.isLegacyInteractive && ( - - )} {isWeb && ( <> -
- {renderAds && ( - -
-
- -
-
-
- )} - - -
+ {format.theme === ArticleSpecial.Labs && ( @@ -312,456 +114,168 @@ export const InteractiveLayout = (props: WebProps | AppsProps) => { )} )} -
- {isApps && renderAds && ( - - - - )} -
-
- - -
- -
-
- -
- -
-
- - {format.theme === ArticleSpecial.Labs ? ( - <> - ) : ( - - )} - - -
- -
- {!isUndefined(article.starRating) ? ( -
- -
- ) : null} -
- - - - -
-
- -
-
-
- -
- {isApps ? ( - <> - - - - - - - - ) : ( - - )} -
-
- - - - - -
-
-
- -
-
- - - +
+ {article.mainMediaElements.length > 0 && ( +
+
-
- -
- -
- -
- +
+ -
- - {isWeb && renderAds && ( -
- -
- )} - - {article.storyPackage && ( -
- - - -
- )} - - - + + + - - - {showComments && ( -
- + +
+
+ + -
- )} - - {!isPaidContent && ( -
- - - - - -
- )} - - {isWeb && renderAds && ( -
- -
- )} -
- + + + + +
+ +
+ +
+ +
{isWeb && props.NAV.subNavSections && (
@@ -843,3 +357,87 @@ export const InteractiveLayout = (props: WebProps | AppsProps) => { ); }; + +const BannerAndMasthead = (props: { + renderAds: boolean; + nav: NavType; + editionId: EditionId; + config: ConfigType; + contributionsServiceUrl: string; + pageId: string | undefined; +}) => ( +
+ {props.renderAds ? ( + +
+ +
+
+ ) : null} + +
+); + +const Meta = ({ + renderingTarget, + format, + article, + branding, +}: { + renderingTarget: RenderingTarget; + format: ArticleFormat; + article: ArticleDeprecated; + branding?: Branding; +}) => { + if (renderingTarget === 'Apps') { + return ( + + ); + } else { + return ( + + ); + } +}; diff --git a/dotcom-rendering/src/layouts/InteractiveLayoutDeprecated.tsx b/dotcom-rendering/src/layouts/InteractiveLayoutDeprecated.tsx new file mode 100644 index 00000000000..108dc4f53f8 --- /dev/null +++ b/dotcom-rendering/src/layouts/InteractiveLayoutDeprecated.tsx @@ -0,0 +1,845 @@ +import { css, Global } from '@emotion/react'; +import { isUndefined } from '@guardian/libs'; +import { + from, + palette as sourcePalette, + until, +} from '@guardian/source/foundations'; +import { Hide } from '@guardian/source/react-components'; +import { StraightLines } from '@guardian/source-development-kitchen/react-components'; +import React from 'react'; +import { AdPortals } from '../components/AdPortals.importable'; +import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; +import { AppsFooter } from '../components/AppsFooter.importable'; +import { ArticleBody } from '../components/ArticleBody'; +import { ArticleContainer } from '../components/ArticleContainer'; +import { ArticleHeadline } from '../components/ArticleHeadline'; +import { ArticleMetaApps } from '../components/ArticleMeta.apps'; +import { ArticleMeta } from '../components/ArticleMeta.web'; +import { ArticleTitle } from '../components/ArticleTitle'; +import { Border } from '../components/Border'; +import { Carousel } from '../components/Carousel.importable'; +import { DecideLines } from '../components/DecideLines'; +import { DiscussionLayout } from '../components/DiscussionLayout'; +import { Footer } from '../components/Footer'; +import { GridItem } from '../components/GridItem'; +import { HeaderAdSlot } from '../components/HeaderAdSlot'; +import { InteractivesDisableArticleSwipe } from '../components/InteractivesDisableArticleSwipe.importable'; +import { InteractivesNativePlatformWrapper } from '../components/InteractivesNativePlatformWrapper.importable'; +import { Island } from '../components/Island'; +import { LabsHeader } from '../components/LabsHeader'; +import { MainMedia } from '../components/MainMedia'; +import { Masthead } from '../components/Masthead/Masthead'; +import { MostViewedFooterData } from '../components/MostViewedFooterData.importable'; +import { MostViewedFooterLayout } from '../components/MostViewedFooterLayout'; +import { OnwardsUpper } from '../components/OnwardsUpper.importable'; +import { Section } from '../components/Section'; +import { SlotBodyEnd } from '../components/SlotBodyEnd.importable'; +import { Standfirst } from '../components/Standfirst'; +import { StarRatingDeprecated } from '../components/StarRating/StarRatingDeprecated'; +import { StickyBottomBanner } from '../components/StickyBottomBanner.importable'; +import { SubMeta } from '../components/SubMeta'; +import { SubNav } from '../components/SubNav.importable'; +import { type ArticleFormat, ArticleSpecial } from '../lib/articleFormat'; +import { canRenderAds } from '../lib/canRenderAds'; +import { getContributionsServiceUrl } from '../lib/contributions'; +import { decideStoryPackageTrails } from '../lib/decideTrail'; +import type { NavType } from '../model/extract-nav'; +import { palette as themePalette } from '../palette'; +import type { ArticleDeprecated } from '../types/article'; +import type { RenderingTarget } from '../types/renderingTarget'; +import { + interactiveGlobalStyles, + interactiveLegacyClasses, +} from './lib/interactiveLegacyStyling'; +import { BannerWrapper, Stuck } from './lib/stickiness'; + +const InteractiveGrid = ({ children }: { children: React.ReactNode }) => ( +
+ {children} +
+); + +const maxWidth = css` + ${from.desktop} { + max-width: 620px; + } +`; + +const stretchLines = css` + ${until.phablet} { + margin-left: -20px; + margin-right: -20px; + } + ${until.mobileLandscape} { + margin-left: -10px; + margin-right: -10px; + } +`; + +const starWrapper = css` + margin-bottom: 18px; + margin-top: 6px; + background-color: ${themePalette('--star-rating-background')}; + color: ${themePalette('--star-rating-fill')}; + display: inline-block; + + ${until.phablet} { + padding-left: 20px; + margin-left: -20px; + } + ${until.leftCol} { + padding-left: 0px; + margin-left: -0px; + } + + padding-left: 10px; + margin-left: -10px; +`; + +export const temporaryBodyCopyColourOverride = css` + .content__main-column--interactive p { + /* stylelint-disable-next-line declaration-no-important */ + color: ${themePalette('--article-text')} !important; + } +`; + +interface CommonProps { + article: ArticleDeprecated; + format: ArticleFormat; + renderingTarget: RenderingTarget; + serverTime?: number; +} + +interface WebProps extends CommonProps { + NAV: NavType; + renderingTarget: 'Web'; +} + +interface AppsProps extends CommonProps { + renderingTarget: 'Apps'; +} + +export const InteractiveLayoutDeprecated = (props: WebProps | AppsProps) => { + const { article, format, renderingTarget, serverTime } = props; + const { + config: { isPaidContent, host, hasSurveyAd }, + editionId, + } = article; + + const isApps = renderingTarget === 'Apps'; + const isWeb = renderingTarget === 'Web'; + + const showComments = article.isCommentable && !isPaidContent; + + const { branding } = article.commercialProperties[article.editionId]; + + const contributionsServiceUrl = getContributionsServiceUrl(article); + + const renderAds = canRenderAds(article); + + return ( + <> + {isApps && ( + <> + + + + + + + + + )} + {article.isLegacyInteractive && ( + + )} + {isWeb && ( + <> +
+ {renderAds && ( + +
+
+ +
+
+
+ )} + + +
+ + {format.theme === ArticleSpecial.Labs && ( + +
+ +
+
+ )} + + {renderAds && hasSurveyAd && ( + + )} + + )} +
+ {isApps && renderAds && ( + + + + )} +
+
+ + +
+ +
+
+ +
+ +
+
+ + {format.theme === ArticleSpecial.Labs ? ( + <> + ) : ( + + )} + + +
+ +
+ {!isUndefined(article.starRating) ? ( +
+ +
+ ) : null} +
+ + + + +
+
+ +
+
+
+ +
+ {isApps ? ( + <> + + + + + + + + ) : ( + + )} +
+
+ + + + + +
+
+
+ +
+
+ + + +
+
+ +
+ +
+ +
+ +
+ + {isWeb && renderAds && ( +
+ +
+ )} + + {article.storyPackage && ( +
+ + + +
+ )} + + + + + + {showComments && ( +
+ +
+ )} + + {!isPaidContent && ( +
+ + + + + +
+ )} + + {isWeb && renderAds && ( +
+ +
+ )} +
+ + {isWeb && props.NAV.subNavSections && ( +
+ + + +
+ )} + + {isWeb && ( + <> +
+
+
+ + + + + + + + + )} + {isApps && ( +
+ + + +
+ )} + + ); +};