Skip to content

Commit b393942

Browse files
- Adds optional Read More which will anchor to the product element on the page when wired up
- Additional tweaks so the card is more aligned with the Figma designs - Added logic when there is no heading to put the product and brand name under the image on seperate lines - Added logic to handle when the product/brand name is longer than one line and needs to wrap - Using the ProductCardImage instead of Picture for consistency across these elements but happy to change it back again or discuss further - Added more storybooks
1 parent 7ff741c commit b393942

File tree

2 files changed

+84
-19
lines changed

2 files changed

+84
-19
lines changed

dotcom-rendering/src/components/ProductCarouselCard.stories.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ const meta = {
254254
display: ArticleDisplay.Standard,
255255
theme: Pillar.Lifestyle,
256256
},
257+
showReadMore: true,
257258
},
258259
} satisfies Meta<typeof ProductCarouselCard>;
259260

@@ -262,3 +263,38 @@ export default meta;
262263
type Story = StoryObj<typeof meta>;
263264

264265
export const Default = {} satisfies Story;
266+
267+
export const WithoutReadMore = {
268+
args: {
269+
showReadMore: false,
270+
},
271+
} satisfies Story;
272+
273+
export const WithoutHeadingDisclaimerOrReadMore = {
274+
args: {
275+
product: {
276+
...product,
277+
primaryHeadingHtml: '',
278+
},
279+
showReadMore: false,
280+
},
281+
} satisfies Story;
282+
283+
export const WithLongHeadingProductNameAndCTA = {
284+
args: {
285+
product: {
286+
...product,
287+
primaryHeadingHtml: 'Super long product category review name',
288+
productName:
289+
'Sky Kettle with a super duper long name that goes on and on',
290+
productCtas: [
291+
{
292+
url: 'https://www.johnlewis.com/bosch-twk7203gb-sky-variable-temperature-kettle-1-7l-black/p3228625',
293+
text: '',
294+
retailer: 'John Lewis with a very long name',
295+
price: '£45.99',
296+
},
297+
],
298+
},
299+
},
300+
};

dotcom-rendering/src/components/ProductCarouselCard.tsx

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,27 @@ import {
44
headlineMedium17,
55
lifestyle,
66
space,
7+
textSans17,
78
textSansBold15,
89
textSansBold17,
910
} from '@guardian/source/foundations';
1011
import { ProductCardButtons } from './ProductCardButtons';
1112
import { ProductBlockElement } from '../types/content';
12-
import { Picture } from './Picture';
1313
import { ArticleFormat } from '../lib/articleFormat';
14+
import { ProductCardImage } from './ProductCardImage';
1415

1516
export type ProductCarouselCardProps = {
1617
product: ProductBlockElement;
1718
format: ArticleFormat;
19+
showReadMore?: boolean;
1820
};
1921

2022
const baseCard = css`
2123
display: flex;
2224
width: 280px;
2325
flex-direction: column;
2426
align-items: flex-start;
25-
padding: 12px 10px 16px 10px;
27+
padding: ${space[3]}px 10px ${space[4]}px 10px;
2628
`;
2729

2830
const productCarouselCardHeading = css`
@@ -39,8 +41,8 @@ const readMoreCta = css`
3941
text-decoration-line: underline;
4042
text-decoration-color: #dcdcdc; //TODO update this to use var(--Link-Pillar-color-link-underline-pillar, #DCDCDC);
4143
color: ${lifestyle[400]};
44+
text-underline-offset: 20%;
4245
padding-bottom: ${space[2]}px;
43-
text-underline-offset: 20%; /* 3px */
4446
`;
4547

4648
const priceStyle = css`
@@ -70,34 +72,61 @@ const imageArea = css`
7072
img {
7173
width: 100%;
7274
height: 280px;
73-
object-fit: contain;
75+
object-fit: cover;
7476
}
7577
`;
7678

79+
const brandAndProductNameRow = css`
80+
display: block;
81+
line-height: 1.3;
82+
`;
83+
84+
const brandAndProductNameInline = css`
85+
${headlineMedium17};
86+
display: inline;
87+
`;
88+
89+
const productNameStyle = css`
90+
${textSans17};
91+
`;
92+
7793
export const ProductCarouselCard = ({
7894
product,
7995
format,
96+
showReadMore,
8097
}: ProductCarouselCardProps) => {
98+
const hasHeading = !!product.primaryHeadingHtml;
8199
return (
82100
<div css={baseCard}>
83-
<div css={productCarouselCardHeading}>
84-
{product.primaryHeadingHtml}
85-
</div>
86-
<div css={brandAndProductName}>
87-
{product.brandName} {product.productName}
88-
</div>
89-
<div css={readMoreCta}>Read more</div>
101+
{hasHeading && (
102+
<>
103+
<div css={productCarouselCardHeading}>
104+
{product.primaryHeadingHtml}
105+
</div>
106+
<div css={brandAndProductNameRow}>
107+
<span css={brandAndProductNameInline}>
108+
{product.brandName}{' '}
109+
</span>
110+
<span css={brandAndProductNameInline}>
111+
{product.productName}
112+
</span>
113+
</div>
114+
</>
115+
)}
116+
{showReadMore && <div css={readMoreCta}>Read more</div>}
90117
<div css={imageArea}>
91-
<Picture
92-
role="productCard"
93-
master={product.image?.url ?? ''}
94-
alt={product.image?.alt ?? ''}
95-
width={product.image?.width ?? 0}
96-
height={product.image?.height ?? 0}
97-
loading="eager"
118+
<ProductCardImage
98119
format={format}
120+
image={product.image}
121+
url={undefined}
99122
/>
100123
</div>
124+
{!hasHeading && (
125+
<div>
126+
<div css={brandAndProductName}>{product.brandName}</div>
127+
<div css={productNameStyle}>{product.productName}</div>
128+
</div>
129+
)}
101130
<div css={priceStyle}>
102131
{product.productCtas && product.productCtas.length > 0
103132
? product.productCtas[0]?.price ?? 'Price unavailable'
@@ -112,7 +141,7 @@ export const ProductCarouselCard = ({
112141
product.productCtas[0]?.retailer ??
113142
'retailer'
114143
}`
115-
: 'Buy'
144+
: 'Buy' //TODO better fallback?
116145
}
117146
/>
118147
</div>

0 commit comments

Comments
 (0)