Skip to content

Large diffs are not rendered by default.

73 changes: 64 additions & 9 deletions dotcom-rendering/src/components/DirectoryPageNav.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { allModes } from '../../.storybook/modes';
import preview from '../../.storybook/preview';
import { BetaABTests } from '../experiments/lib/beta-ab-tests';
import { setBetaABTests } from '../lib/useAB';
import { DirectoryPageNav } from './DirectoryPageNav';
import { ConfigProvider } from './ConfigContext';
import { DirectoryPageNav } from './DirectoryPageNav.island';

const mockAB = new BetaABTests({
isServer: true,
Expand All @@ -15,43 +16,97 @@ setBetaABTests(mockAB);
const meta = preview.meta({
component: DirectoryPageNav,
title: 'Components/Directory Page Nav',
parameters: {
chromatic: {
modes: {
'light mobileMedium': allModes['light mobileMedium'],
'light desktop': allModes['light desktop'],
'light leftCol': allModes['light leftCol'],
},
});

// So that these aren't applied to the apps stories
const webChromaticParams = {
chromatic: {
modes: {
'light mobileMedium': allModes['light mobileMedium'],
'light desktop': allModes['light desktop'],
'light leftCol': allModes['light leftCol'],
},
},
});
};

export const WomensEuro2025 = meta.story({
args: {
pageId: 'football/women-s-euro-2025/table',
},
parameters: webChromaticParams,
});

export const WorldCup2026 = meta.story({
args: {
pageId: 'football/world-cup-2026',
},
parameters: webChromaticParams,
});

export const WorldCup2026MatchCenter = meta.story({
args: {
pageId: 'football/world-cup-2026/overview',
},
parameters: webChromaticParams,
});

export const WorldCup2026ArticleWeb = meta.story({
args: {
pageId: 'football/2026/may/19/brazils-world-cup-squad-offers-a-hint-of-the-magical-pragmatism-of-1994',
pageTags: [
{
id: 'football/world-cup-2026',
type: 'Topic',
title: 'World Cup 2026 Fronts',
},
],
},
parameters: webChromaticParams,
});

export const WorldCup2026ArticleApp = meta.story({
render: (args) => (
<ConfigProvider
value={{
renderingTarget: 'Apps',
darkModeAvailable: true,
assetOrigin: '/',
editionId: 'UK',
}}
>
<DirectoryPageNav {...args} />
</ConfigProvider>
),
args: {
pageId: 'football/2026/may/19/brazils-world-cup-squad-offers-a-hint-of-the-magical-pragmatism-of-1994',
pageTags: [
{
id: 'football/world-cup-2026',
type: 'Topic',
title: 'World Cup 2026',
},
],
},
parameters: {
chromatic: {
modes: {
'apps light': allModes['light'],
'apps dark': allModes['dark'],
},
},
},
});

export const OtherCompetition = meta.story({
args: {
pageId: 'football/premierleague/table',
},
parameters: webChromaticParams,
});

export const WinterOlympics = meta.story({
args: {
pageId: 'sport/winter-olympics-2026',
},
parameters: webChromaticParams,
});
14 changes: 14 additions & 0 deletions dotcom-rendering/src/components/DirectoryPageNavIsland.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { TagType } from '../types/tag';
import { DirectoryPageNav } from './DirectoryPageNav.island';
import { Island } from './Island';

type Props = {
pageId: string;
pageTags?: TagType[];
};

export const DirectoryPageNavIsland = (args: Props) => (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you created this component to avoid repeating to wrap the component by Island in all the places it's used?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You caught me 🤠

<Island priority="feature" defer={{ until: 'visible' }}>
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now needs to be an island so it can disable the native article "swipe to next article" when scrolling the nav via bridget.

<DirectoryPageNav {...args} />
</Island>
);
5 changes: 3 additions & 2 deletions dotcom-rendering/src/components/FootballMatchesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { Result } from '../lib/result';
import { palette } from '../palette';
import type { FootballMatchListPageKind, Region } from '../sportDataPage';
import { AdSlot } from './AdSlot.web';
import { DirectoryPageNav } from './DirectoryPageNav';
import { DirectoryPageNavIsland } from './DirectoryPageNavIsland';
import { FootballCompetitionSelect } from './FootballCompetitionSelect';
import { FootballMatchList } from './FootballMatchList';

Expand Down Expand Up @@ -59,7 +59,8 @@ export const FootballMatchesPage = ({
pageId,
}: Props) => (
<>
<DirectoryPageNav pageId={pageId} />
<DirectoryPageNavIsland pageId={pageId} />

<main
id="maincontent"
data-layout="FootballDataPageLayout"
Expand Down
5 changes: 3 additions & 2 deletions dotcom-rendering/src/components/FootballTablesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { grid } from '../grid';
import { palette } from '../palette';
import type { Region } from '../sportDataPage';
import { AdSlot } from './AdSlot.web';
import { DirectoryPageNav } from './DirectoryPageNav';
import { DirectoryPageNavIsland } from './DirectoryPageNavIsland';
import { FootballTableList } from './FootballTableList';
import { FootballTablesCompetitionSelect } from './FootballTablesCompetitionSelect.island';
import { Island } from './Island';
Expand All @@ -31,7 +31,8 @@ export const FootballTablesPage = ({
guardianBaseUrl,
}: Props) => (
<>
<DirectoryPageNav pageId={pageId} />
<DirectoryPageNavIsland pageId={pageId} />

<main
id="maincontent"
data-layout="FootballDataPageLayout"
Expand Down
3 changes: 2 additions & 1 deletion dotcom-rendering/src/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const paddedContainer = `

type VerticalRuleOptions = {
centre?: boolean;
color?: string;
};

/**
Expand Down Expand Up @@ -121,7 +122,7 @@ const verticalRules = (options: VerticalRuleOptions = {}): string => `
top: 0;
bottom: 0;
width: 1px;
background-color: ${palette('--article-border')};
background-color: ${options.color ?? palette('--article-border')};
content: '';
}

Expand Down
16 changes: 15 additions & 1 deletion dotcom-rendering/src/layouts/AudioLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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';
Expand All @@ -45,6 +46,8 @@ import { canRenderAds } from '../lib/canRenderAds';
import { getContributionsServiceUrl } from '../lib/contributions';
import { decideStoryPackageTrails } from '../lib/decideTrail';
import { parse } from '../lib/slot-machine-flags';
import { useBetaAB } from '../lib/useAB';
import { worldCupTagId } from '../lib/worldCup2026';
import type { NavType } from '../model/extract-nav';
import { palette as themePalette } from '../palette';
import type { ArticleDeprecated } from '../types/article';
Expand Down Expand Up @@ -164,6 +167,12 @@ export const AudioLayout = (props: WebProps | AppProps) => {

const isLabs = format.theme === ArticleSpecial.Labs;

const ab = useBetaAB();

const isWorldCup2026 =
article.tags.some((tag) => tag.id === worldCupTagId) &&
ab?.isUserInTest('webx-world-cup-2026-subnav');

const renderAds = canRenderAds(article);

return (
Expand Down Expand Up @@ -191,7 +200,7 @@ export const AudioLayout = (props: WebProps | AppProps) => {
discussionApiUrl={article.config.discussionApiUrl}
idApiUrl={article.config.idApiUrl}
contributionsServiceUrl={contributionsServiceUrl}
showSubNav={!isLabs}
showSubNav={!isLabs && !isWorldCup2026}
showSlimNav={false}
hasPageSkinContentSelfConstrain={true}
pageId={article.pageId}
Expand Down Expand Up @@ -227,6 +236,11 @@ export const AudioLayout = (props: WebProps | AppProps) => {
<AdPortals />
</Island>
)}
<DirectoryPageNavIsland
pageTags={article.tags}
pageId={article.pageId}
/>

<Section
fullWidth={true}
showTopBorder={false}
Expand Down
15 changes: 14 additions & 1 deletion dotcom-rendering/src/layouts/CommentLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ArticleTitle } from '../components/ArticleTitle';
import { Border } from '../components/Border';
import { Carousel } from '../components/Carousel.island';
import { ContributorAvatar } from '../components/ContributorAvatar';
import { DirectoryPageNavIsland } from '../components/DirectoryPageNavIsland';
import { DiscussionLayout } from '../components/DiscussionLayout';
import { Footer } from '../components/Footer';
import { GridItem } from '../components/GridItem';
Expand All @@ -43,6 +44,8 @@ import { canRenderAds } from '../lib/canRenderAds';
import { getContributionsServiceUrl } from '../lib/contributions';
import { decideStoryPackageTrails } from '../lib/decideTrail';
import { parse } from '../lib/slot-machine-flags';
import { useBetaAB } from '../lib/useAB';
import { worldCupTagId } from '../lib/worldCup2026';
import type { NavType } from '../model/extract-nav';
import { palette as themePalette } from '../palette';
import type { ArticleDeprecated } from '../types/article';
Expand Down Expand Up @@ -298,6 +301,12 @@ export const CommentLayout = (props: WebProps | AppsProps) => {

const contributionsServiceUrl = getContributionsServiceUrl(article);

const ab = useBetaAB();

const isWorldCup2026 =
article.tags.some((tag) => tag.id === worldCupTagId) &&
ab?.isUserInTest('webx-world-cup-2026-subnav');

const renderAds = canRenderAds(article);

return (
Expand Down Expand Up @@ -325,7 +334,7 @@ export const CommentLayout = (props: WebProps | AppsProps) => {
discussionApiUrl={article.config.discussionApiUrl}
idApiUrl={article.config.idApiUrl}
contributionsServiceUrl={contributionsServiceUrl}
showSubNav={true}
showSubNav={!isWorldCup2026}
showSlimNav={false}
hasPageSkin={false}
hasPageSkinContentSelfConstrain={false}
Expand All @@ -343,6 +352,10 @@ export const CommentLayout = (props: WebProps | AppsProps) => {
<AdPortals />
</Island>
)}
<DirectoryPageNavIsland
pageTags={article.tags}
pageId={article.pageId}
/>
<Section
fullWidth={true}
showTopBorder={false}
Expand Down
4 changes: 2 additions & 2 deletions dotcom-rendering/src/layouts/FrontLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Fragment } from 'react';
import { AdSlot } from '../components/AdSlot.web';
import { CPScottHeader } from '../components/CPScottHeader';
import { DecideContainer } from '../components/DecideContainer';
import { DirectoryPageNav } from '../components/DirectoryPageNav';
import { DirectoryPageNavIsland } from '../components/DirectoryPageNavIsland';
import { EditionSwitcherBanner } from '../components/EditionSwitcherBanner.island';
import { Footer } from '../components/Footer';
import { FrontMostViewed } from '../components/FrontMostViewed';
Expand Down Expand Up @@ -274,7 +274,7 @@ export const FrontLayout = ({ front, NAV }: Props) => {
/>
</Island>
)}
<DirectoryPageNav pageId={pageId} />
<DirectoryPageNavIsland pageId={pageId} />

{filteredCollections.map((collection, index) => {
// Backfills should be added to the end of any curated content
Expand Down
17 changes: 16 additions & 1 deletion dotcom-rendering/src/layouts/GalleryLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ArticleMetaApps } from '../components/ArticleMeta.apps';
import { ArticleMeta } from '../components/ArticleMeta.web';
import { ArticleTitle } from '../components/ArticleTitle';
import { Caption } from '../components/Caption';
import { DirectoryPageNavIsland } from '../components/DirectoryPageNavIsland';
import { DiscussionLayout } from '../components/DiscussionLayout';
import { FetchMoreGalleriesData } from '../components/FetchMoreGalleriesData.island';
import { Footer } from '../components/Footer';
Expand Down Expand Up @@ -45,6 +46,8 @@ import { canRenderAds } from '../lib/canRenderAds';
import { getContributionsServiceUrl } from '../lib/contributions';
import { decideMainMediaCaption } from '../lib/decide-caption';
import type { EditionId } from '../lib/edition';
import { useBetaAB } from '../lib/useAB';
import { worldCupTagId } from '../lib/worldCup2026';
import type { NavType } from '../model/extract-nav';
import { palette } from '../palette';
import type { ArticleDeprecated, Gallery } from '../types/article';
Expand Down Expand Up @@ -105,6 +108,12 @@ export const GalleryLayout = (props: WebProps | AppProps) => {

const isLabs = format.theme === ArticleSpecial.Labs;

const ab = useBetaAB();

const isWorldCup2026 =
frontendData.tags.some((tag) => tag.id === worldCupTagId) &&
ab?.isUserInTest('webx-world-cup-2026-subnav');

const renderAds = canRenderAds(frontendData);
const showMerchandisingHigh = isWeb && renderAds && !isLabs;

Expand All @@ -124,6 +133,7 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
contributionsServiceUrl={contributionsServiceUrl}
pageId={frontendData.pageId}
tagIds={frontendData.tags.map((tag) => tag.id)}
showSlimNav={!isWorldCup2026}
/>
) : null}
<GalleryLabsHeader
Expand All @@ -141,6 +151,10 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
<AdPortals />
</Island>
)}
<DirectoryPageNavIsland
pageTags={frontendData.tags}
pageId={frontendData.pageId}
/>
<header css={headerStyles}>
<MainMediaGallery
mainMedia={gallery.mainMedia}
Expand Down Expand Up @@ -380,6 +394,7 @@ const BannerAndMasthead = (props: {
contributionsServiceUrl: string;
pageId: string | undefined;
tagIds?: string[];
showSlimNav?: boolean;
}) => (
<div data-print-layout="hide" id="bannerandheader">
{props.renderAds ? (
Expand All @@ -404,7 +419,7 @@ const BannerAndMasthead = (props: {
idApiUrl={props.config.idApiUrl}
contributionsServiceUrl={props.contributionsServiceUrl}
showSubNav={false}
showSlimNav={true}
showSlimNav={props.showSlimNav ?? true}
hasPageSkin={false}
hasPageSkinContentSelfConstrain={false}
pageId={props.pageId}
Expand Down
4 changes: 2 additions & 2 deletions dotcom-rendering/src/layouts/InteractiveLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { ArticleTitle } from '../components/ArticleTitle';
import { Border } from '../components/Border';
import { Carousel } from '../components/Carousel.island';
import { DecideLines } from '../components/DecideLines';
import { DirectoryPageNav } from '../components/DirectoryPageNav';
import { DirectoryPageNavIsland } from '../components/DirectoryPageNavIsland';
import { DiscussionLayout } from '../components/DiscussionLayout';
import { Footer } from '../components/Footer';
import { GridItem } from '../components/GridItem';
Expand Down Expand Up @@ -311,7 +311,7 @@ export const InteractiveLayout = (props: WebProps | AppsProps) => {
</>
)}
<main data-layout="InteractiveLayout">
<DirectoryPageNav
<DirectoryPageNavIsland
pageId={article.pageId}
pageTags={article.tags}
/>
Expand Down
Loading
Loading