diff --git a/src/app/components/Curation/HighImpactPromo/README.md b/src/app/components/Curation/HighImpactPromo/README.md index d37bef10a27..8d529e1a8b2 100644 --- a/src/app/components/Curation/HighImpactPromo/README.md +++ b/src/app/components/Curation/HighImpactPromo/README.md @@ -18,22 +18,22 @@ Typically used when editors set a curation item's prominence to "Maximum" in Tip - Renders promotional content with enhanced visual styling. - Integrates with the existing Standard Grid flow. - Provides clear visual distinction from standard curation items. -- Attribution is automatically derived from the service context (e.g., brand name and service URL) but can be overridden with the `attribution` prop. +- Subject link is derived from `relatedTopic` when present, otherwise it falls back to service context (brand name and service URL). --- ## Props -| Prop | Type | Required | Description | -| ------------------- | ------- | -------- | --------------------------------------------------------------------- | -| `title` | string | Yes | The promotional headline. | -| `link` | string | Yes | URL destination for the promo. | -| `imageUrl` | string | Yes | Image URL for the promotional content. | -| `imageAlt` | string | Yes | Alt text for the promotional image. | -| `lazy` | boolean | No | Enables lazy loading for the image. | -| `headingLevel` | number | No | The heading level for the title (defaults to 3). | -| `eventTrackingData` | object | No | Tracking metadata for analytics. | -| `attribution` | object | No | An object with `text` and `link` to override the default attribution. | +| Prop | Type | Required | Description | +| ------------------- | ------- | -------- | ---------------------------------------------------------------- | +| `title` | string | Yes | The promotional headline. | +| `link` | string | Yes | URL destination for the promo. | +| `imageUrl` | string | Yes | Image URL for the promotional content. | +| `imageAlt` | string | Yes | Alt text for the promotional image. | +| `lazy` | boolean | No | Enables lazy loading for the image. | +| `headingLevel` | number | No | The heading level for the title (defaults to 3). | +| `eventTrackingData` | object | No | Tracking metadata for analytics. | +| `relatedTopic` | object | No | An object with `title` and `link.url` used for the subject link. | --- diff --git a/src/app/components/Curation/HighImpactPromo/index.stories.tsx b/src/app/components/Curation/HighImpactPromo/index.stories.tsx index 48fe2d3413e..af2f8dad613 100644 --- a/src/app/components/Curation/HighImpactPromo/index.stories.tsx +++ b/src/app/components/Curation/HighImpactPromo/index.stories.tsx @@ -5,56 +5,47 @@ import metadata from './metadata.json'; import readme from './README.md'; const highImpactFixtureCuration = fixture.data.curations[0] as BaseCuration; +const baseProps = highImpactFixtureCuration.summaries?.[0] as Summary; -const Component = () => { - return ( -
- - - -
- ); -}; +interface ExternalProps { + relatedTopic?: { title: string; link: { url: string } } | null; +} + +const Component = ({ relatedTopic }: ExternalProps) => ( + +); export default { title: 'Components/Curation/High Impact Promo', - component: Component, + Component, + decorators: [ + Story => ( +
+ +
+ ), + ], parameters: { metadata, docs: { readme }, chromatic: { disable: true }, }, + args: { + relatedTopic: { + title: 'Related Topic Example', + link: { url: '/topic/example' }, + }, + }, + argTypes: { + relatedTopic: { control: 'object' }, + }, }; -export const Example = {}; +export const Example = Component; diff --git a/src/app/components/Curation/HighImpactPromo/index.test.tsx b/src/app/components/Curation/HighImpactPromo/index.test.tsx index f96b2b0a79f..f22d0850900 100644 --- a/src/app/components/Curation/HighImpactPromo/index.test.tsx +++ b/src/app/components/Curation/HighImpactPromo/index.test.tsx @@ -12,18 +12,18 @@ const promoFixtureData = summaries?.[0] as HighImpactPromoProps; interface FixtureProps { promoData?: HighImpactPromoProps; headingLevel?: number; - attributions?: { title: string; link: { url: string } }[] | null; + relatedTopic?: { title: string; link: { url: string } } | null | undefined; } const Fixture = ({ promoData = promoFixtureData, headingLevel, - attributions, + relatedTopic, }: FixtureProps) => ( ); @@ -64,16 +64,16 @@ describe('High Impact Promo', () => { ); }); - it('should render default values if attribution prop is not provided', () => { + it('should render default subject values when relatedTopic prop is not provided', () => { render(, { service: 'mundo' }); - const attributionLink = screen.getByRole('link', { + const subjectLink = screen.getByRole('link', { name: 'BBC News Mundo', }); - expect(attributionLink).toBeInTheDocument(); - expect(attributionLink).toHaveAttribute('href', '/mundo'); + expect(subjectLink).toBeInTheDocument(); + expect(subjectLink).toHaveAttribute('href', '/mundo'); - const divider = attributionLink.previousElementSibling; + const divider = subjectLink.previousElementSibling; expect(divider).toBeInTheDocument(); expect(divider).toHaveStyle({ 'background-color': '#EB0000', @@ -82,39 +82,6 @@ describe('High Impact Promo', () => { }); }); - it('should render correct attribution when an attributions prop is provided', () => { - const customAttributions = [ - { - title: 'Pidgin Related Topic', - link: { url: '/pidgin/topics/234567' }, - }, - ]; - render(); - - const attributionLink = screen.getByRole('link', { - name: 'Pidgin Related Topic', - }); - expect(attributionLink).toBeInTheDocument(); - expect(attributionLink).toHaveAttribute('href', '/pidgin/topics/234567'); - }); - it('should render default attribution when attributions prop is null', () => { - render(, { service: 'mundo' }); - const attributionLink = screen.getByRole('link', { - name: 'BBC News Mundo', - }); - expect(attributionLink).toBeInTheDocument(); - expect(attributionLink).toHaveAttribute('href', '/mundo'); - }); - - it('should render default attribution when attributions prop is an empty array', () => { - render(, { service: 'mundo' }); - const attributionLink = screen.getByRole('link', { - name: 'BBC News Mundo', - }); - expect(attributionLink).toBeInTheDocument(); - expect(attributionLink).toHaveAttribute('href', '/mundo'); - }); - it.each<[Services, string]>([ ['mundo', 'ltr'], ['arabic', 'rtl'], @@ -123,4 +90,21 @@ describe('High Impact Promo', () => { const promo = screen.getByTestId('high-impact-promo'); expect(promo).toHaveAttribute('dir', dir); }); + + it('should render relatedTopic when provided', () => { + const relatedTopic = { + title: 'Россия', + link: { url: 'https://www.bbc.com/russian/topics/cw6eyw7m0m1t' }, + }; + render(); + + const relatedTopicLink = screen.getByRole('link', { + name: 'Россия', + }); + expect(relatedTopicLink).toBeInTheDocument(); + expect(relatedTopicLink).toHaveAttribute( + 'href', + 'https://www.bbc.com/russian/topics/cw6eyw7m0m1t', + ); + }); }); diff --git a/src/app/components/Curation/HighImpactPromo/index.tsx b/src/app/components/Curation/HighImpactPromo/index.tsx index c8d75ab8dcd..67b9d96b456 100644 --- a/src/app/components/Curation/HighImpactPromo/index.tsx +++ b/src/app/components/Curation/HighImpactPromo/index.tsx @@ -7,19 +7,19 @@ import { ServiceContext } from '#app/contexts/ServiceContext'; import { getBrandPath } from '#app/legacy/containers/Brand'; import styles from './index.styles'; -type AttributionLink = { +type RelatedTopicLink = { url: string; scheme?: string; host?: string; path?: string; }; -type Attribution = { +type RelatedTopic = { + link: RelatedTopicLink; title: string; - link: AttributionLink; }; export interface HighImpactPromoProps extends Summary { - attributions?: Attribution[] | null; + relatedTopic?: RelatedTopic | null; } const HighImpactPromo = ({ @@ -30,16 +30,15 @@ const HighImpactPromo = ({ link, headingLevel = 3, eventTrackingData, - attributions, + relatedTopic, }: HighImpactPromoProps) => { const { isAmp } = use(RequestContext); const { dir, service, brandName } = use(ServiceContext) || {}; - const [firstAttribution] = attributions || []; - const attributionLink = - firstAttribution?.link?.url || (service ? getBrandPath(service) : null); - const attributionText = firstAttribution?.title || brandName; - const hasAttribution = Boolean(attributionLink && attributionText); + const subjectLink = + relatedTopic?.link?.url || (service ? getBrandPath(service) : undefined); + const subjectText = relatedTopic?.title || brandName; + const hasSubject = Boolean(subjectLink && subjectText); const clickTrackerHandler = useClickTrackerHandler(eventTrackingData); @@ -65,14 +64,14 @@ const HighImpactPromo = ({ {title} - {hasAttribution &&
} - {hasAttribution && attributionLink && ( + {hasSubject &&
} + {hasSubject && ( - {attributionText} + {subjectText} )}