From 96d428ae8e2061f576014a3a4d3c1066ba8b766e Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Thu, 11 Jun 2026 17:54:02 +0100 Subject: [PATCH 1/3] Move AudioLayout to grid --- .../src/layouts/AudioLayout.stories.tsx | 6 +- dotcom-rendering/src/layouts/AudioLayout.tsx | 757 ------------------ dotcom-rendering/src/layouts/DecideLayout.tsx | 5 +- .../src/layouts/StandardLayout.tsx | 82 +- 4 files changed, 67 insertions(+), 783 deletions(-) delete mode 100644 dotcom-rendering/src/layouts/AudioLayout.tsx diff --git a/dotcom-rendering/src/layouts/AudioLayout.stories.tsx b/dotcom-rendering/src/layouts/AudioLayout.stories.tsx index 9227a2e43d9..9b13f7271c8 100644 --- a/dotcom-rendering/src/layouts/AudioLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/AudioLayout.stories.tsx @@ -5,12 +5,12 @@ import { ArticleDesign } from '../lib/articleFormat'; import { getCurrentPillar } from '../lib/layoutHelpers'; import { extractNAV } from '../model/extract-nav'; import { enhanceArticleType } from '../types/article'; -import { AudioLayout } from './AudioLayout'; +import { StandardLayout } from './StandardLayout'; const meta = { title: 'Layouts/Audio', - component: AudioLayout, -} satisfies Meta; + component: StandardLayout, +} satisfies Meta; export default meta; diff --git a/dotcom-rendering/src/layouts/AudioLayout.tsx b/dotcom-rendering/src/layouts/AudioLayout.tsx deleted file mode 100644 index 94aebed7489..00000000000 --- a/dotcom-rendering/src/layouts/AudioLayout.tsx +++ /dev/null @@ -1,757 +0,0 @@ -import { css } from '@emotion/react'; -import { - from, - palette as sourcePalette, - space, - until, -} from '@guardian/source/foundations'; -import { StraightLines } from '@guardian/source-development-kitchen/react-components'; -import { AdPortals } from '../components/AdPortals.island'; -import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; -import { AffiliateDisclaimer } from '../components/AffiliateDisclaimer'; -import { AppsAudioPlayer } from '../components/AppsAudioPlayer.island'; -import { AppsFooter } from '../components/AppsFooter.island'; -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 { AudioPlayerWrapper } from '../components/AudioPlayerWrapper.island'; -import { Border } from '../components/Border'; -import { Carousel } from '../components/Carousel.island'; -import { DirectoryPageNavIsland } from '../components/DirectoryPageNavIsland'; -import { DiscussionLayout } from '../components/DiscussionLayout'; -import { Footer } from '../components/Footer'; -import { GridItem } from '../components/GridItem'; -import { HeaderAdSlot } from '../components/HeaderAdSlot'; -import { Island } from '../components/Island'; -import { LabsHeader } from '../components/LabsHeader'; -import { formatAudioDuration } from '../components/ListenToArticle.island'; -import { Masthead } from '../components/Masthead/Masthead'; -import { MostViewedFooterData } from '../components/MostViewedFooterData.island'; -import { MostViewedFooterLayout } from '../components/MostViewedFooterLayout'; -import { MostViewedRightWithAd } from '../components/MostViewedRightWithAd.island'; -import { OnwardsUpper } from '../components/OnwardsUpper.island'; -import { RightColumn } from '../components/RightColumn'; -import { Section } from '../components/Section'; -import { SlotBodyEnd } from '../components/SlotBodyEnd.island'; -import { Standfirst } from '../components/Standfirst'; -import { StickyBottomBanner } from '../components/StickyBottomBanner.island'; -import { SubMeta } from '../components/SubMeta'; -import { SubNav } from '../components/SubNav.island'; -import { type ArticleFormat, ArticleSpecial } from '../lib/articleFormat'; -import { getAudioData } from '../lib/audio-data'; -import { canRenderAds } from '../lib/canRenderAds'; -import { getContributionsServiceUrl } from '../lib/contributions'; -import { decideStoryPackageTrails } from '../lib/decideTrail'; -import { parse } from '../lib/slot-machine-flags'; -import { worldCupTagId } from '../lib/worldCup2026'; -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 { BannerWrapper, Stuck } from './lib/stickiness'; - -const AudioGrid = ({ children }: { children: React.ReactNode }) => ( -
- {children} -
-); - -const maxWidth = css` - ${from.desktop} { - max-width: 620px; - } -`; - -interface Props { - article: ArticleDeprecated; - format: ArticleFormat; - renderingTarget: RenderingTarget; - serverTime?: number; -} - -interface WebProps extends Props { - NAV: NavType; - renderingTarget: 'Web'; -} - -interface AppProps extends Props { - renderingTarget: 'Apps'; -} - -export const AudioLayout = (props: WebProps | AppProps) => { - const { article, format, renderingTarget, serverTime } = props; - const isWeb = renderingTarget === 'Web'; - const isApps = renderingTarget === 'Apps'; - const audioData = getAudioData(article.mainMediaElements); - - const { - config: { isPaidContent, host, hasSurveyAd }, - editionId, - } = article; - - const showBodyEndSlot = parse(article.slotMachineFlags ?? '').showBodyEnd; - - const showComments = article.isCommentable && !isPaidContent; - - const { branding } = article.commercialProperties[article.editionId]; - - const contributionsServiceUrl = getContributionsServiceUrl(article); - - const isLabs = format.theme === ArticleSpecial.Labs; - - const isWorldCup2026 = article.tags.some((tag) => tag.id === worldCupTagId); - - const renderAds = canRenderAds(article); - - return ( - <> - {isWeb && ( -
- {renderAds && ( - -
- -
-
- )} - tag.id)} - contentType={article.contentType} - /> -
- )} - - {isWeb && format.theme === ArticleSpecial.Labs && ( - -
- -
-
- )} - - {isWeb && renderAds && hasSurveyAd && ( - - )} - -
- {isApps && renderAds && ( - - - - )} - - -
- - - - - - {format.theme === ArticleSpecial.Labs ? ( - <> - ) : ( - - )} - - -
- -
-
- -
- {renderingTarget === 'Web' ? ( - - ) : ( - - )} - {!!article.affiliateLinksDisclaimer && ( - - )} -
-
- - {isWeb && audioData && ( - - - - )} - {isApps && audioData && ( - - - - )} - - - - - - - - - {isWeb && showBodyEndSlot && ( - - - - )} - - - - - - {isWeb && ( -
- - - - - -
- )} -
-
-
- - {isWeb && renderAds && !isLabs && ( -
- -
- )} - - {article.storyPackage && ( -
- - - -
- )} - - - - - {isWeb && showComments && ( -
- -
- )} - - {!isPaidContent && ( -
- - - - - -
- )} - - {isWeb && renderAds && !isLabs && ( -
- -
- )} -
- - {isWeb && ( - <> - {props.NAV.subNavSections && ( -
- - - -
- )} -
-
-
- - - - - - {renderAds && ( - - )} - - )} - {isApps && ( -
- - - -
- )} - - ); -}; diff --git a/dotcom-rendering/src/layouts/DecideLayout.tsx b/dotcom-rendering/src/layouts/DecideLayout.tsx index b6dd7f4a27c..7724a3dbd64 100644 --- a/dotcom-rendering/src/layouts/DecideLayout.tsx +++ b/dotcom-rendering/src/layouts/DecideLayout.tsx @@ -2,7 +2,6 @@ import { ArticleDesign, ArticleDisplay } from '../lib/articleFormat'; import type { NavType } from '../model/extract-nav'; import type { Article } from '../types/article'; import type { RenderingTarget } from '../types/renderingTarget'; -import { AudioLayout } from './AudioLayout'; import { CommentLayout } from './CommentLayout'; import { CrosswordLayout } from './CrosswordLayout'; import { FullPageInteractiveLayout } from './FullPageInteractiveLayout'; @@ -191,7 +190,7 @@ const DecideLayoutApps = ({ article, renderingTarget }: AppProps) => { ); case ArticleDesign.Audio: return ( - { ); case ArticleDesign.Audio: return ( - { const renderAds = canRenderAds(article); + const audioData = getAudioData(article.mainMediaElements); + const layoutType: LayoutType = isMedia ? 'media' : isShowcase @@ -283,25 +291,59 @@ export const StandardLayout = (props: WebProps | AppProps) => { ]} > - + {audioData ? ( + <> + {isApps ? ( + + ) : ( + + )} + + + ) : ( + + )} Date: Tue, 23 Jun 2026 14:27:33 +0100 Subject: [PATCH 2/3] Create AudioPlayer component --- .../src/components/AudioPlayer.tsx | 52 +++++++++++++++++++ .../src/layouts/StandardLayout.tsx | 49 ++++------------- 2 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 dotcom-rendering/src/components/AudioPlayer.tsx diff --git a/dotcom-rendering/src/components/AudioPlayer.tsx b/dotcom-rendering/src/components/AudioPlayer.tsx new file mode 100644 index 00000000000..ddb489b8f96 --- /dev/null +++ b/dotcom-rendering/src/components/AudioPlayer.tsx @@ -0,0 +1,52 @@ +import { css } from '@emotion/react'; +import { space } from '@guardian/source/foundations'; +import { StraightLines } from '@guardian/source-development-kitchen/react-components'; +import { palette } from '../palette'; +import { AppsAudioPlayer } from './AppsAudioPlayer.island'; +import { AudioPlayerWrapper } from './AudioPlayerWrapper.island'; +import { formatAudioDuration } from './ListenToArticle.island'; + +export const AudioPlayer = ({ + audioData, + isSensitive, + isAcastEnabled, + isApps, +}: { + audioData: { + audioDownloadUrl: string; + durationSeconds?: number; + mediaId: string; + }; + isSensitive: boolean; + isAcastEnabled: boolean; + isApps: boolean; +}) => { + return ( + <> + {isApps ? ( + + ) : ( + + )} + + + ); +}; diff --git a/dotcom-rendering/src/layouts/StandardLayout.tsx b/dotcom-rendering/src/layouts/StandardLayout.tsx index 4ef12ebfdde..b8c6c3ed054 100644 --- a/dotcom-rendering/src/layouts/StandardLayout.tsx +++ b/dotcom-rendering/src/layouts/StandardLayout.tsx @@ -11,7 +11,6 @@ import { StraightLines } from '@guardian/source-development-kitchen/react-compon import { AdPortals } from '../components/AdPortals.island'; import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; import { AffiliateDisclaimer } from '../components/AffiliateDisclaimer'; -import { AppsAudioPlayer } from '../components/AppsAudioPlayer.island'; import { AppsEpic } from '../components/AppsEpic.island'; import { AppsFooter } from '../components/AppsFooter.island'; import { ArticleBody } from '../components/ArticleBody'; @@ -20,7 +19,7 @@ import { ArticleHeadline } from '../components/ArticleHeadline'; import { ArticleMetaApps } from '../components/ArticleMeta.apps'; import { ArticleMeta } from '../components/ArticleMeta.web'; import { ArticleTitle } from '../components/ArticleTitle'; -import { AudioPlayerWrapper } from '../components/AudioPlayerWrapper.island'; +import { AudioPlayer } from '../components/AudioPlayer'; import { Carousel } from '../components/Carousel.island'; import { CricketMatchHeaderWrapper } from '../components/CricketMatchHeaderWrapper.island'; import { DecideLines } from '../components/DecideLines'; @@ -33,10 +32,7 @@ import { GuardianLabsLines } from '../components/GuardianLabsLines'; import { HeaderAdSlot } from '../components/HeaderAdSlot'; import { Island } from '../components/Island'; import { LabsHeader } from '../components/LabsHeader'; -import { - formatAudioDuration, - ListenToArticle, -} from '../components/ListenToArticle.island'; +import { ListenToArticle } from '../components/ListenToArticle.island'; import { MainMedia } from '../components/MainMedia'; import { Masthead } from '../components/Masthead/Masthead'; import { MatchHeaderFallback } from '../components/MatchHeaderFallback'; @@ -292,39 +288,14 @@ export const StandardLayout = (props: WebProps | AppProps) => { > {audioData ? ( - <> - {isApps ? ( - - ) : ( - - )} - - + ) : ( Date: Tue, 23 Jun 2026 15:06:13 +0100 Subject: [PATCH 3/3] Conditionally render strech lines, turn of most viewed --- .../src/layouts/StandardLayout.tsx | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/dotcom-rendering/src/layouts/StandardLayout.tsx b/dotcom-rendering/src/layouts/StandardLayout.tsx index b8c6c3ed054..d90c9546257 100644 --- a/dotcom-rendering/src/layouts/StandardLayout.tsx +++ b/dotcom-rendering/src/layouts/StandardLayout.tsx @@ -167,8 +167,6 @@ export const StandardLayout = (props: WebProps | AppProps) => { format.design === ArticleDesign.Video || format.design === ArticleDesign.Audio; - const isVideo = format.design === ArticleDesign.Video; - const isShowcase = format.display === ArticleDisplay.Showcase; const showComments = article.isCommentable && !isPaidContent; @@ -312,7 +310,9 @@ export const StandardLayout = (props: WebProps | AppProps) => { hideCaption={isMedia} shouldHideAds={article.shouldHideAds} contentType={article.contentType} - contentLayout="StandardLayout" + contentLayout={`${ + ArticleDisplay[format.display] + }Layout`} /> )} @@ -353,18 +353,22 @@ export const StandardLayout = (props: WebProps | AppProps) => { layoutType={layoutType} element="aside" > -
- {isWeb && - format.theme === ArticleSpecial.Labs && - format.design !== ArticleDesign.Video ? ( - - ) : ( - - )} -
+ {!audioData && ( +
+ {isWeb && + format.theme === ArticleSpecial.Labs && + format.design !== ArticleDesign.Video ? ( + + ) : ( + + )} +
+ )} {isApps ? ( <> @@ -465,7 +469,7 @@ export const StandardLayout = (props: WebProps | AppProps) => { {/* Only show Listen to Article button on App landscape views */} {isApps && ( - {!isVideo && ( + {!isMedia && (
{ !!article.config .shouldHideReaderRevenue } + shouldHideMostViewed={!!audioData} />