Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/app/components/Curation/HighImpactPromo/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@ import readme from './README.md';

const highImpactFixtureCuration = fixture.data.curations[0] as BaseCuration;

const topicTags = [
{
topicId: 'c404v061z85t',
topicName: 'Africa',
subjectList: [],
curationList: [],
types: [],
home: '',
topicUrl: '',
},
{
topicId: 'c2dwqd1zr92t',
topicName: 'Nigeria',
subjectList: [],
curationList: [],
types: [],
home: '',
topicUrl: '',
},
];

const Component = () => {
return (
<div
Expand All @@ -16,13 +37,20 @@ const Component = () => {
maxWidth: '480px',
}}
>
{/* Default attribution (service-based) */}
<HighImpactPromo
{...(highImpactFixtureCuration.summaries?.[0] as Summary)}
attribution={{
link: '/pidgin',
text: 'BBC News Pidgin',
}}
/>
{/* Attribution with primaryTopic (topic-based) */}
<HighImpactPromo
{...(highImpactFixtureCuration.summaries?.[1] as Summary)}
primaryTopic={topicTags[0]}
/>
{/* Attribution with custom attribution prop */}
<HighImpactPromo
{...(highImpactFixtureCuration.summaries?.[1] as Summary)}
attribution={{
Expand Down
48 changes: 48 additions & 0 deletions src/app/components/Curation/HighImpactPromo/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from '#app/components/react-testing-library-with-providers';
import homePageFixture from '#data/ws/homePage/index.json';
import { Services } from '#app/models/types/global';
import getTopicPageUrl from '#app/utilities/getTopicPageUrl';
import HighImpactPromo, { HighImpactPromoProps } from '.';

const { summaries } = homePageFixture.data.curations[0];
Expand Down Expand Up @@ -99,6 +100,53 @@ describe('High Impact Promo', () => {
expect(attributionLink).toHaveAttribute('href', '/pidgin');
});

it('should render topic-based attribution if topics are present', () => {
const topics = [
{
topicId: 'c404v061z85t',
topicName: 'Africa',
subjectList: [],
curationList: [],
types: [],
home: '',
topicUrl: '',
},
{
topicId: 'c2dwqd1zr92t',
topicName: 'Nigeria',
subjectList: [],
curationList: [],
types: [],
home: '',
topicUrl: '',
},
];
const promoWithTopics = { ...promoFixtureData, topics };
render(<Fixture promoData={promoWithTopics} />, {
service: 'pidgin',
pageType: 'home',
// @ts-expect-error: translations is not in the official type but is supported by the test utils
translations: { topicsPath: 'topics' },
});

const attributionLink = screen.getByRole('link', { name: 'Africa' });
expect(attributionLink).toBeInTheDocument();
expect(attributionLink).toHaveAttribute(
'href',
getTopicPageUrl('c404v061z85t', 'pidgin'),
);
});

it('should fall back to service attribution if topics is empty', () => {
const promoWithNoTopics = { ...promoFixtureData, topics: [] };
render(<Fixture promoData={promoWithNoTopics} />, { service: 'pidgin' });
const attributionLink = screen.getByRole('link', {
name: 'BBC News Pidgin',
});
expect(attributionLink).toBeInTheDocument();
expect(attributionLink).toHaveAttribute('href', '/pidgin');
});

it.each<[Services, string]>([
['mundo', 'ltr'],
['arabic', 'rtl'],
Expand Down
18 changes: 13 additions & 5 deletions src/app/components/Curation/HighImpactPromo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { use } from 'react';
import pathOr from 'ramda/src/pathOr';
import { Summary } from '#app/models/types/curationData';
import Promo from '#components/Promo';
import useClickTrackerHandler from '#app/hooks/useClickTrackerHandler';
import { RequestContext } from '#app/contexts/RequestContext';
import { ServiceContext } from '#app/contexts/ServiceContext';
import { getBrandPath } from '#app/legacy/containers/Brand';
import getTopicPageUrl from '#app/utilities/getTopicPageUrl';
import styles from './index.styles';

type Attribution = {
Expand All @@ -24,13 +26,19 @@ const HighImpactPromo = ({
headingLevel = 3,
eventTrackingData,
attribution,
primaryTopic,
}: HighImpactPromoProps) => {
const { isAmp } = use(RequestContext);
const { dir, service, brandName } = use(ServiceContext) || {};
const { isAmp, variant } = use(RequestContext);
const { dir, service, brandName, translations } = use(ServiceContext) || {};

const attributionLink =
attribution?.link || (service ? getBrandPath(service) : null);
const attributionText = attribution?.text || brandName;
const topicsPath = pathOr('topics', ['topicsPath'], translations);

// Use primaryTopic for attribution if available, otherwise fall back to default attribution
const attributionLink = primaryTopic?.topicId
? getTopicPageUrl(primaryTopic.topicId, service, variant, topicsPath)
: attribution?.link || (service ? getBrandPath(service) : null);
const attributionText =
primaryTopic?.topicName || attribution?.text || brandName;
const hasAttribution = attributionLink && attributionText;

const clickTrackerHandler = useClickTrackerHandler(eventTrackingData);
Expand Down
29 changes: 19 additions & 10 deletions src/app/components/TopicTags/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { use } from 'react';
import getTopicPageUrl from '#app/utilities/getTopicPageUrl';
import { ServiceContext } from '#app/contexts/ServiceContext';
import { RequestContext } from '#app/contexts/RequestContext';
import { TopicTag } from '#app/models/types/metadata';
Expand All @@ -23,14 +24,6 @@ export const TopicTags = ({
const { service, translations } = use(ServiceContext);
const { variant } = use(RequestContext);

const getTopicPageUrl = (id: string) => {
const isPublicService = ['news', 'cymrufyw', 'naidheachdan'];
const hostname = `https://www.bbc.${isPublicService.includes(service) ? 'co.uk' : 'com'}`;
const topicsPath = translations?.topicsPath ?? 'topics';

return `${hostname}/${service}/${topicsPath}/${id}${variant ? `/${variant}` : ''}`;
};

const hasMultiple = tags.length > 1;

return hasMultiple ? (
Expand All @@ -42,7 +35,15 @@ export const TopicTags = ({
>
{tags.map(tag => (
<li key={tag.topicId} css={styles.topicTagItem}>
<a href={getTopicPageUrl(tag.topicId)} {...clickTrackerHandler}>
<a
href={getTopicPageUrl(
tag.topicId,
service,
variant,
translations?.topicsPath ?? 'topics',
)}
{...clickTrackerHandler}
>
{tag.topicName}
</a>
</li>
Expand All @@ -55,7 +56,15 @@ export const TopicTags = ({
{...viewTracker}
>
<div css={styles.topicTagItem}>
<a href={getTopicPageUrl(tags[0].topicId)} {...clickTrackerHandler}>
<a
href={getTopicPageUrl(
tags[0].topicId,
service,
variant,
translations?.topicsPath ?? 'topics',
)}
{...clickTrackerHandler}
>
{tags[0].topicName}
</a>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/app/models/types/curationData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { RadioScheduleData } from '#app/models/types/radioSchedule';
import { EventTrackingData } from '#app/lib/analyticsUtils/types';
import { MostReadData } from '../../components/MostRead/types';
import { TopicTag } from './metadata';

// This maps to the Summary type definition from the BFF
interface BaseSummary {
Expand Down Expand Up @@ -33,6 +34,7 @@ export interface Summary extends BaseSummary {
timeOfDayExperimentName?: string | null;
timeOfDayVariant?: string | null;
isPortraitImage?: boolean;
primaryTopic?: TopicTag;
}

export const VISUAL_STYLE = {
Expand Down
22 changes: 22 additions & 0 deletions src/app/utilities/getTopicPageUrl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Variants } from '#app/models/types/global';

/**
* Returns the canonical topic page URL for a given topic ID, service, and variant.
* @param topicId - The topic ID (e.g. 'c404v061z85t')
* @param service - The BBC service (e.g. 'pidgin')
* @param variant - Optional variant (e.g. 'simp')
* @param topicsPath - The path segment for topics (e.g. 'topics', 'pynciau')
* @returns The full topic page URL
*/
export const getTopicPageUrl = (
topicId: string,
service: string,
variant?: Variants | null,
topicsPath = 'topics',
) => {
const isPublicService = ['news', 'cymrufyw', 'naidheachdan'];
const hostname = `https://www.bbc.${isPublicService.includes(service) ? 'co.uk' : 'com'}`;
return `${hostname}/${service}/${topicsPath}/${topicId}${variant ? `/${variant}` : ''}`;
};

export default getTopicPageUrl;
Loading