Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<em>Best Kettle overall:<em/>',
primaryHeadingText: extractHeadingText('<em>Best Kettle overall:</em>'),
secondaryHeadingHtml: 'Bosch Sky Kettle',
brandName: 'Bosch',
productName: 'Sky Kettle',
Expand Down Expand Up @@ -277,7 +279,9 @@ export const WithLongHeadingProductNameAndCTA = {
args: {
product: {
...product,
primaryHeadingHtml: 'Super long product category review name',
primaryHeadingText: extractHeadingText(
'<em>Super long product: category review name:</em>',
),
productName:
'Sky Kettle with a super duper long name that goes on and on',
productCtas: [
Expand Down
9 changes: 3 additions & 6 deletions dotcom-rendering/src/components/ProductCarouselCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,9 @@ export const ProductCarouselCard = ({
<div css={baseCard}>
{hasHeading && (
<>
<div
css={productCarouselCardHeading}
dangerouslySetInnerHTML={{
__html: product.primaryHeadingHtml,
}}
/>
<div css={productCarouselCardHeading}>
{product.primaryHeadingText}
</div>
<div css={brandAndProductNameRow}>
<span css={brandAndProductNameInline}>
{product.brandName}{' '}
Expand Down
3 changes: 3 additions & 0 deletions dotcom-rendering/src/frontend/schemas/feArticle.json
Original file line number Diff line number Diff line change
Expand Up @@ -4480,6 +4480,9 @@
"primaryHeadingHtml": {
"type": "string"
},
"primaryHeadingText": {
"type": "string"
},
"customAttributes": {
"type": "array",
"items": {
Expand Down
3 changes: 3 additions & 0 deletions dotcom-rendering/src/model/block-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3968,6 +3968,9 @@
"primaryHeadingHtml": {
"type": "string"
},
"primaryHeadingText": {
"type": "string"
},
"customAttributes": {
"type": "array",
"items": {
Expand Down
12 changes: 9 additions & 3 deletions dotcom-rendering/src/model/enhanceProductElement.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// typescript
import type { ArticleFormat } from '../lib/articleFormat';
import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat';
import type {
Expand Down Expand Up @@ -26,8 +25,8 @@ const productBlockElement: ProductBlockElement = {
starRating: '5',
productName: 'Sky Kettle',
image: productImage,
secondaryHeadingHtml: 'Best Kettle Overall',
primaryHeadingHtml: 'Bosch Sky Kettle',
primaryHeadingHtml: '<em>Best Kettle: Overall:</em>',
secondaryHeadingHtml: 'Bosch Sky Kettle',
customAttributes: [
{ name: 'What we love', value: 'It packs away pretty small' },
{
Expand Down Expand Up @@ -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',
);
});
});
14 changes: 14 additions & 0 deletions dotcom-rendering/src/model/enhanceProductElement.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { JSDOM } from 'jsdom';
import type {
FEElement,
ProductBlockElement,
Expand All @@ -13,6 +14,7 @@ const enhanceProductBlockElement = (
...element,
content: elementsEnhancer(element.content),
lowestPrice: getLowestPrice(element.productCtas),
primaryHeadingText: extractHeadingText(element.primaryHeadingHtml),
});

/**
Expand Down Expand Up @@ -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() ?? '';
};
1 change: 1 addition & 0 deletions dotcom-rendering/src/types/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ export interface ProductBlockElement {
image?: ProductImage;
secondaryHeadingHtml: string;
primaryHeadingHtml: string;
primaryHeadingText?: string;
customAttributes: ProductCustomAttribute[];
content: FEElement[];
h2Id?: string;
Expand Down
Loading