From e124998c986a82a1a800278db713666a5f51fd56 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Fri, 3 Jul 2026 11:54:42 +0100 Subject: [PATCH 1/2] implement onward content in basic form for hosted gallery pages --- .../components/FetchHostedOnwards.island.tsx | 8 +- .../src/components/GalleryCaption.tsx | 8 +- .../src/components/GalleryImage.tsx | 16 +++- .../src/components/HostedContentOnwards.tsx | 74 ++++++++++++++----- .../components/HostedContentOnwardsCard.tsx | 44 +++++------ .../layouts/HostedGalleryLayout.stories.tsx | 15 ++++ .../src/layouts/HostedGalleryLayout.tsx | 53 ++++++------- dotcom-rendering/src/paletteDeclarations.ts | 24 ++++-- 8 files changed, 157 insertions(+), 85 deletions(-) diff --git a/dotcom-rendering/src/components/FetchHostedOnwards.island.tsx b/dotcom-rendering/src/components/FetchHostedOnwards.island.tsx index f709d16a8e5..18d6924d6d9 100644 --- a/dotcom-rendering/src/components/FetchHostedOnwards.island.tsx +++ b/dotcom-rendering/src/components/FetchHostedOnwards.island.tsx @@ -6,13 +6,18 @@ import { HostedContentOnwards } from './HostedContentOnwards'; type Props = { url: string; branding?: Branding; + isGalleryPage?: boolean; }; type OnwardsResponse = { trails: TrailType[]; }; -export const FetchHostedOnwards = ({ branding, url }: Props) => { +export const FetchHostedOnwards = ({ + branding, + url, + isGalleryPage = false, +}: Props) => { const { data, error } = useApi(url); if (error) { @@ -31,6 +36,7 @@ export const FetchHostedOnwards = ({ branding, url }: Props) => { ); }; diff --git a/dotcom-rendering/src/components/GalleryCaption.tsx b/dotcom-rendering/src/components/GalleryCaption.tsx index b503b5fd7f9..ddd07129a8b 100644 --- a/dotcom-rendering/src/components/GalleryCaption.tsx +++ b/dotcom-rendering/src/components/GalleryCaption.tsx @@ -55,14 +55,14 @@ const hostedGalleryStyles = css` ${textSansBold17} align-self: end; + ${from.tablet} { + padding-bottom: ${space[10]}px; + } + ${between.tablet.and.desktop} { padding-left: 0; padding-right: 0; } - - ${from.tablet} { - padding-bottom: ${space[10]}px; - } `; const positionIndicatorStyles = css` diff --git a/dotcom-rendering/src/components/GalleryImage.tsx b/dotcom-rendering/src/components/GalleryImage.tsx index 63bc52b8aa9..d48011784e3 100644 --- a/dotcom-rendering/src/components/GalleryImage.tsx +++ b/dotcom-rendering/src/components/GalleryImage.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import { isUndefined } from '@guardian/libs'; import { between, from, space, until } from '@guardian/source/foundations'; import { grid } from '../grid'; -import { type ArticleFormat } from '../lib/articleFormat'; +import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; import { getImage } from '../lib/image'; import { palette } from '../palette'; import type { ImageBlockElement } from '../types/content'; @@ -47,6 +47,12 @@ const styles = css` } `; +const hostedGalleryOverrides = css` + ${between.desktop.and.leftCol} { + ${grid.centreRule(2, 'transparent')} + } +`; + const galleryBodyImageStyles = css` display: inline; position: relative; @@ -87,7 +93,13 @@ export const GalleryImage = ({ } return ( -
+
{ return ( - <> +

More from @@ -69,15 +97,25 @@ export const HostedContentOnwards = ({

-
    +
      {trails.map((trail) => { return ( -
    • - +
    • +
    • ); })}
    - +
); }; diff --git a/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx b/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx index 4aac5b9d864..601e7fe5866 100644 --- a/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx +++ b/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx @@ -6,17 +6,9 @@ import type { TrailType } from '../types/trails'; type Props = { trail: TrailType; + isGalleryPage?: boolean; }; -type CardPictureProps = { - image: string; - alt: string; -}; - -const imageStyles = css` - width: 120px; -`; - const mediaOverlayContainerStyles = css` position: absolute; top: 0; @@ -61,28 +53,26 @@ const headingStyles = css` color: ${palette('--card-headline')}; `; -const CardPicture = ({ image, alt }: CardPictureProps) => { - return ( - <> - - {alt} - -
-
-
- - ); -}; - -export const HostedContentOnwardsCard = ({ trail }: Props) => { +export const HostedContentOnwardsCard = ({ + trail, + isGalleryPage = false, +}: Props) => { return (

{trail.headline}

{!!trail.image && ( - + <> + + {trail.image.altText} + +
+
+
+ )}
); diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx index 09ae8e498d3..6bbaf59b44f 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx @@ -2,14 +2,25 @@ import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorat import { allModes } from '../../.storybook/modes'; import preview from '../../.storybook/preview'; import { hostedGallery } from '../../fixtures/manual/hostedGallery'; +import { hostedOnwardsTrails } from '../../fixtures/manual/onwardsTrails'; import { ArticleDesign, ArticleDisplay, ArticleSpecial, } from '../lib/articleFormat'; +import { customMockFetch } from '../lib/mockRESTCalls'; import { enhanceArticleType } from '../types/article'; import { HostedGalleryLayout } from './HostedGalleryLayout'; +const mockOnwardsContentFetch = customMockFetch([ + { + mockedMethod: 'GET', + mockedUrl: `${hostedGallery.config.ajaxUrl}/${hostedGallery.config.pageId}/onward.json`, + mockedStatus: 200, + mockedBody: { trails: hostedOnwardsTrails }, + }, +]); + const meta = preview.meta({ title: 'Layouts/HostedGallery', component: HostedGalleryLayout, @@ -21,6 +32,10 @@ const meta = preview.meta({ }, }, }, + render: (args) => { + global.fetch = mockOnwardsContentFetch; + return ; + }, }); const { hostedCampaignColour = '' } = diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx index 1dc8bde41b3..6d8fd768ac5 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx @@ -7,11 +7,11 @@ import { import { ArticleHeadline } from '../components/ArticleHeadline'; import { BackToTop } from '../components/BackToTop'; import { CallToActionButton } from '../components/CallToActionAtom'; +import { FetchHostedOnwards } from '../components/FetchHostedOnwards.island'; import { GalleryImage } from '../components/GalleryImage'; import { HostedContentHeader } from '../components/HostedContentHeader.island'; import { Island } from '../components/Island'; import { MainMediaGallery } from '../components/MainMediaGallery'; -import { OnwardsUpper } from '../components/OnwardsUpper.island'; import { Section } from '../components/Section'; import { ShareButton } from '../components/ShareButton.island'; import { Standfirst } from '../components/Standfirst'; @@ -78,7 +78,7 @@ const metaStyles = css` } `; -const bttStyles = css` +const paddedContainer = css` ${grid.paddedContainer} ${grid.outerRules()} background-color: ${palette('--article-inner-background')}; @@ -105,8 +105,17 @@ const ctaButtonStyles = css` margin-right: ${space[3]}px; `; +const onwardContentStyles = css` + ${grid.column.centre} + ${from.desktop} { + ${grid.between('centre-column-start', 'right-column-end')} + } + + padding-bottom: ${space[5]}px; +`; + export const HostedGalleryLayout = (props: WebProps | AppProps) => { - const { gallery, renderingTarget, format, serverTime } = props; + const { gallery, renderingTarget, format } = props; const { frontendData } = gallery; const { commercialProperties, editionId } = frontendData; @@ -149,6 +158,7 @@ export const HostedGalleryLayout = (props: WebProps | AppProps) => { format={format} renderingTarget={props.renderingTarget} /> + { frontendData.webPublicationDateDeprecated } /> + {
)} + { pageId={frontendData.pageId} webTitle={frontendData.webTitle} /> -
+ +
+ +
+ + + +
- - - ); }; diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 22aee146b06..39528551531 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -2571,7 +2571,12 @@ const cardMediaBackgroundDark: PaletteFunction = () => const cardMediaWaveformLight: PaletteFunction = () => sourcePalette.neutral[86]; const cardMediaWaveformDark: PaletteFunction = () => sourcePalette.neutral[38]; -const cardHeadlineTextLight: PaletteFunction = () => sourcePalette.neutral[7]; +const cardHeadlineTextLight: PaletteFunction = (format) => { + if (format.design === ArticleDesign.HostedGallery) { + return sourcePalette.neutral[97]; + } + return sourcePalette.neutral[7]; +}; const cardTextDark: PaletteFunction = () => sourcePalette.neutral[86]; @@ -5795,6 +5800,15 @@ const discussionLoadingShimmerLight: PaletteFunction = () => const discussionLoadingShimmerDark: PaletteFunction = () => sourcePalette.neutral[46]; +const onwardTextLight: PaletteFunction = (format) => { + if (format.design === ArticleDesign.HostedGallery) { + return sourcePalette.neutral[86]; + } + return sourcePalette.neutral[7]; +}; + +const onwardTextDark: PaletteFunction = () => sourcePalette.neutral[86]; + const paginationTextLight: PaletteFunction = ({ theme }) => { switch (theme) { case Pillar.News: @@ -7488,10 +7502,6 @@ const paletteColours = { light: highlightContainerStartLight, dark: highlightContainerStartDark, }, - '--hosted-content-onwards-heading': { - light: () => sourcePalette.neutral[7], - dark: () => sourcePalette.neutral[86], - }, '--image-title-background': { light: imageTitleBackground, dark: imageTitleBackground, @@ -7841,8 +7851,8 @@ const paletteColours = { dark: () => sourcePalette.neutral[20], }, '--onward-text': { - light: () => sourcePalette.neutral[7], - dark: () => sourcePalette.neutral[86], + light: onwardTextLight, + dark: onwardTextDark, }, '--pagination-text': { light: paginationTextLight, From fdf4b67d07e57cb1d57795846171f8348cdbdd43 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Fri, 3 Jul 2026 16:11:27 +0100 Subject: [PATCH 2/2] use fastly image resizer for images in hosted onward cards, add Gallery variation for onward story --- .../src/components/HostedContentOnwards.stories.tsx | 8 ++++++++ dotcom-rendering/src/components/HostedContentOnwards.tsx | 1 + .../src/components/HostedContentOnwardsCard.tsx | 9 +++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx b/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx index ab8c4612f45..838c8c8cfec 100644 --- a/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx +++ b/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx @@ -9,6 +9,7 @@ const meta = preview.meta({ args: { trails: hostedOnwardsTrails, brandName: 'TrendAI', + isGalleryPage: false, }, render: (args) => , }); @@ -18,3 +19,10 @@ export const Default = meta.story({}); export const WithAccentColour = meta.story({ decorators: hostedPaletteDecorator('#d90c1f'), }); + +export const HostedGallery = meta.story({ + args: { + isGalleryPage: true, + }, + decorators: hostedPaletteDecorator('#d90c1f'), +}); diff --git a/dotcom-rendering/src/components/HostedContentOnwards.tsx b/dotcom-rendering/src/components/HostedContentOnwards.tsx index ee0818fb547..51c554cc821 100644 --- a/dotcom-rendering/src/components/HostedContentOnwards.tsx +++ b/dotcom-rendering/src/components/HostedContentOnwards.tsx @@ -31,6 +31,7 @@ const headerStyles = css` const headingStyles = css` ${textSans17} padding-top: ${space[2]}px; + padding-bottom: ${space[2]}px; color: ${palette('--onward-text')}; @media (prefers-color-scheme: dark) { diff --git a/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx b/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx index 601e7fe5866..7902fd66164 100644 --- a/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx +++ b/dotcom-rendering/src/components/HostedContentOnwardsCard.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/react'; import { space, textSansBold15 } from '@guardian/source/foundations'; import { getZIndex } from '../lib/getZIndex'; +import { generateImageURL } from '../lib/image'; import { palette } from '../palette'; import type { TrailType } from '../types/trails'; @@ -65,8 +66,12 @@ export const HostedContentOnwardsCard = ({ {trail.image.altText}