From 8b4915854b4749c25a16e36f0283e1e03ef0f355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLilyL0u=E2=80=9D?= Date: Thu, 16 Jul 2026 16:12:37 +0100 Subject: [PATCH 01/23] wip --- src/app/components/Navigation/index.test.tsx | 268 +++++++++++++++++- src/app/components/Navigation/index.tsx | 51 +++- .../components/PageLayoutWrapper/index.tsx | 16 ++ src/app/legacy/containers/Header/index.jsx | 7 +- 4 files changed, 332 insertions(+), 10 deletions(-) diff --git a/src/app/components/Navigation/index.test.tsx b/src/app/components/Navigation/index.test.tsx index 939333dc8cf..ccab1333b39 100644 --- a/src/app/components/Navigation/index.test.tsx +++ b/src/app/components/Navigation/index.test.tsx @@ -1,4 +1,13 @@ -import { ARTICLE_PAGE, HOME_PAGE } from '#app/routes/utils/pageTypes'; +import { + ARTICLE_PAGE, + HOME_PAGE, + TOPIC_PAGE, + TV_PAGE, + LIVE_TV_PAGE, + AUDIO_PAGE, + LIVE_RADIO_PAGE, + MEDIA_ARTICLE_PAGE, +} from '#app/routes/utils/pageTypes'; import LanguageNavigation from '#app/legacy/containers/Navigation/LanguageNavigation'; import { render, @@ -307,6 +316,263 @@ describe('Navigation', () => { }); }); + describe('Page-type navigation attribution', () => { + const commonProps = { + bbcOrigin: 'https://www.test.bbc.co.uk', + id: 'c0000000000o', + isAmp: false, + statusCode: 200, + service: 'news' as const, + }; + + const navItemsWithWatch = [ + { title: 'Home', url: '/hausa' }, + { title: 'Watch', url: '/hausa/bbc_hausa_tv' }, + { title: 'Listen', url: '/hausa/bbc_hausa_radio/liveradio' }, + ]; + + const navItemsWithSubItemWatch = [ + { title: 'Home', url: '/hausa' }, + { + title: 'Watch', + url: '/hausa/watch', + subItems: [{ title: 'Live TV', url: '/hausa/watch/bbc_hausa_tv/live' }], + }, + { title: 'Listen', url: '/hausa/bbc_hausa_radio/liveradio' }, + ]; + + const navItemsWithoutWatchOrListen = [{ title: 'Home', url: '/news' }]; + + it('highlights Home for an article page with no URL match', () => { + const { container } = render( + , + { + ...commonProps, + pageType: ARTICLE_PAGE, + pathname: '/news/articles/c0000000000o', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + + it('highlights Home for an article page with no primaryMediaType, even with Watch/Listen nav items', () => { + const { container } = render( + , + { + ...commonProps, + pageType: ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/hausa'); + }); + + it('highlights Listen nav item for an article page with audio primaryMediaType', () => { + const { container } = render( + , + { + ...commonProps, + pageType: ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute( + 'href', + '/hausa/bbc_hausa_radio/liveradio', + ); + }); + + it('highlights Watch nav item for an article page with video primaryMediaType', () => { + const { container } = render( + , + { + ...commonProps, + pageType: ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); + }); + + it('falls back to Home for an article page with audio primaryMediaType when no Listen nav item exists', () => { + const { container } = render( + , + { + ...commonProps, + pageType: ARTICLE_PAGE, + pathname: '/news/articles/c0000000000o', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + + it('highlights Home for a topic page with no URL match', () => { + const { container } = render( + , + { + ...commonProps, + pageType: TOPIC_PAGE, + pathname: '/news/topics/c0000000000t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + + it('highlights Watch nav item for a tv page type', () => { + const { container } = render( + , + { + ...commonProps, + pageType: TV_PAGE, + pathname: '/hausa/bbc_hausa_tv/tv/w172yjj83ptptnj', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); + }); + + it('highlights Watch nav item for a liveTV page type', () => { + const { container } = render( + , + { + ...commonProps, + pageType: LIVE_TV_PAGE, + pathname: '/hausa/watch/bbc_hausa_tv/live', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/hausa/watch'); + }); + + it('highlights Watch nav item for a mediaArticle page type', () => { + const { container } = render( + , + { + ...commonProps, + pageType: MEDIA_ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); + }); + + it('falls back to Home for a video mediaArticle page type when no Watch nav item exists', () => { + const { container } = render( + , + { + ...commonProps, + pageType: MEDIA_ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + + it('highlights Listen nav item for an audio mediaArticle page type', () => { + const { container } = render( + , + { + ...commonProps, + pageType: MEDIA_ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute( + 'href', + '/hausa/bbc_hausa_radio/liveradio', + ); + }); + + it('falls back to Home for an audio mediaArticle page type when no Listen nav item exists', () => { + const { container } = render( + , + { + ...commonProps, + pageType: MEDIA_ARTICLE_PAGE, + pathname: '/hausa/articles/c1234567890t', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + + it('highlights Listen nav item for an audio page type', () => { + const { container } = render( + , + { + ...commonProps, + pageType: AUDIO_PAGE, + pathname: '/hausa/bbc_hausa_radio/audio/w3ct5yzk', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute( + 'href', + '/hausa/bbc_hausa_radio/liveradio', + ); + }); + + it('highlights Listen nav item for a liveRadio page type', () => { + const { container } = render( + , + { + ...commonProps, + pageType: LIVE_RADIO_PAGE, + pathname: '/hausa/bbc_hausa_radio/liveradio', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute( + 'href', + '/hausa/bbc_hausa_radio/liveradio', + ); + }); + + it('falls back to Home when on a tv page but no Watch nav item exists', () => { + const { container } = render( + , + { + ...commonProps, + pageType: TV_PAGE, + pathname: '/news/bbc_news_tv/tv/w172xyz', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + + it('falls back to Home when on an audio page but no Listen nav item exists', () => { + const { container } = render( + , + { + ...commonProps, + pageType: LIVE_RADIO_PAGE, + pathname: '/news/bbc_news_radio/liveradio', + }, + ); + const activeLink = container.querySelector('[data-active="true"]'); + expect(activeLink).toHaveAttribute('href', '/news'); + }); + }); + describe('Language Navigation', () => { it('should render LanguageNavigation for WS service in all environment', async () => { const { getByTestId } = await act(async () => diff --git a/src/app/components/Navigation/index.tsx b/src/app/components/Navigation/index.tsx index ee545ee9e2b..1b54c5aeffc 100644 --- a/src/app/components/Navigation/index.tsx +++ b/src/app/components/Navigation/index.tsx @@ -9,6 +9,14 @@ import useViewTracker from '#app/hooks/useViewTracker'; import { RequestContext } from '#contexts/RequestContext'; import { ServiceContext } from '#contexts/ServiceContext'; import { Direction, Navigation, PageTypes } from '#app/models/types/global'; +import { + TV_PAGE, + LIVE_TV_PAGE, + AUDIO_PAGE, + LIVE_RADIO_PAGE, + MEDIA_ARTICLE_PAGE, + ARTICLE_PAGE, +} from '#app/routes/utils/pageTypes'; import { TopStoryItem } from '#app/pages/ArticlePage/PagePromoSections/TopStoriesSection/types'; import Canonical from './index.canonical'; import Amp from './index.amp'; @@ -119,21 +127,27 @@ const matchesUrl = ({ /** * Find which top item should be active: - * - If current page matches a top item url -> that index is active + * - If current page URL matches a top item url -> that index is active * - Else if it matches any subItem url -> parent index is active - * - Else if pageType === 'home' -> 0 - * - Else -> -1 (no active) + * - Else use page-type attribution: + * - Video page (tv, liveTV), video mediaArticle, or article with video primaryMediaType -> index 1 (Watch) + * - Audio page (audio, liveRadio), audio mediaArticle, or article with audio primaryMediaType -> index 2 (Listen) + * - Any other type (article, topic, home, etc.) -> index 0 (Home) + * Nav items are hopefully always ordered: 0=Home, 1=Watch, 2=Listen, otherwise it won't be possible to know which one to highlight when we aren't matching on url + * primaryMediaType must be explicitly 'video' or 'audio' to trigger Watch/Listen attribution. */ const getActiveTopIndex = ({ topItems, canonicalLink, origin, pageType, + primaryMediaType, }: { topItems: Navigation[]; origin: string; canonicalLink?: string; pageType?: PageTypes; + primaryMediaType?: 'audio' | 'video'; }) => { if (!topItems?.length) return -1; @@ -158,18 +172,37 @@ const getActiveTopIndex = ({ ); if (parentIndexByChild > -1) return parentIndexByChild; - // We always want the first top level nav item to be active on the home page, - // and the first nav item should always be 'Home' - if (pageType === 'home') return 0; + // Page-type attribution: nav items are ordered Home (0), Watch (1), Listen (2). + // Video pages, video mediaArticles, and article pages with a video primaryMediaType -> Watch (index 1). + if ( + pageType === TV_PAGE || + pageType === LIVE_TV_PAGE || + (pageType === MEDIA_ARTICLE_PAGE && primaryMediaType === 'video') || + (pageType === ARTICLE_PAGE && primaryMediaType === 'video') + ) { + return topItems.length > 1 ? 1 : 0; + } - return -1; -}; + // Audio pages, audio mediaArticles, and article pages with an audio primaryMediaType -> Listen (index 2). + if ( + pageType === AUDIO_PAGE || + pageType === LIVE_RADIO_PAGE || + (pageType === MEDIA_ARTICLE_PAGE && primaryMediaType === 'audio') || + (pageType === ARTICLE_PAGE && primaryMediaType === 'audio') + ) { + return topItems.length > 2 ? 2 : 0; + } + + // All other page types (article, topic, home, live, etc.) default to Home (index 0). + return 0; +};; type NavigationContainerProps = { navItems?: Navigation[]; propsForTopBarOJComponent?: { blocks?: TopStoryItem[]; }; + primaryMediaType?: 'audio' | 'video'; }; const navEventTrackingMetadata = { componentName: 'scrollable-navigation' }; @@ -178,6 +211,7 @@ const dropdownNavEventTrackingData = { componentName: 'dropdown-navigation' }; const NavigationContainer: React.FC = ({ navItems, propsForTopBarOJComponent, + primaryMediaType, }) => { const { isAmp, isLite, pageType, canonicalLink, origin } = use(RequestContext); @@ -228,6 +262,7 @@ const NavigationContainer: React.FC = ({ canonicalLink, origin, pageType, + primaryMediaType, }); const topScrollableListItems = ( diff --git a/src/app/components/PageLayoutWrapper/index.tsx b/src/app/components/PageLayoutWrapper/index.tsx index 182db566f04..f508c43c279 100644 --- a/src/app/components/PageLayoutWrapper/index.tsx +++ b/src/app/components/PageLayoutWrapper/index.tsx @@ -33,6 +33,7 @@ type Props = { type: PageTypes; topics?: { topicName: string }[]; }; + blockTypes?: string[]; content?: { model?: ModelType }; secondaryColumn?: { topStories: TopStoryItem[] }; mostRead?: { items: (OptimoMostReadRecord | CPSMostReadRecord)[] }; @@ -54,6 +55,20 @@ const PageLayoutWrapper = ({ const isErrorPage = ![200].includes(status) || !status; const pageType = pageData?.metadata?.type; + + const primaryMediaType = (() => { + const blockTypes = pageData?.blockTypes ?? []; + if (blockTypes.includes('audio')) return 'audio' as const; + if (blockTypes.includes('video')) return 'video' as const; + // Fallback: scan top-level content blocks for an audio or video block. + // This covers SFV articles where blockTypes may not be populated. + const contentBlocks = (pageData?.content?.model?.blocks ?? []) as { + type: string; + }[]; + if (contentBlocks.some(b => b.type === 'audio')) return 'audio' as const; + if (contentBlocks.some(b => b.type === 'video')) return 'video' as const; + return undefined; + })(); const reportingPageType = pageType?.replace(/ /g, ''); const isOfflinePage = pageType === OFFLINE_PAGE; const isWindowValid = typeof window !== 'undefined'; @@ -233,6 +248,7 @@ const PageLayoutWrapper = ({
{ +const HeaderContainer = ({ + navItems, + propsForTopBarOJComponent, + primaryMediaType, +}) => { const { isAmp, isApp, pageType, isLite } = use(RequestContext); const { service, translations, dir, scriptLink, lang, serviceLang } = use(ServiceContext); @@ -142,6 +146,7 @@ const HeaderContainer = ({ navItems, propsForTopBarOJComponent }) => { ); From 0fca8e3253d7c023efe62661f18125ec9ada61b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLilyL0u=E2=80=9D?= Date: Thu, 16 Jul 2026 17:12:45 +0100 Subject: [PATCH 02/23] prettier bug added an extra semicolon --- src/app/components/Navigation/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/Navigation/index.tsx b/src/app/components/Navigation/index.tsx index 1b54c5aeffc..4aa21867947 100644 --- a/src/app/components/Navigation/index.tsx +++ b/src/app/components/Navigation/index.tsx @@ -195,7 +195,7 @@ const getActiveTopIndex = ({ // All other page types (article, topic, home, live, etc.) default to Home (index 0). return 0; -};; +}; type NavigationContainerProps = { navItems?: Navigation[]; From afd4605a08bec75e206f4dea886c12703af7693f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLilyL0u=E2=80=9D?= Date: Thu, 16 Jul 2026 17:51:42 +0100 Subject: [PATCH 03/23] put back default of nothing highlighted for article not in bottom nav --- src/app/components/Navigation/index.test.tsx | 32 ++++++++++---------- src/app/components/Navigation/index.tsx | 12 +++++--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/app/components/Navigation/index.test.tsx b/src/app/components/Navigation/index.test.tsx index ccab1333b39..536f3d9da07 100644 --- a/src/app/components/Navigation/index.test.tsx +++ b/src/app/components/Navigation/index.test.tsx @@ -343,7 +343,7 @@ describe('Navigation', () => { const navItemsWithoutWatchOrListen = [{ title: 'Home', url: '/news' }]; - it('highlights Home for an article page with no URL match', () => { + it('highlights nothing for an article page with no URL match', () => { const { container } = render( , { @@ -353,10 +353,10 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); - it('highlights Home for an article page with no primaryMediaType, even with Watch/Listen nav items', () => { + it('highlights nothing for an article page with no primaryMediaType, even with Watch/Listen nav items', () => { const { container } = render( , { @@ -366,7 +366,7 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/hausa'); + expect(activeLink).toBeNull(); }); it('highlights Listen nav item for an article page with audio primaryMediaType', () => { @@ -398,7 +398,7 @@ describe('Navigation', () => { expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); }); - it('falls back to Home for an article page with audio primaryMediaType when no Listen nav item exists', () => { + it('highlights nothing for an article page with audio primaryMediaType when no Listen nav item exists', () => { const { container } = render( { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); - it('highlights Home for a topic page with no URL match', () => { + it('highlights nothing for a topic page with no URL match', () => { const { container } = render( , { @@ -424,7 +424,7 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); it('highlights Watch nav item for a tv page type', () => { @@ -466,7 +466,7 @@ describe('Navigation', () => { expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); }); - it('falls back to Home for a video mediaArticle page type when no Watch nav item exists', () => { + it('highlights nothing for a video mediaArticle page type when no Watch nav item exists', () => { const { container } = render( { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); it('highlights Listen nav item for an audio mediaArticle page type', () => { @@ -498,7 +498,7 @@ describe('Navigation', () => { ); }); - it('falls back to Home for an audio mediaArticle page type when no Listen nav item exists', () => { + it('highlights nothing for an audio mediaArticle page type when no Listen nav item exists', () => { const { container } = render( { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); it('highlights Listen nav item for an audio page type', () => { @@ -546,7 +546,7 @@ describe('Navigation', () => { ); }); - it('falls back to Home when on a tv page but no Watch nav item exists', () => { + it('highlights nothing when on a tv page but no Watch nav item exists', () => { const { container } = render( , { @@ -556,10 +556,10 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); - it('falls back to Home when on an audio page but no Listen nav item exists', () => { + it('highlights nothing when on an audio page but no Listen nav item exists', () => { const { container } = render( , { @@ -569,7 +569,7 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toHaveAttribute('href', '/news'); + expect(activeLink).toBeNull(); }); }); diff --git a/src/app/components/Navigation/index.tsx b/src/app/components/Navigation/index.tsx index 4aa21867947..0b968ac5ca8 100644 --- a/src/app/components/Navigation/index.tsx +++ b/src/app/components/Navigation/index.tsx @@ -132,9 +132,10 @@ const matchesUrl = ({ * - Else use page-type attribution: * - Video page (tv, liveTV), video mediaArticle, or article with video primaryMediaType -> index 1 (Watch) * - Audio page (audio, liveRadio), audio mediaArticle, or article with audio primaryMediaType -> index 2 (Listen) - * - Any other type (article, topic, home, etc.) -> index 0 (Home) + * - Any other type (article, topic, home, etc.) -> nothing highlighted (-1) * Nav items are hopefully always ordered: 0=Home, 1=Watch, 2=Listen, otherwise it won't be possible to know which one to highlight when we aren't matching on url * primaryMediaType must be explicitly 'video' or 'audio' to trigger Watch/Listen attribution. + * Home is only ever highlighted via a direct URL match (top-level or subItem), never as a fallback. */ const getActiveTopIndex = ({ topItems, @@ -180,7 +181,7 @@ const getActiveTopIndex = ({ (pageType === MEDIA_ARTICLE_PAGE && primaryMediaType === 'video') || (pageType === ARTICLE_PAGE && primaryMediaType === 'video') ) { - return topItems.length > 1 ? 1 : 0; + return topItems.length > 1 ? 1 : -1; } // Audio pages, audio mediaArticles, and article pages with an audio primaryMediaType -> Listen (index 2). @@ -190,11 +191,12 @@ const getActiveTopIndex = ({ (pageType === MEDIA_ARTICLE_PAGE && primaryMediaType === 'audio') || (pageType === ARTICLE_PAGE && primaryMediaType === 'audio') ) { - return topItems.length > 2 ? 2 : 0; + return topItems.length > 2 ? 2 : -1; } - // All other page types (article, topic, home, live, etc.) default to Home (index 0). - return 0; + // All other page types (article, topic, home, live, etc.) have no page-type + // attribution; leave nothing highlighted unless a URL match was found above. + return -1; }; type NavigationContainerProps = { From 4e8688719e333ae4acc61b19a55cba2ada22fda6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLilyL0u=E2=80=9D?= Date: Fri, 17 Jul 2026 14:00:54 +0100 Subject: [PATCH 04/23] make home default highlighted --- src/app/components/Navigation/index.test.tsx | 32 ++++++++++---------- src/app/components/Navigation/index.tsx | 15 +++++---- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/app/components/Navigation/index.test.tsx b/src/app/components/Navigation/index.test.tsx index 536f3d9da07..ccab1333b39 100644 --- a/src/app/components/Navigation/index.test.tsx +++ b/src/app/components/Navigation/index.test.tsx @@ -343,7 +343,7 @@ describe('Navigation', () => { const navItemsWithoutWatchOrListen = [{ title: 'Home', url: '/news' }]; - it('highlights nothing for an article page with no URL match', () => { + it('highlights Home for an article page with no URL match', () => { const { container } = render( , { @@ -353,10 +353,10 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); - it('highlights nothing for an article page with no primaryMediaType, even with Watch/Listen nav items', () => { + it('highlights Home for an article page with no primaryMediaType, even with Watch/Listen nav items', () => { const { container } = render( , { @@ -366,7 +366,7 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/hausa'); }); it('highlights Listen nav item for an article page with audio primaryMediaType', () => { @@ -398,7 +398,7 @@ describe('Navigation', () => { expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); }); - it('highlights nothing for an article page with audio primaryMediaType when no Listen nav item exists', () => { + it('falls back to Home for an article page with audio primaryMediaType when no Listen nav item exists', () => { const { container } = render( { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); - it('highlights nothing for a topic page with no URL match', () => { + it('highlights Home for a topic page with no URL match', () => { const { container } = render( , { @@ -424,7 +424,7 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); it('highlights Watch nav item for a tv page type', () => { @@ -466,7 +466,7 @@ describe('Navigation', () => { expect(activeLink).toHaveAttribute('href', '/hausa/bbc_hausa_tv'); }); - it('highlights nothing for a video mediaArticle page type when no Watch nav item exists', () => { + it('falls back to Home for a video mediaArticle page type when no Watch nav item exists', () => { const { container } = render( { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); it('highlights Listen nav item for an audio mediaArticle page type', () => { @@ -498,7 +498,7 @@ describe('Navigation', () => { ); }); - it('highlights nothing for an audio mediaArticle page type when no Listen nav item exists', () => { + it('falls back to Home for an audio mediaArticle page type when no Listen nav item exists', () => { const { container } = render( { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); it('highlights Listen nav item for an audio page type', () => { @@ -546,7 +546,7 @@ describe('Navigation', () => { ); }); - it('highlights nothing when on a tv page but no Watch nav item exists', () => { + it('falls back to Home when on a tv page but no Watch nav item exists', () => { const { container } = render( , { @@ -556,10 +556,10 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); - it('highlights nothing when on an audio page but no Listen nav item exists', () => { + it('falls back to Home when on an audio page but no Listen nav item exists', () => { const { container } = render( , { @@ -569,7 +569,7 @@ describe('Navigation', () => { }, ); const activeLink = container.querySelector('[data-active="true"]'); - expect(activeLink).toBeNull(); + expect(activeLink).toHaveAttribute('href', '/news'); }); }); diff --git a/src/app/components/Navigation/index.tsx b/src/app/components/Navigation/index.tsx index 0b968ac5ca8..48f64c62b98 100644 --- a/src/app/components/Navigation/index.tsx +++ b/src/app/components/Navigation/index.tsx @@ -132,10 +132,10 @@ const matchesUrl = ({ * - Else use page-type attribution: * - Video page (tv, liveTV), video mediaArticle, or article with video primaryMediaType -> index 1 (Watch) * - Audio page (audio, liveRadio), audio mediaArticle, or article with audio primaryMediaType -> index 2 (Listen) - * - Any other type (article, topic, home, etc.) -> nothing highlighted (-1) + * - Any other type (non-media article, topic, home, etc.) -> index 0 (Home) * Nav items are hopefully always ordered: 0=Home, 1=Watch, 2=Listen, otherwise it won't be possible to know which one to highlight when we aren't matching on url * primaryMediaType must be explicitly 'video' or 'audio' to trigger Watch/Listen attribution. - * Home is only ever highlighted via a direct URL match (top-level or subItem), never as a fallback. + * Home is the default/fallback for every page that isn't a Watch or Listen match. */ const getActiveTopIndex = ({ topItems, @@ -181,7 +181,7 @@ const getActiveTopIndex = ({ (pageType === MEDIA_ARTICLE_PAGE && primaryMediaType === 'video') || (pageType === ARTICLE_PAGE && primaryMediaType === 'video') ) { - return topItems.length > 1 ? 1 : -1; + return topItems.length > 1 ? 1 : 0; } // Audio pages, audio mediaArticles, and article pages with an audio primaryMediaType -> Listen (index 2). @@ -191,13 +191,12 @@ const getActiveTopIndex = ({ (pageType === MEDIA_ARTICLE_PAGE && primaryMediaType === 'audio') || (pageType === ARTICLE_PAGE && primaryMediaType === 'audio') ) { - return topItems.length > 2 ? 2 : -1; + return topItems.length > 2 ? 2 : 0; } - // All other page types (article, topic, home, live, etc.) have no page-type - // attribution; leave nothing highlighted unless a URL match was found above. - return -1; -}; + // All other page types (article, topic, home, live, etc.) default to Home (index 0). + return 0; +};; type NavigationContainerProps = { navItems?: Navigation[]; From 48971724b3f7e08c13d5be9fa3d2cf0b567899c7 Mon Sep 17 00:00:00 2001 From: Nabeel Khan Date: Tue, 21 Jul 2026 14:34:00 +0100 Subject: [PATCH 05/23] Updated snapshots and fixed linting issues --- .../__snapshots__/index.test.tsx.snap | 452 ++++++++++++------ src/app/components/Navigation/index.tsx | 2 +- 2 files changed, 316 insertions(+), 138 deletions(-) diff --git a/src/app/components/Navigation/__snapshots__/index.test.tsx.snap b/src/app/components/Navigation/__snapshots__/index.test.tsx.snap index b301f45ffa0..b312e27a4bd 100644 --- a/src/app/components/Navigation/__snapshots__/index.test.tsx.snap +++ b/src/app/components/Navigation/__snapshots__/index.test.tsx.snap @@ -1218,6 +1218,7 @@ exports[`Navigation should correctly render amp navigation on a URL not associat right: 0; bottom: 0; border-bottom: 0.25rem solid #B80000; + border-bottom: 0.3125rem solid #B80000; } .emotion-14:focus::after { @@ -1242,7 +1243,94 @@ exports[`Navigation should correctly render amp navigation on a URL not associat border: 0.1875rem solid #000000; } -.emotion-20 { +.emotion-16::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; +} + +.emotion-18 { + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + clip: rect(1px, 1px, 1px, 1px); + height: 1px; + overflow: hidden; + position: absolute; + width: 1px; + margin: 0; + white-space: nowrap; +} + +.emotion-21 { + font-size: 0.9375rem; + line-height: 1.25rem; + font-family: ReithSans,Helvetica,Arial,sans-serif; + font-style: normal; + font-weight: 400; + color: #141414; + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + display: inline-block; + padding: 0.75rem 0.25rem; + outline: none; +} + +@media (min-width: 20rem) and (max-width: 37.4375rem) { + .emotion-21 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (min-width: 37.5rem) { + .emotion-21 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (max-width: 37.4375rem) { + .emotion-21 { + padding: 0.75rem 0.5rem; + } +} + +.emotion-21:hover::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; +} + +.emotion-21:focus::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + top: 0; + border: 0.1875rem solid #000000; +} + +.emotion-21:focus-visible::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + top: 0; + border: 0.1875rem solid #000000; +} + +.emotion-23 { position: relative; padding: 0; margin: 0; @@ -1257,14 +1345,14 @@ exports[`Navigation should correctly render amp navigation on a URL not associat height: 2.75rem; } -.emotion-20:hover, -.emotion-20:focus { +.emotion-23:hover, +.emotion-23:focus { cursor: pointer; box-shadow: inset 0 0 0 0.25rem #FFFFFF; } -.emotion-20:hover::after, -.emotion-20:focus::after { +.emotion-23:hover::after, +.emotion-23:focus::after { content: ''; position: absolute; left: 0; @@ -1275,59 +1363,47 @@ exports[`Navigation should correctly render amp navigation on a URL not associat } @media (min-width: 37.5rem) { - .emotion-20 { + .emotion-23 { display: none; visibility: hidden; } } @media (min-width: 20rem) { - .emotion-20 { + .emotion-23 { height: 2.75rem; width: 2.75rem; } } -.emotion-20 svg { +.emotion-23 svg { vertical-align: middle; } @media (min-width: 20rem) { - .emotion-20 { + .emotion-23 { width: 2.75rem; height: 2.75rem; } } -.emotion-20 svg { +.emotion-23 svg { vertical-align: middle; fill: #FFFFFF; } -.emotion-22 { +.emotion-25 { color: #000000; fill: currentColor; } @media screen and (forced-colors: active) { - .emotion-22 { + .emotion-25 { fill: linkText; } } -.emotion-26 { - -webkit-clip-path: inset(100%); - clip-path: inset(100%); - clip: rect(1px, 1px, 1px, 1px); - height: 1px; - overflow: hidden; - position: absolute; - width: 1px; - margin: 0; - white-space: nowrap; -} - -.emotion-27 { +.emotion-30 { background-color: #FFFFFF; clear: both; position: absolute; @@ -1339,38 +1415,38 @@ exports[`Navigation should correctly render amp navigation on a URL not associat } @media (min-width: 37.5rem) { - .emotion-27 { + .emotion-30 { display: none; visibility: hidden; } } -.emotion-27 ul { +.emotion-30 ul { padding: 0; border: none; } -.emotion-27 ul li { +.emotion-30 ul li { padding: 0; } -.emotion-27 ul li:last-child { +.emotion-30 ul li:last-child { padding-bottom: 0; } -.emotion-27 a { +.emotion-30 a { display: block; position: relative; padding-inline: 0.5rem; } -.emotion-27 a:hover { +.emotion-30 a:hover { background-color: #E6E8EA; -webkit-text-decoration: none; text-decoration: none; } -.emotion-27 a:before { +.emotion-30 a:before { content: ""; position: absolute; top: 0; @@ -1382,32 +1458,32 @@ exports[`Navigation should correctly render amp navigation on a URL not associat opacity: 0; } -.emotion-27 a:hover::before { +.emotion-30 a:hover::before { opacity: 1; } -.emotion-27 a:focus-visible { +.emotion-30 a:focus-visible { outline-offset: -0.1875rem; } -.emotion-29 { +.emotion-32 { list-style-type: none; margin: 0; padding: 0 0.5rem; border-bottom: 0.0625rem solid #E6E8EA; } -.emotion-31 { +.emotion-34 { padding: 0.75rem 0; border-bottom: 0.0625rem solid #E6E8EA; } -.emotion-31:last-child { +.emotion-34:last-child { padding-bottom: 0.25rem; border: 0; } -.emotion-33 { +.emotion-36 { font-size: 0.9375rem; line-height: 1.25rem; font-family: ReithSans,Helvetica,Arial,sans-serif; @@ -1421,34 +1497,34 @@ exports[`Navigation should correctly render amp navigation on a URL not associat } @media (min-width: 20rem) and (max-width: 37.4375rem) { - .emotion-33 { + .emotion-36 { font-size: 1rem; line-height: 1.25rem; } } @media (min-width: 37.5rem) { - .emotion-33 { + .emotion-36 { font-size: 1rem; line-height: 1.25rem; } } -.emotion-33:hover, -.emotion-33:focus { +.emotion-36:hover, +.emotion-36:focus { -webkit-text-decoration: underline; text-decoration: underline; text-decoration-color: #B80000; } -.emotion-35 { +.emotion-38 { width: 100%; position: relative; z-index: 1; } @media (max-width: 37.4375rem) { - .emotion-36 { + .emotion-39 { white-space: nowrap; overflow-x: scroll; scroll-behavior: auto; @@ -1459,20 +1535,20 @@ exports[`Navigation should correctly render amp navigation on a URL not associat } @media (min-width: 25rem) { - .emotion-36 { + .emotion-39 { scroll-padding-inline-end: 6rem; } } - .emotion-36::-webkit-scrollbar { + .emotion-39::-webkit-scrollbar { display: none; } - .emotion-36:focus-visible { + .emotion-39:focus-visible { outline: none; } - .emotion-36:focus-visible::after { + .emotion-39:focus-visible::after { outline: 0.1875rem solid #000000; content: ''; position: absolute; @@ -1480,7 +1556,7 @@ exports[`Navigation should correctly render amp navigation on a URL not associat height: 100%; } - .emotion-36:after { + .emotion-39:after { content: ' '; height: 100%; width: 3rem; @@ -1498,18 +1574,18 @@ exports[`Navigation should correctly render amp navigation on a URL not associat } @media (min-width: 25rem) { - .emotion-36:after { + .emotion-39:after { width: 6rem; } } } -.emotion-36 li { +.emotion-39 li { -webkit-margin-end: 0; margin-inline-end: 0; } -.emotion-36 li a { +.emotion-39 li a { padding: 0 0.5rem; height: 2.75rem; display: -webkit-box; @@ -1525,7 +1601,7 @@ exports[`Navigation should correctly render amp navigation on a URL not associat justify-content: center; } -.emotion-36 li:before { +.emotion-39 li:before { content: ""; position: absolute; inset-inline-end: 0; @@ -1541,12 +1617,12 @@ exports[`Navigation should correctly render amp navigation on a URL not associat opacity: 1; } -.emotion-36 li:last-child:before { +.emotion-39 li:last-child:before { display: none; } @media (max-width: 37.4375rem) { - .emotion-36.si-nav-scrollable-hidden { + .emotion-39.si-nav-scrollable-hidden { display: none; visibility: hidden; } @@ -1589,10 +1665,23 @@ exports[`Navigation should correctly render amp navigation on a URL not associat role="listitem" > - Home + + + Current page, + + Home +
  • About @@ -1621,7 +1710,7 @@ exports[`Navigation should correctly render amp navigation on a URL not associat
  • - Home + + + Current page, + + Home +
  • About @@ -4227,13 +4405,13 @@ exports[`Navigation should correctly render canonical navigation on a URL not as +
  • +
    +
    + +
    +
    +
    +
    +
    +
      +
    +
    + +
    +
    + + +`; + +exports[`Navigation Page-type navigation attribution highlights dropdown item when URL matches a nested topic subItem on Home 1`] = ` +.emotion-0 { + position: relative; + width: 100%; + margin: 0 auto; + border-bottom: 0.0625rem solid #B80000; + opacity: 0.7; +} + +.emotion-1 { + position: relative; + background-color: #FFFFFF; +} + +@media (min-width: 62.9375rem) { + .emotion-1::after { + content: ''; + position: absolute; + bottom: 0; + right: 0; + left: 0; + border-bottom: 0.0625rem solid #E6E8EA; + } +} + +.emotion-1 .emotion-13::after { + left: 0; +} + +.emotion-3 { + position: relative; + max-width: 63.4rem; + margin: 0 auto; + background-color: #FFFFFF; +} + +@media (min-width: 37.5rem) { + .emotion-3 { + margin: 0 0.8rem; + } +} + +@media (min-width: 66rem) { + .emotion-3 { + margin: 0 auto; + } +} + +.emotion-5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; +} + +.emotion-6 { + position: relative; + width: 100%; +} + +.emotion-7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + justify-content: space-between; + position: relative; + z-index: 0; + background-color: #B80000; +} + +.emotion-7::before { + content: ''; + position: absolute; + z-index: -1; + top: 0; + bottom: 0; + width: 100vw; + left: 50%; + -webkit-transform: translateX(-50%); + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + transform: translateX(-50%); + background: #B80000; + pointer-events: none; +} + +@media (max-width: 37.4375rem) { + .emotion-8 { + white-space: nowrap; + overflow-x: scroll; + scroll-behavior: auto; + -webkit-overflow-scrolling: touch; + scroll-padding-inline-end: 3rem; + scrollbar-width: none; + -ms-overflow-style: none; + } + + @media (min-width: 25rem) { + .emotion-8 { + scroll-padding-inline-end: 6rem; + } + } + + .emotion-8::-webkit-scrollbar { + display: none; + } + + .emotion-8:focus-visible { + outline: none; + } + + .emotion-8:focus-visible::after { + outline: 0.1875rem solid #000000; + content: ''; + position: absolute; + width: 100%; + height: 100%; + } + + .emotion-8:after { + content: ' '; + height: 100%; + width: 3rem; + position: absolute; + right: 0; + bottom: 0; + z-index: 3; + overflow: hidden; + pointer-events: none; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 1) 100% + ); + } + + @media (min-width: 25rem) { + .emotion-8:after { + width: 6rem; + } + } +} + +.emotion-8 li { + -webkit-margin-end: 0; + margin-inline-end: 0; +} + +.emotion-8 li a { + color: #FFFFFF; + padding: 0 0.5rem; + height: 2.75rem; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; +} + +.emotion-8 li a:hover::after { + border-bottom-color: #FFFFFF; +} + +.emotion-8 li a:focus-visible::after { + box-shadow: inset 0 0 0 0.1875rem #FFFFFF; + outline: 0.125rem solid #000000; + outline-offset: -0.125rem; +} + +.emotion-8 li a:focus::after { + box-shadow: inset 0 0 0 0.1875rem #FFFFFF; + outline: 0.125rem solid #000000; + outline-offset: -0.125rem; +} + +.emotion-8 li a[data-active="true"] span::after { + border-bottom-color: #FFFFFF; +} + +.emotion-8 li:before { + content: ""; + position: absolute; + inset-inline-end: 0; + top: 50%; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + height: 60%; + width: 0.0625rem; + background: #FFFFFF; + display: block; + opacity: 0.3; +} + +.emotion-8 li:last-child:before { + display: none; +} + +.emotion-8:after { + background: none; +} + +.emotion-10 { + list-style-type: none; + padding: 0; + margin: 0; + position: relative; +} + +@media (min-width: 37.5rem) { + .emotion-10 { + overflow: hidden; + } +} + +.emotion-12 { + display: inline-block; + position: relative; + z-index: 2; + -webkit-margin-end: 0.75rem; + margin-inline-end: 0.75rem; +} + +@media (max-width: 37.4375rem) { + .emotion-12:last-child { + margin-right: 3rem; + } +} + +@media (min-width: 37.5rem) { + .emotion-12::after { + content: ''; + position: absolute; + bottom: -1px; + width: 80rem; + border-bottom: 0.0625rem solid #E6E8EA; + z-index: -1; + } +} + +.emotion-14 { + font-size: 0.9375rem; + line-height: 1.25rem; + font-family: ReithSans,Helvetica,Arial,sans-serif; + font-style: normal; + font-weight: 400; + color: #141414; + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + display: inline-block; + padding: 0.75rem 0.25rem; + outline: none; +} + +@media (min-width: 20rem) and (max-width: 37.4375rem) { + .emotion-14 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (min-width: 37.5rem) { + .emotion-14 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (max-width: 37.4375rem) { + .emotion-14 { + padding: 0.75rem 0.5rem; + } +} + +.emotion-14:hover::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + border-bottom: 0.3125rem solid #B80000; +} + +.emotion-14:focus::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + top: 0; + border: 0.1875rem solid #000000; +} + +.emotion-14:focus-visible::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + top: 0; + border: 0.1875rem solid #000000; +} + +.emotion-16::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; +} + +.emotion-20 { + font-size: 0.9375rem; + line-height: 1.25rem; + font-family: ReithSans,Helvetica,Arial,sans-serif; + font-style: normal; + font-weight: 400; + color: #141414; + cursor: pointer; + -webkit-text-decoration: none; + text-decoration: none; + display: inline-block; + padding: 0.75rem 0.25rem; + outline: none; +} + +@media (min-width: 20rem) and (max-width: 37.4375rem) { + .emotion-20 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (min-width: 37.5rem) { + .emotion-20 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (max-width: 37.4375rem) { + .emotion-20 { + padding: 0.75rem 0.5rem; + } +} + +.emotion-20:hover::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; +} + +.emotion-20:focus::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + top: 0; + border: 0.1875rem solid #000000; +} + +.emotion-20:focus-visible::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-bottom: 0.25rem solid #B80000; + top: 0; + border: 0.1875rem solid #000000; +} + +.emotion-26 { + position: relative; + padding: 0; + margin: 0; + background-color: transparent; + border: 0; + float: left; + height: 2.75rem; + width: 2.75rem; + background-color: #B80000; + color: #FFFFFF; + width: 2.75rem; + height: 2.75rem; +} + +.emotion-26:hover, +.emotion-26:focus { + cursor: pointer; + box-shadow: inset 0 0 0 0.25rem #FFFFFF; +} + +.emotion-26:hover::after, +.emotion-26:focus::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + border: 0.25rem solid #000000; +} + +@media (min-width: 37.5rem) { + .emotion-26 { + display: none; + visibility: hidden; + } +} + +@media (min-width: 20rem) { + .emotion-26 { + height: 2.75rem; + width: 2.75rem; + } +} + +.emotion-26 svg { + vertical-align: middle; +} + +@media (min-width: 20rem) { + .emotion-26 { + width: 2.75rem; + height: 2.75rem; + } +} + +.emotion-26 svg { + vertical-align: middle; + fill: #FFFFFF; +} + +.emotion-28 { + color: #000000; + fill: currentColor; +} + +@media screen and (forced-colors: active) { + .emotion-28 { + fill: linkText; + } +} + +.emotion-30 { + -webkit-clip-path: inset(100%); + clip-path: inset(100%); + clip: rect(1px, 1px, 1px, 1px); + height: 1px; + overflow: hidden; + position: absolute; + width: 1px; + margin: 0; + white-space: nowrap; +} + +.emotion-31 { + background-color: #FFFFFF; + clear: both; + overflow: hidden; + height: 0; + -webkit-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + transition-timing-function: cubic-bezier(0, 0, 0.58, 1); + visibility: hidden; + position: absolute; + top: 100%; + left: 0; + width: 100%; + z-index: 99999; + border-bottom: 0.1875rem solid #B80000; +} + +@media (min-width: 37.5rem) { + .emotion-31 { + display: none; + visibility: hidden; + } +} + +@media (prefers-reduced-motion: reduce) { + .emotion-31 { + -webkit-transition: none; + transition: none; + } +} + +.emotion-31 ul { + padding: 0; + border: none; +} + +.emotion-31 ul li { + padding: 0; +} + +.emotion-31 ul li:last-child { + padding-bottom: 0; +} + +.emotion-31 a { + display: block; + position: relative; + padding-inline: 0.5rem; +} + +.emotion-31 a:hover { + background-color: #E6E8EA; + -webkit-text-decoration: none; + text-decoration: none; +} + +.emotion-31 a:before { + content: ""; + position: absolute; + top: 0; + inset-inline-start: 0; + height: 100%; + width: 0.25rem; + background: #B80000; + display: block; + opacity: 0; +} + +.emotion-31 a:hover::before { + opacity: 1; +} + +.emotion-31 a:focus-visible { + outline-offset: -0.1875rem; +} + +.emotion-33 { + list-style-type: none; + margin: 0; + padding: 0 0.5rem; + border-bottom: 0.0625rem solid #E6E8EA; +} + +.emotion-35 { + padding: 0.75rem 0; + border-bottom: 0.0625rem solid #E6E8EA; +} + +.emotion-35:last-child { + padding-bottom: 0.25rem; + border: 0; +} + +.emotion-37 { + font-size: 0.9375rem; + line-height: 1.25rem; + font-family: ReithSans,Helvetica,Arial,sans-serif; + font-style: normal; + font-weight: 400; + color: #141414; + -webkit-text-decoration: none; + text-decoration: none; + padding: 0.75rem 0; + display: inline-block; +} + +@media (min-width: 20rem) and (max-width: 37.4375rem) { + .emotion-37 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +@media (min-width: 37.5rem) { + .emotion-37 { + font-size: 1rem; + line-height: 1.25rem; + } +} + +.emotion-37:hover, +.emotion-37:focus { + -webkit-text-decoration: underline; + text-decoration: underline; + text-decoration-color: #B80000; +} + +.emotion-43 { + width: 100%; + position: relative; + z-index: 1; +} + +@media (max-width: 37.4375rem) { + .emotion-44 { + white-space: nowrap; + overflow-x: scroll; + scroll-behavior: auto; + -webkit-overflow-scrolling: touch; + scroll-padding-inline-end: 3rem; + scrollbar-width: none; + -ms-overflow-style: none; + } + + @media (min-width: 25rem) { + .emotion-44 { + scroll-padding-inline-end: 6rem; + } + } + + .emotion-44::-webkit-scrollbar { + display: none; + } + + .emotion-44:focus-visible { + outline: none; + } + + .emotion-44:focus-visible::after { + outline: 0.1875rem solid #000000; + content: ''; + position: absolute; + width: 100%; + height: 100%; + } + + .emotion-44:after { + content: ' '; + height: 100%; + width: 3rem; + position: absolute; + right: 0; + bottom: 0; + z-index: 3; + overflow: hidden; + pointer-events: none; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 1) 100% + ); + } + + @media (min-width: 25rem) { + .emotion-44:after { + width: 6rem; + } + } +} + +.emotion-44 li { + -webkit-margin-end: 0; + margin-inline-end: 0; +} + +.emotion-44 li a { + padding: 0 0.5rem; + height: 2.75rem; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; +} + +.emotion-44 li:before { + content: ""; + position: absolute; + inset-inline-end: 0; + top: 50%; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + height: 60%; + width: 0.0625rem; + background: #B0B2B4; + display: block; + opacity: 1; +} + +.emotion-44 li:last-child:before { + display: none; +} + +.emotion-55 { + position: absolute; + width: 100%; + inset-inline-start: 0; +} + +@media (min-width: 1041px) { + .emotion-55 { + width: 100%; + } +} + +.emotion-55::after { + content: ''; + position: absolute; + inset-block-end: 0; + width: 100%; + border-bottom: 0.0625rem solid #E6E8EA; +} + +
    +
    + +
    +`; + exports[`Navigation should correctly render amp navigation 1`] = ` .emotion-0 { position: relative; @@ -763,7 +2599,8 @@ exports[`Navigation should correctly render amp navigation 1`] = ` role="listitem" > - - Current page, - Home @@ -1690,7 +3519,7 @@ exports[`Navigation should correctly render amp navigation on a URL not associat role="listitem" > About @@ -1710,7 +3539,7 @@ exports[`Navigation should correctly render amp navigation on a URL not associat
    - - Current page, - Home @@ -4394,7 +6217,7 @@ exports[`Navigation should correctly render canonical navigation on a URL not as role="listitem" > About @@ -4405,13 +6228,13 @@ exports[`Navigation should correctly render canonical navigation on a URL not as
    Légende vidéo, diff --git a/ws-nextjs-app/integration/pages/articles/gahuza/__snapshots__/canonical.test.ts.snap b/ws-nextjs-app/integration/pages/articles/gahuza/__snapshots__/canonical.test.ts.snap index 4ff19444668..a5dbc41e6eb 100644 --- a/ws-nextjs-app/integration/pages/articles/gahuza/__snapshots__/canonical.test.ts.snap +++ b/ws-nextjs-app/integration/pages/articles/gahuza/__snapshots__/canonical.test.ts.snap @@ -134,17 +134,31 @@ exports[`Canonical Articles Header I can see the branding 1`] = ` exports[`Canonical Articles Header Navigation link should match text and url 1`] = ` { - "text": "Uru rupapuro, Urupapuro rw'itangiriro", + "text": "Urupapuro rw'itangiriro", "url": "/gahuza", } `; +exports[`Canonical Articles Header Navigation link should match text and url 2`] = ` +{ + "text": "Raba", + "url": "/gahuza/topics/cpqx1yvd5ydt", +} +`; + +exports[`Canonical Articles Header Navigation link should match text and url 3`] = ` +{ + "text": "Umviriza", + "url": "/gahuza/bbc_gahuza_radio/liveradio", +} +`; + exports[`Canonical Articles Image Caption should match text 1`] = `"Insiguro y'isanamu, Alexei Navalny na Yulia Navalnaya bari i Moscow mu 2013 muri metingi yo kwiyamamaza kw'abakuru b'imijyi"`; exports[`Canonical Articles Image should match snapshot 1`] = ` Alexei Navalny na Yulia Navalnaya bari i Moscow mu 2013 muri metingi yo kwiyamamaza kw'abakuru b'imijyi @@ -15,10 +15,10 @@ exports[`Lite Site Articles Lite Site Summary should match snapshot 1`] = ` Ahagusaba uburyo (ama mega) buke

    Uriko ubona ku rubuga aherekana amakuru mu nyandiko gusa, hakoresha uburyo buke. Ja ku rubuga nyamukuru ubone amakuru mu nyandiko iherekejwe n'amasanamu.

    @@ -27,20 +27,20 @@ exports[`Lite Site Articles Lite Site Summary should match snapshot 1`] = ` data-e2e="to-main-site" >
    Njana ku rubuga nyamukuru canke aho gusoma gusa Ibindi vyerekeye ingene urwo rubuga rugutwara uburyo (ama mega) buke diff --git a/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/amp.test.ts.snap b/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/amp.test.ts.snap index 32777bb1a0a..fc6350323d9 100644 --- a/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/amp.test.ts.snap +++ b/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/amp.test.ts.snap @@ -82,11 +82,25 @@ exports[`AMP Articles Header I can see the branding 1`] = ` exports[`AMP Articles Header Navigation link should match text and url 1`] = ` { - "text": "Shafin da ake ciki, Labaran Duniya", + "text": "Labaran Duniya", "url": "/hausa", } `; +exports[`AMP Articles Header Navigation link should match text and url 2`] = ` +{ + "text": "Shafin da ake ciki, Kallo", + "url": "/hausa/topics/cn09qmz4jryt", +} +`; + +exports[`AMP Articles Header Navigation link should match text and url 3`] = ` +{ + "text": "Sauraro", + "url": "/hausa/bbc_hausa_radio/liveradio", +} +`; + exports[`AMP Articles Image Caption should match text 1`] = `"Bayanan hoto, Wannan hoton gwaji, mallakin BBC, ya nuna taswirar Faransa. Hoton yana cikin katangar ukun farko kuma yana da wannan taken."`; exports[`AMP Articles Image should match snapshot 1`] = ` diff --git a/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/canonical.test.ts.snap b/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/canonical.test.ts.snap index 9c96d25012b..aff3fc76a39 100644 --- a/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/canonical.test.ts.snap +++ b/ws-nextjs-app/integration/pages/articles/hausa/__snapshots__/canonical.test.ts.snap @@ -107,17 +107,31 @@ exports[`Canonical Articles Header I can see the branding 1`] = ` exports[`Canonical Articles Header Navigation link should match text and url 1`] = ` { - "text": "Shafin da ake ciki, Labaran Duniya", + "text": "Labaran Duniya", "url": "/hausa", } `; +exports[`Canonical Articles Header Navigation link should match text and url 2`] = ` +{ + "text": "Shafin da ake ciki, Kallo", + "url": "/hausa/topics/cn09qmz4jryt", +} +`; + +exports[`Canonical Articles Header Navigation link should match text and url 3`] = ` +{ + "text": "Sauraro", + "url": "/hausa/bbc_hausa_radio/liveradio", +} +`; + exports[`Canonical Articles Image Caption should match text 1`] = `"Bayanan hoto, Wannan hoton gwaji, mallakin BBC, ya nuna taswirar Faransa. Hoton yana cikin katangar ukun farko kuma yana da wannan taken."`; exports[`Canonical Articles Image should match snapshot 1`] = ` Taswirar Faransa wanda ke nuna Paris da Cognac
    توضیح ویدئو، diff --git a/ws-nextjs-app/integration/pages/articles/pidginAmpIframe/__snapshots__/amp.test.ts.snap b/ws-nextjs-app/integration/pages/articles/pidginAmpIframe/__snapshots__/amp.test.ts.snap index 7ff5674b62c..6dc3a378a03 100644 --- a/ws-nextjs-app/integration/pages/articles/pidginAmpIframe/__snapshots__/amp.test.ts.snap +++ b/ws-nextjs-app/integration/pages/articles/pidginAmpIframe/__snapshots__/amp.test.ts.snap @@ -10,11 +10,11 @@ exports[`AMP Articles Amp Iframe should match snapshot 1`] = ` width="640" >