diff --git a/dotcom-rendering/src/components/ProductCarouselCard.stories.tsx b/dotcom-rendering/src/components/ProductCarouselCard.stories.tsx index 65bb4d2b842..2e991c69a2d 100644 --- a/dotcom-rendering/src/components/ProductCarouselCard.stories.tsx +++ b/dotcom-rendering/src/components/ProductCarouselCard.stories.tsx @@ -2,13 +2,15 @@ import { css } from '@emotion/react'; import type { Meta, StoryObj } from '@storybook/react-webpack5'; import { productImage } from '../../fixtures/manual/productImage'; import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; +import { extractHeadingText } from '../model/enhanceProductElement'; import type { ProductBlockElement } from '../types/content'; import { ProductCarouselCard } from './ProductCarouselCard'; const product = { _type: 'model.dotcomrendering.pageElements.ProductBlockElement', elementId: 'b1f6e8e2-3f3a-4f0c-8d1e-5f3e3e3e3e3e', - primaryHeadingHtml: 'Best Kettle overall', + primaryHeadingHtml: 'Best Kettle overall:', + primaryHeadingText: extractHeadingText('Best Kettle overall:'), secondaryHeadingHtml: 'Bosch Sky Kettle', brandName: 'Bosch', productName: 'Sky Kettle', @@ -277,7 +279,9 @@ export const WithLongHeadingProductNameAndCTA = { args: { product: { ...product, - primaryHeadingHtml: 'Super long product category review name', + primaryHeadingText: extractHeadingText( + 'Super long product: category review name:', + ), productName: 'Sky Kettle with a super duper long name that goes on and on', productCtas: [ diff --git a/dotcom-rendering/src/components/ProductCarouselCard.tsx b/dotcom-rendering/src/components/ProductCarouselCard.tsx index f8235c386a0..d9d1d3cbf6b 100644 --- a/dotcom-rendering/src/components/ProductCarouselCard.tsx +++ b/dotcom-rendering/src/components/ProductCarouselCard.tsx @@ -84,12 +84,9 @@ export const ProductCarouselCard = ({
{hasHeading && ( <> -
+
+ {product.primaryHeadingText} +
{product.brandName}{' '} diff --git a/dotcom-rendering/src/frontend/schemas/feArticle.json b/dotcom-rendering/src/frontend/schemas/feArticle.json index 4ba32f48d95..f502c6bfec7 100644 --- a/dotcom-rendering/src/frontend/schemas/feArticle.json +++ b/dotcom-rendering/src/frontend/schemas/feArticle.json @@ -4480,6 +4480,9 @@ "primaryHeadingHtml": { "type": "string" }, + "primaryHeadingText": { + "type": "string" + }, "customAttributes": { "type": "array", "items": { diff --git a/dotcom-rendering/src/model/block-schema.json b/dotcom-rendering/src/model/block-schema.json index 3911d9b1c47..6d4fcc858b1 100644 --- a/dotcom-rendering/src/model/block-schema.json +++ b/dotcom-rendering/src/model/block-schema.json @@ -3968,6 +3968,9 @@ "primaryHeadingHtml": { "type": "string" }, + "primaryHeadingText": { + "type": "string" + }, "customAttributes": { "type": "array", "items": { diff --git a/dotcom-rendering/src/model/enhanceProductElement.test.ts b/dotcom-rendering/src/model/enhanceProductElement.test.ts index 3eca867b47d..0d6a8250dd5 100644 --- a/dotcom-rendering/src/model/enhanceProductElement.test.ts +++ b/dotcom-rendering/src/model/enhanceProductElement.test.ts @@ -1,4 +1,3 @@ -// typescript import type { ArticleFormat } from '../lib/articleFormat'; import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import type { @@ -26,8 +25,8 @@ const productBlockElement: ProductBlockElement = { starRating: '5', productName: 'Sky Kettle', image: productImage, - secondaryHeadingHtml: 'Best Kettle Overall', - primaryHeadingHtml: 'Bosch Sky Kettle', + primaryHeadingHtml: 'Best Kettle: Overall:', + secondaryHeadingHtml: 'Bosch Sky Kettle', customAttributes: [ { name: 'What we love', value: 'It packs away pretty small' }, { @@ -167,4 +166,11 @@ describe('enhanceProductBlockElements', () => { expect(enhancedElementWithNaN.lowestPrice).toEqual('£29.99'); }); + + it('extracts all html from primary heading and removes trailing `:` only', () => { + enhanceProductElement(elementsEnhancer)(inputElements); + expect(enhancedElements.primaryHeadingText).toEqual( + 'Best Kettle: Overall', + ); + }); }); diff --git a/dotcom-rendering/src/model/enhanceProductElement.ts b/dotcom-rendering/src/model/enhanceProductElement.ts index 9b46a9dc0e9..ec156e3fe3d 100644 --- a/dotcom-rendering/src/model/enhanceProductElement.ts +++ b/dotcom-rendering/src/model/enhanceProductElement.ts @@ -1,3 +1,4 @@ +import { JSDOM } from 'jsdom'; import type { FEElement, ProductBlockElement, @@ -13,6 +14,7 @@ const enhanceProductBlockElement = ( ...element, content: elementsEnhancer(element.content), lowestPrice: getLowestPrice(element.productCtas), + primaryHeadingText: extractHeadingText(element.primaryHeadingHtml), }); /** @@ -72,3 +74,15 @@ export const enhanceProductElement = (elementsEnhancer: ElementsEnhancer) => (elements: FEElement[]): FEElement[] => elements.flatMap(enhance(elementsEnhancer)); + +export const extractHeadingText = (headingHtml: string): string => { + return removeTrailingColon(extractText(headingHtml)); +}; + +const removeTrailingColon = (text: string): string => { + return text.replace(/\s*:\s*$/, ''); +}; + +const extractText = (html: string): string => { + return JSDOM.fragment(html).textContent?.trim() ?? ''; +}; diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index 51f2fc7cf2f..b96ab519e33 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -480,6 +480,7 @@ export interface ProductBlockElement { image?: ProductImage; secondaryHeadingHtml: string; primaryHeadingHtml: string; + primaryHeadingText?: string; customAttributes: ProductCustomAttribute[]; content: FEElement[]; h2Id?: string;