diff --git a/dotcom-rendering/src/components/ProductElement.stories.tsx b/dotcom-rendering/src/components/ProductElement.stories.tsx index d99d4acdd77..aa27e21482c 100644 --- a/dotcom-rendering/src/components/ProductElement.stories.tsx +++ b/dotcom-rendering/src/components/ProductElement.stories.tsx @@ -4,6 +4,7 @@ import { allModes } from '../../.storybook/modes'; import { productImage } from '../../fixtures/manual/productImage'; import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import { getNestedArticleElement } from '../lib/renderElement'; +import { extractHeadingText } from '../model/enhanceProductElement'; import type { ProductBlockElement } from '../types/content'; import { ProductElement } from './ProductElement'; @@ -23,7 +24,9 @@ const product = { _type: 'model.dotcomrendering.pageElements.ProductBlockElement', elementId: 'b1f6e8e2-3f3a-4f0c-8d1e-5f3e3e3e3e3e', primaryHeadingHtml: 'Best Kettle overall', + primaryHeadingText: extractHeadingText('Best Kettle overall'), secondaryHeadingHtml: 'Bosch Sky Kettle', + secondaryHeadingText: extractHeadingText('Bosch Sky Kettle'), brandName: 'Bosch', productName: 'Sky Kettle', image: productImage, @@ -296,7 +299,9 @@ export const WithoutHeading = { product: { ...product, primaryHeadingHtml: '', + primaryHeadingText: '', secondaryHeadingHtml: '', + secondaryHeadingText: '', }, }, } satisfies Story; @@ -316,6 +321,7 @@ export const NoSecondaryHeading = { ...product, primaryHeadingHtml: 'Primary heading only', secondaryHeadingHtml: '', + secondaryHeadingText: '', }, }, } satisfies Story; @@ -325,6 +331,7 @@ export const NoPrimaryHeading = { product: { ...product, primaryHeadingHtml: '', + primaryHeadingText: '', secondaryHeadingHtml: 'Secondary heading only', }, }, diff --git a/dotcom-rendering/src/components/ProductElement.tsx b/dotcom-rendering/src/components/ProductElement.tsx index 7a465580254..fa4f901589b 100644 --- a/dotcom-rendering/src/components/ProductElement.tsx +++ b/dotcom-rendering/src/components/ProductElement.tsx @@ -2,12 +2,11 @@ import { css } from '@emotion/react'; import { from } from '@guardian/source/foundations'; import type { ReactNode } from 'react'; import type { ArticleFormat } from '../lib/articleFormat'; -import { parseHtml } from '../lib/domUtils'; import type { NestedArticleElement } from '../lib/renderElement'; import type { ProductBlockElement } from '../types/content'; import { ProductCardInline } from './ProductCardInline'; import { ProductCardLeftCol } from './ProductCardLeftCol'; -import { buildElementTree } from './SubheadingBlockComponent'; +import { Subheading } from './Subheading'; const contentContainer = css` position: relative; @@ -86,23 +85,10 @@ const Content = ({ const showLeftCol = product.displayType === 'InlineWithProductCard' && shouldShowLeftColCard; - const subheadingHtml = parseHtml( - `

${ - product.primaryHeadingHtml - ? `${product.primaryHeadingHtml}
` - : '' - } ${product.secondaryHeadingHtml || ''}

`, - ); - const isSubheading = subheadingHtml.textContent - ? subheadingHtml.textContent.trim().length > 0 - : false; return (
- {isSubheading && - Array.from(subheadingHtml.childNodes).map( - buildElementTree(format), - )} +
{showLeftCol && ( @@ -131,3 +117,32 @@ const Content = ({
); }; + +const ProductSubheading = ({ + product, + format, +}: { + product: ProductBlockElement; + format: ArticleFormat; +}) => { + const subheadingHtml = `${ + product.primaryHeadingHtml ? `${product.primaryHeadingHtml}
` : '' + } ${product.secondaryHeadingHtml || ''}`; + + const isSubheading = + !!product.primaryHeadingText || !!product.secondaryHeadingText; + + if (!isSubheading) { + return null; + } + + return ( + + + + ); +}; diff --git a/dotcom-rendering/src/frontend/schemas/feArticle.json b/dotcom-rendering/src/frontend/schemas/feArticle.json index f502c6bfec7..dafb4428d58 100644 --- a/dotcom-rendering/src/frontend/schemas/feArticle.json +++ b/dotcom-rendering/src/frontend/schemas/feArticle.json @@ -4477,6 +4477,9 @@ "secondaryHeadingHtml": { "type": "string" }, + "secondaryHeadingText": { + "type": "string" + }, "primaryHeadingHtml": { "type": "string" }, diff --git a/dotcom-rendering/src/model/block-schema.json b/dotcom-rendering/src/model/block-schema.json index 6d4fcc858b1..629c256f7ef 100644 --- a/dotcom-rendering/src/model/block-schema.json +++ b/dotcom-rendering/src/model/block-schema.json @@ -3965,6 +3965,9 @@ "secondaryHeadingHtml": { "type": "string" }, + "secondaryHeadingText": { + "type": "string" + }, "primaryHeadingHtml": { "type": "string" }, diff --git a/dotcom-rendering/src/model/enhanceProductElement.ts b/dotcom-rendering/src/model/enhanceProductElement.ts index ec156e3fe3d..1d0f8f91c29 100644 --- a/dotcom-rendering/src/model/enhanceProductElement.ts +++ b/dotcom-rendering/src/model/enhanceProductElement.ts @@ -15,6 +15,7 @@ const enhanceProductBlockElement = ( content: elementsEnhancer(element.content), lowestPrice: getLowestPrice(element.productCtas), primaryHeadingText: extractHeadingText(element.primaryHeadingHtml), + secondaryHeadingText: extractHeadingText(element.secondaryHeadingHtml), }); /** diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index b96ab519e33..89b03c967ff 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -479,6 +479,7 @@ export interface ProductBlockElement { productName: string; image?: ProductImage; secondaryHeadingHtml: string; + secondaryHeadingText?: string; primaryHeadingHtml: string; primaryHeadingText?: string; customAttributes: ProductCustomAttribute[];