diff --git a/dotcom-rendering/src/components/Masthead/Masthead.tsx b/dotcom-rendering/src/components/Masthead/Masthead.tsx index 922d38d46bd..71ab5ed1e1f 100644 --- a/dotcom-rendering/src/components/Masthead/Masthead.tsx +++ b/dotcom-rendering/src/components/Masthead/Masthead.tsx @@ -2,7 +2,7 @@ import type { ReactElement } from 'react'; import type { EditionId } from '../../lib/edition'; import type { NavType } from '../../model/extract-nav'; import { palette as themePalette } from '../../palette'; -import type { CustomSubnav } from '../../types/customSubnav'; +import type { CustomSubnavForPage } from '../../types/customSubnav'; import { Island } from '../Island'; import { Section } from '../Section'; import { Titlepiece } from '../Titlepiece.island'; @@ -18,7 +18,7 @@ type Props = { discussionApiUrl: string; idApiUrl: string; showSubNav?: boolean; - customSubnav?: CustomSubnav; + customSubnav?: CustomSubnavForPage; /** The slim nav is a slimmed down version of the Titlepiece usually used for Immersive articles */ showSlimNav?: boolean; hasPageSkin?: boolean; diff --git a/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.stories.tsx b/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.stories.tsx new file mode 100644 index 00000000000..8e8800f4f12 --- /dev/null +++ b/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.stories.tsx @@ -0,0 +1,78 @@ +import { css } from '@emotion/react'; +import { palette as sourcePalette, space } from '@guardian/source/foundations'; +import type { Meta, StoryObj } from '@storybook/react-webpack5'; +import { + ArticleDesign, + ArticleDisplay, + Pillar, +} from '../../../lib/articleFormat'; +import type { CustomSubnav } from '../../../types/customSubnav'; +import { CustomSubNav } from './CustomSubNav'; + +const customSubNav: CustomSubnav = { + id: 'us-politics', + header: { + headerText: 'US politics', + copy: '', + dotcomPath: '/us-news/us-politics', + }, + format: 'large', + links: [ + { + linkText: 'Trump administration', + dotcomPath: '/us-news/trump-administration', + }, + { linkText: 'US Congress', dotcomPath: '/us-news/us-congress' }, + { linkText: 'US supreme court', dotcomPath: '/law/us-supreme-court' }, + { + linkText: 'US elections 2024', + dotcomPath: '/us-news/us-elections-2024', + }, + { linkText: 'US immigration', dotcomPath: '/us-news/usimmigration' }, + ], + pages: [], +}; + +const meta = { + component: CustomSubNav, + title: 'Components/Masthead/Titlepiece/CustomSubNav', + decorators: [ + (Story) => ( +
+ +
+ ), + ], + render: (args) => , + args: { + customSubNav, + currentNavLink: 'US Congress', + hasPageSkin: false, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Front = { + args: { renderingPage: 'front' }, +} satisfies Story; + +export const Article = { + args: { renderingPage: 'article' }, + parameters: { + formats: [ + { + design: ArticleDesign.Standard, + display: ArticleDisplay.Standard, + theme: Pillar.News, + }, + ], + }, +} satisfies Story; diff --git a/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.tsx b/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.tsx index 6c1432275e7..06a7001b65b 100644 --- a/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.tsx +++ b/dotcom-rendering/src/components/Masthead/Titlepiece/CustomSubNav.tsx @@ -4,19 +4,22 @@ */ import { css } from '@emotion/react'; import { + breakpoints, from, headlineBold28, space, textSans14, textSansBold14, } from '@guardian/source/foundations'; +import { grid } from '../../../grid'; import { nestedOphanComponents } from '../../../lib/ophan-helpers'; import { palette as themePalette } from '../../../palette'; -import type { CustomSubnav } from '../../../types/customSubnav'; +import type { CustomSubnav, RenderingPage } from '../../../types/customSubnav'; type Props = { customSubNav: CustomSubnav; currentNavLink: string; + renderingPage: RenderingPage; hasPageSkin?: boolean; }; @@ -43,6 +46,132 @@ const subNavStylesFromLeftCol = css` } `; +/** On articles the header and links sit on a single aligned row. */ +const articleContainerStyles = css` + display: flex; + align-items: stretch; + min-height: 36px; + + ${from.mobileMedium} { + min-height: 40px; + } + ${from.tablet} { + min-height: 42px; + } + ${from.leftCol} { + min-height: 44px; + } +`; + +/** + * On articles the links row sits inline next to the header. The stacked top margins are + * removed so the header divider spans the full component height set by the container. + */ +const articleSubNavStyles = css` + margin-top: 0; + align-items: center; + + ${from.mobileMedium} { + margin-top: 0; + } + ${from.leftCol} { + margin-top: 0; + } +`; + +const articleHeaderStyles = css` + ${textSansBold14} + color: ${themePalette('--custom-subnav-header-text')}; + display: flex; + align-items: center; + white-space: nowrap; + padding-right: ${space[2]}px; + margin-right: ${space[2]}px; + border-right: 1px solid ${themePalette('--masthead-nav-lines')}; +`; + +/** Served from `src/static`; temporary test assets until real images arrive in the payload. */ +const headerImageBasePath = '/static/frontend/customsubnav'; + +/** + * On fronts, when an image is present the subnav mirrors DirectoryPageNav: a + * fixed-width, centred grid (blue) with the image spanning the full width on the + * top row and the links beneath it. The surrounding row is white (set by the + * Titlepiece wrapper), so only this padded container shows blue. + */ +const imageNavStyles = css` + ${grid.paddedContainer} + position: relative; + background-color: ${themePalette('--masthead-nav-background')}; +`; + +const imageWrapperStyles = css` + ${grid.column.all} + grid-row: 1; + position: relative; + display: block; + border-bottom: 1px solid ${themePalette('--masthead-nav-lines')}; +`; + +const headerImageStyles = css` + display: block; + width: 100%; + height: auto; +`; + +const imageHeaderTextStyles = css` + ${headlineBold28} + position: absolute; + left: ${space[3]}px; + bottom: ${space[1]}px; + color: ${themePalette('--masthead-nav-link-text')}; + + ${from.mobileLandscape} { + left: ${space[5]}px; + } +`; + +const imageListStyles = css` + ${grid.column.all} + grid-row: 2; + ${textSans14} + display: flex; + align-items: center; + column-gap: ${space[2]}px; + min-height: 28px; + padding: 0 ${space[3]}px; + + ${from.mobileLandscape} { + padding: 0 ${space[5]}px; + } + ${from.tablet} { + min-height: 30px; + } +`; + +/** Temporary breakpoint-specific header images; swap for payload images later. */ +const HeaderImage = () => ( + + + + + + +); + /** Sets horizontal scrolling behaviour and removes the scrollbar */ const scrollableSubNavStyles = css` overflow-x: scroll; @@ -75,56 +204,104 @@ const selectedLink = css` `; /** - * Renders a custom subnav (header + links, images and further data to follow) targeted at a front. + * Renders a custom subnav (header + links, images and further data to follow) assigned to fronts or articles. */ export const CustomSubNav = ({ customSubNav, currentNavLink, + renderingPage, hasPageSkin, }: Props) => { + const isArticle = renderingPage === 'article'; + const hasHeaderImage = !isArticle && (customSubNav.images?.length ?? 0) > 0; + + const linkItems = customSubNav.links.map(({ linkText, dotcomPath }) => ( +
  • + + {linkText === currentNavLink ? ( + {linkText} + ) : ( + linkText + )} + +
  • + )); + + if (hasHeaderImage) { + return ( +
    +
    + + + {customSubNav.header.headerText} + +
    +
      + {linkItems} +
    +
    + ); + } + return (
    - {customSubNav.header.headerText} + {isArticle ? ( + + {customSubNav.header.headerText} + + ) : ( + customSubNav.header.headerText + )}
    ); diff --git a/dotcom-rendering/src/components/Titlepiece.island.tsx b/dotcom-rendering/src/components/Titlepiece.island.tsx index ed6164cba2c..529ebf6bc21 100644 --- a/dotcom-rendering/src/components/Titlepiece.island.tsx +++ b/dotcom-rendering/src/components/Titlepiece.island.tsx @@ -2,6 +2,7 @@ import { css, Global } from '@emotion/react'; import { from, headlineBold14, + palette as sourcePalette, space, until, visuallyHidden, @@ -11,7 +12,7 @@ import { nestedOphanComponents } from '../lib/ophan-helpers'; import { useEditionSwitcherBanner } from '../lib/useEditionSwitcherBanner'; import type { NavType } from '../model/extract-nav'; import { palette as themePalette } from '../palette'; -import type { CustomSubnav } from '../types/customSubnav'; +import type { CustomSubnavForPage } from '../types/customSubnav'; import { expandedMenuRootId, navInputCheckboxId, @@ -32,7 +33,7 @@ interface Props { nav: NavType; editionId: EditionId; showSubNav?: boolean; - customSubnav?: CustomSubnav; + customSubnav?: CustomSubnavForPage; showSlimNav?: boolean; hasPageSkin?: boolean; pageId?: string; @@ -316,6 +317,14 @@ const fadeStyles = css` ${themePalette('--masthead-nav-background')} 100% ); `; + +/** For the image custom subnav the row is white and full-bleed; CustomSubNav + * centres its own blue padded container on top (mirrors DirectoryPageNav). */ +const customSubnavImageWrapper = css` + grid-column: viewport-start / viewport-end; + grid-row: 3; + background-color: ${sourcePalette.neutral[100]}; +`; export const Titlepiece = ({ nav, editionId, @@ -327,6 +336,10 @@ export const Titlepiece = ({ }: Props) => { const { showBanner } = useEditionSwitcherBanner(pageId, editionId); + const hasCustomSubnavImage = + customSubnav?.renderingPage === 'front' && + (customSubnav.data.images?.length ?? 0) > 0; + return ( diff --git a/dotcom-rendering/src/frontend/schemas/feArticle.json b/dotcom-rendering/src/frontend/schemas/feArticle.json index c4acc49dbf3..0fe237e54b9 100644 --- a/dotcom-rendering/src/frontend/schemas/feArticle.json +++ b/dotcom-rendering/src/frontend/schemas/feArticle.json @@ -6625,7 +6625,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json b/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json index 6e722c8c2de..c858195d643 100644 --- a/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json +++ b/dotcom-rendering/src/frontend/schemas/feFootballMatchInfoPage.json @@ -437,7 +437,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json b/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json index 467b5945d6f..fefacafb810 100644 --- a/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json +++ b/dotcom-rendering/src/frontend/schemas/feFootballMatchListPage.json @@ -319,7 +319,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/frontend/schemas/feFootballTablesPage.json b/dotcom-rendering/src/frontend/schemas/feFootballTablesPage.json index b67921976dc..f5e90c8a41f 100644 --- a/dotcom-rendering/src/frontend/schemas/feFootballTablesPage.json +++ b/dotcom-rendering/src/frontend/schemas/feFootballTablesPage.json @@ -312,7 +312,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/frontend/schemas/feFront.json b/dotcom-rendering/src/frontend/schemas/feFront.json index b850763db67..f73e02f5604 100644 --- a/dotcom-rendering/src/frontend/schemas/feFront.json +++ b/dotcom-rendering/src/frontend/schemas/feFront.json @@ -3645,7 +3645,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/frontend/schemas/feTagPage.json b/dotcom-rendering/src/frontend/schemas/feTagPage.json index 192ac9e2a8b..191d0b5be5a 100644 --- a/dotcom-rendering/src/frontend/schemas/feTagPage.json +++ b/dotcom-rendering/src/frontend/schemas/feTagPage.json @@ -1703,7 +1703,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/layouts/CommentLayout.tsx b/dotcom-rendering/src/layouts/CommentLayout.tsx index 446ceb3daa3..6a6b6ca3a33 100644 --- a/dotcom-rendering/src/layouts/CommentLayout.tsx +++ b/dotcom-rendering/src/layouts/CommentLayout.tsx @@ -333,7 +333,13 @@ export const CommentLayout = (props: WebProps | AppsProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={!isWorldCup2026} + showSubNav={!isWorldCup2026 && !props.NAV.customSubNav} + customSubnav={ + props.NAV.customSubNav && { + data: props.NAV.customSubNav, + renderingPage: 'article', + } + } showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index e223724074c..78a5ee7d428 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -240,7 +240,12 @@ export const FrontLayout = ({ front, NAV }: Props) => { contributionsServiceUrl={contributionsServiceUrl} idApiUrl={front.config.idApiUrl} showSubNav={showStandardSubNav} - customSubnav={NAV.customSubNav} + customSubnav={ + NAV.customSubNav && { + data: NAV.customSubNav, + renderingPage: 'front', + } + } showSlimNav={false} hasPageSkin={hasPageSkin} hasPageSkinContentSelfConstrain={true} diff --git a/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx b/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx index 0bb9c91a48c..6506e0d55e7 100644 --- a/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx +++ b/dotcom-rendering/src/layouts/FullPageInteractiveLayout.tsx @@ -175,6 +175,12 @@ const NavHeader = ({ article, NAV, renderAds }: HeaderProps) => { idApiUrl={article.config.idApiUrl} contributionsServiceUrl={article.contributionsServiceUrl} showSubNav={false} + customSubnav={ + NAV.customSubNav && { + data: NAV.customSubNav, + renderingPage: 'article', + } + } showSlimNav={true} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/GalleryLayout.tsx b/dotcom-rendering/src/layouts/GalleryLayout.tsx index c550dd30133..b49cfec13bb 100644 --- a/dotcom-rendering/src/layouts/GalleryLayout.tsx +++ b/dotcom-rendering/src/layouts/GalleryLayout.tsx @@ -416,6 +416,12 @@ const BannerAndMasthead = (props: { idApiUrl={props.config.idApiUrl} contributionsServiceUrl={props.contributionsServiceUrl} showSubNav={false} + customSubnav={ + props.nav.customSubNav && { + data: props.nav.customSubNav, + renderingPage: 'article', + } + } showSlimNav={props.showSlimNav ?? true} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/InteractiveLayout.tsx b/dotcom-rendering/src/layouts/InteractiveLayout.tsx index 80e2f30611e..07df0b43576 100644 --- a/dotcom-rendering/src/layouts/InteractiveLayout.tsx +++ b/dotcom-rendering/src/layouts/InteractiveLayout.tsx @@ -284,6 +284,12 @@ export const InteractiveLayout = (props: WebProps | AppsProps) => { contributionsServiceUrl={contributionsServiceUrl} showSlimNav={true} showSubNav={false} + customSubnav={ + props.NAV.customSubNav && { + data: props.NAV.customSubNav, + renderingPage: 'article', + } + } hasPageSkin={false} hasPageSkinContentSelfConstrain={false} pageId={article.pageId} diff --git a/dotcom-rendering/src/layouts/LiveLayout.tsx b/dotcom-rendering/src/layouts/LiveLayout.tsx index 5c9d2dcd57b..c768bec5af3 100644 --- a/dotcom-rendering/src/layouts/LiveLayout.tsx +++ b/dotcom-rendering/src/layouts/LiveLayout.tsx @@ -338,7 +338,13 @@ export const LiveLayout = (props: WebProps | AppsProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={!isWorldCup2026} + showSubNav={!isWorldCup2026 && !props.NAV.customSubNav} + customSubnav={ + props.NAV.customSubNav && { + data: props.NAV.customSubNav, + renderingPage: 'article', + } + } showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/PictureLayout.tsx b/dotcom-rendering/src/layouts/PictureLayout.tsx index f4e3f55728c..99657235c63 100644 --- a/dotcom-rendering/src/layouts/PictureLayout.tsx +++ b/dotcom-rendering/src/layouts/PictureLayout.tsx @@ -309,7 +309,13 @@ export const PictureLayout = (props: WebProps | AppsProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={!isWorldCup2026} + showSubNav={!isWorldCup2026 && !props.NAV.customSubNav} + customSubnav={ + props.NAV.customSubNav && { + data: props.NAV.customSubNav, + renderingPage: 'article', + } + } showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/StandardLayout.tsx b/dotcom-rendering/src/layouts/StandardLayout.tsx index cd5abdf5f3d..8be67fb950d 100644 --- a/dotcom-rendering/src/layouts/StandardLayout.tsx +++ b/dotcom-rendering/src/layouts/StandardLayout.tsx @@ -119,7 +119,17 @@ export const StandardLayout = (props: WebProps | AppProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={!isLabs && !isWorldCup2026} + showSubNav={ + !isLabs && + !isWorldCup2026 && + !props.NAV.customSubNav + } + customSubnav={ + props.NAV.customSubNav && { + data: props.NAV.customSubNav, + renderingPage: 'article', + } + } showSlimNav={ format.display === ArticleDisplay.Immersive } diff --git a/dotcom-rendering/src/model/newsletter-page-schema.json b/dotcom-rendering/src/model/newsletter-page-schema.json index 2963f083b0e..a01a2a4d791 100644 --- a/dotcom-rendering/src/model/newsletter-page-schema.json +++ b/dotcom-rendering/src/model/newsletter-page-schema.json @@ -456,7 +456,6 @@ ] }, "CustomSubnavLink": { - "description": "A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload.", "type": "object", "properties": { "linkText": { diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index dd7d58bceee..3d66a6c4cd3 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -6572,6 +6572,39 @@ const crosswordCluesHeaderBorderBottom: PaletteFunction = () => const crosswordTextLight: PaletteFunction = () => sourcePalette.neutral[7]; const crosswordTextDark: PaletteFunction = () => sourcePalette.neutral[86]; +const customSubnavHeaderTextLight: PaletteFunction = ({ theme }) => { + switch (theme) { + case Pillar.News: + case Pillar.Opinion: + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: + return pillarPalette(theme, 500); + case ArticleSpecial.Labs: + return sourcePalette.labs[500]; + case ArticleSpecial.SpecialReport: + return sourcePalette.specialReport[500]; + case ArticleSpecial.SpecialReportAlt: + return sourcePalette.specialReportAlt[300]; + } +}; +const customSubnavHeaderTextDark: PaletteFunction = ({ theme }) => { + switch (theme) { + case Pillar.News: + case Pillar.Opinion: + case Pillar.Sport: + case Pillar.Culture: + case Pillar.Lifestyle: + return pillarPalette(theme, 500); + case ArticleSpecial.Labs: + return sourcePalette.labs[400]; + case ArticleSpecial.SpecialReport: + return sourcePalette.specialReport[500]; + case ArticleSpecial.SpecialReportAlt: + return sourcePalette.specialReportAlt[200]; + } +}; + // ----- Palette ----- // /** @@ -7129,6 +7162,10 @@ const paletteColours = { light: crosswordTextLight, dark: crosswordTextDark, }, + '--custom-subnav-header-text': { + light: customSubnavHeaderTextLight, + dark: customSubnavHeaderTextDark, + }, '--dateline': { light: datelineLight, dark: datelineDark, diff --git a/dotcom-rendering/src/static/customsubnav/wc-desktop.png b/dotcom-rendering/src/static/customsubnav/wc-desktop.png new file mode 100644 index 00000000000..d6be7e250aa Binary files /dev/null and b/dotcom-rendering/src/static/customsubnav/wc-desktop.png differ diff --git a/dotcom-rendering/src/static/customsubnav/wc-mobile.png b/dotcom-rendering/src/static/customsubnav/wc-mobile.png new file mode 100644 index 00000000000..6742c2d0654 Binary files /dev/null and b/dotcom-rendering/src/static/customsubnav/wc-mobile.png differ diff --git a/dotcom-rendering/src/static/customsubnav/wc-tablet.png b/dotcom-rendering/src/static/customsubnav/wc-tablet.png new file mode 100644 index 00000000000..017e643d363 Binary files /dev/null and b/dotcom-rendering/src/static/customsubnav/wc-tablet.png differ diff --git a/dotcom-rendering/src/static/customsubnav/wc-wide.png b/dotcom-rendering/src/static/customsubnav/wc-wide.png new file mode 100644 index 00000000000..3eb86e41aa3 Binary files /dev/null and b/dotcom-rendering/src/static/customsubnav/wc-wide.png differ diff --git a/dotcom-rendering/src/types/customSubnav.ts b/dotcom-rendering/src/types/customSubnav.ts index 7bac6e7d85e..d811bce2233 100644 --- a/dotcom-rendering/src/types/customSubnav.ts +++ b/dotcom-rendering/src/types/customSubnav.ts @@ -2,6 +2,9 @@ * A custom subnav that can appear on assigned pages (e.g. fronts, articles). It arrives inside the `nav` object of the request payload. */ +/** The kind of page a custom subnav is rendered on, used to vary its styling. */ +export type RenderingPage = 'front' | 'article'; + export interface CustomSubnavLink { linkText: string; dotcomPath: string; @@ -30,3 +33,9 @@ export interface CustomSubnav { pages: CustomSubnavTargetedPage[]; images?: CustomSubnavImage[]; } + +/** A custom subnav bundled with the type of page it's rendered on */ +export interface CustomSubnavForPage { + data: CustomSubnav; + renderingPage: RenderingPage; +}