Skip to content

Commit 1f51155

Browse files
Add an interactive test to storybook. Add a section ID to the product carousel. and add a section for the navigation button portal to hydrate
1 parent d8ff3e6 commit 1f51155

File tree

4 files changed

+89
-22
lines changed

4 files changed

+89
-22
lines changed

dotcom-rendering/src/components/CarouselNavigationButtons.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type ThemeButton,
88
} from '@guardian/source/react-components';
99
import { useEffect, useState } from 'react';
10-
import ReactDOM from 'react-dom';
10+
import { createPortal } from 'react-dom';
1111
import { palette } from '../palette';
1212

1313
type CarouselNavigationProps = {
@@ -84,7 +84,7 @@ export const CarouselNavigationButtons = ({
8484

8585
if (!portalNode) return null;
8686

87-
return ReactDOM.createPortal(
87+
return createPortal(
8888
<div
8989
aria-controls="carousel"
9090
aria-label="carousel arrows"
@@ -101,6 +101,7 @@ export const CarouselNavigationButtons = ({
101101
}
102102
size="small"
103103
disabled={!previousButtonEnabled}
104+
data-testid="carousel-back-button"
104105
aria-label="previous"
105106
value="previous"
106107
data-link-name={dataLinkNamePreviousButton}
@@ -109,6 +110,7 @@ export const CarouselNavigationButtons = ({
109110
<Button
110111
hideLabel={true}
111112
iconSide="left"
113+
data-testid="carousel-forward-button"
112114
icon={<SvgChevronRightSingle />}
113115
onClick={onClickNextButton}
114116
priority="tertiary"

dotcom-rendering/src/components/ScrollableCarousel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ export const ScrollableCarousel = ({
312312
const showNavigation =
313313
kind === CarouselKind.VisibleSlides
314314
? carouselLength > visibleCarouselSlidesOnTablet
315-
: false;
315+
: true;
316316

317317
const scrollTo = (direction: 'left' | 'right') => {
318318
if (!carouselRef.current) return;
Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1+
import { css } from '@emotion/react';
2+
import { palette, space } from '@guardian/source/foundations';
13
import type { ArticleFormat } from '../lib/articleFormat';
24
import type { ProductBlockElement } from '../types/content';
35
import { ProductCarouselCard } from './ProductCarouselCard';
46
import type { FixedSlideWidth } from './ScrollableCarousel';
57
import { CarouselKind, ScrollableCarousel } from './ScrollableCarousel';
8+
import { Subheading } from './Subheading';
9+
10+
const carouselHeader = css`
11+
padding-bottom: 10px;
12+
padding-top: ${space[6]}px;
13+
border-bottom: 1px solid ${palette.neutral[86]};
14+
margin-bottom: 10px;
15+
display: flex;
16+
justify-content: space-between;
17+
`;
618

719
export const ScrollableProduct = ({
820
products,
@@ -19,21 +31,37 @@ export const ScrollableProduct = ({
1931
],
2032
};
2133
return (
22-
<ScrollableCarousel
23-
isArticle={true}
24-
kind={CarouselKind.FixedWidthSlides}
25-
carouselLength={products.length}
26-
fixedSlideWidth={fixedCardWidth}
27-
gapSizes={{ row: 'none', column: 'large' }}
28-
>
29-
{products.map((product: ProductBlockElement) => (
30-
<ScrollableCarousel.SubgridItem
31-
key={product.productCtas[0]?.url ?? product.elementId}
32-
subgridRows={4}
34+
<>
35+
<div css={carouselHeader}>
36+
<Subheading
37+
format={format}
38+
id={'at-a-glance'}
39+
topPadding={false}
3340
>
34-
<ProductCarouselCard product={product} format={format} />
35-
</ScrollableCarousel.SubgridItem>
36-
))}
37-
</ScrollableCarousel>
41+
At a glance
42+
</Subheading>
43+
<div id={'at-a-glance-carousel-navigation'}></div>
44+
</div>
45+
<ScrollableCarousel
46+
isArticle={true}
47+
kind={CarouselKind.FixedWidthSlides}
48+
carouselLength={products.length}
49+
fixedSlideWidth={fixedCardWidth}
50+
gapSizes={{ row: 'none', column: 'large' }}
51+
sectionId={'at-a-glance'}
52+
>
53+
{products.map((product: ProductBlockElement) => (
54+
<ScrollableCarousel.SubgridItem
55+
key={product.productCtas[0]?.url ?? product.elementId}
56+
subgridRows={4}
57+
>
58+
<ProductCarouselCard
59+
product={product}
60+
format={format}
61+
/>
62+
</ScrollableCarousel.SubgridItem>
63+
))}
64+
</ScrollableCarousel>
65+
</>
3866
);
3967
};

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

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,39 @@ const meta = {
1515
breakpoints.tablet,
1616
breakpoints.wide,
1717
],
18+
// Because this component uses react.createPortal
19+
// we delay to give the portals time to hydrate
20+
delay: 500,
1821
},
1922
},
2023
args: {
2124
products: [
22-
{ ...exampleProduct, h2Id: 'product' },
2325
{
2426
...exampleProduct,
27+
primaryHeadingHtml: '<em>Product 0</em>',
28+
h2Id: 'product',
29+
},
30+
{
31+
...exampleProduct,
32+
primaryHeadingHtml: '<em>Product 1</em>',
2533
h2Id: 'product-1',
2634
productName: 'Lorem ipsum dolor sit amet',
2735
},
28-
{ ...exampleProduct, h2Id: 'product-2' },
29-
{ ...exampleProduct, h2Id: 'product-3' },
30-
{ ...exampleProduct, h2Id: 'product-4' },
36+
{
37+
...exampleProduct,
38+
primaryHeadingHtml: '<em>Product 2</em>',
39+
h2Id: 'product-2',
40+
},
41+
{
42+
...exampleProduct,
43+
primaryHeadingHtml: '<em>Product 3</em>',
44+
h2Id: 'product-3',
45+
},
46+
{
47+
...exampleProduct,
48+
primaryHeadingHtml: '<em>Product 4</em>',
49+
h2Id: 'product-4',
50+
},
3151
],
3252
format: {
3353
design: ArticleDesign.Review,
@@ -78,3 +98,20 @@ export default meta;
7898
type Story = StoryObj<typeof meta>;
7999

80100
export const With5Cards = {} satisfies Story;
101+
102+
export const ClickForwardArrow = {
103+
play: async ({ canvas, userEvent }) => {
104+
await userEvent.click(canvas.getByTestId('carousel-forward-button'));
105+
},
106+
} satisfies Story;
107+
108+
export const ClickForwardTwiceAndBackOnce = {
109+
play: async ({ canvas, userEvent }) => {
110+
const user = userEvent.setup({
111+
delay: 500, // delay between each interaction
112+
});
113+
await user.click(canvas.getByTestId('carousel-forward-button'));
114+
await user.click(canvas.getByTestId('carousel-forward-button'));
115+
await user.click(canvas.getByTestId('carousel-back-button'));
116+
},
117+
} satisfies Story;

0 commit comments

Comments
 (0)