Skip to content
Draft
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
6 changes: 3 additions & 3 deletions ab-testing/config/abTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,16 @@ const ABTests: ABTest[] = [
shouldForceMetricsCollection: false,
},
{
name: "thefilter-at-a-glance-redesign",
name: "thefilter-at-a-glance-redesign-v2",
description:
"Testing redesigned at a glance component on The Filter articles",
owners: ["thefilter.dev@guardian.co.uk"],
expirationDate: "2026-04-01",
expirationDate: "2026-05-06",
type: "server",
status: "ON",
audienceSize: 0 / 100,
audienceSpace: "C",
groups: ["control", "stacked", "carousel"],
groups: ["control", "carousel", "stacked-default", "stacked-expanded"],
shouldForceMetricsCollection: false,
},
{
Expand Down
14 changes: 14 additions & 0 deletions dotcom-rendering/src/components/ProductSummary.island.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,26 @@ export const ProductSummary = ({
);
}

if (variant === 'stacked-default') {
return (
<Island priority="feature" defer={{ until: 'idle' }}>
<StackedProducts
products={products}
heading={'At a glance'}
format={format}
showAllProducts={false}
/>
</Island>
);
}

return (
<Island priority="feature" defer={{ until: 'idle' }}>
<StackedProducts
products={products}
heading={'At a glance'}
format={format}
showAllProducts={true}
/>
</Island>
);
Expand Down
10 changes: 7 additions & 3 deletions dotcom-rendering/src/components/StackedProducts.island.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ export const StackedProducts = ({
products,
heading,
format,
showAllProducts,
}: {
products: ProductBlockElement[];
heading: string;
format: ArticleFormat;
showAllProducts: boolean;
}) => {
const [isExpanded, setIsExpanded] = useState(false);
return (
Expand All @@ -54,7 +56,7 @@ export const StackedProducts = ({
<Subheading format={format} topPadding={false}>
{heading}
</Subheading>
{products.length > cardsShownByDefault && (
{products.length > cardsShownByDefault && !showAllProducts && (
<p css={cardCounterStyles}>
{isExpanded
? products.length
Expand Down Expand Up @@ -82,7 +84,9 @@ export const StackedProducts = ({
data-component={`at-a-glance-stacked-card-${index + 1}`}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably change this data-component depending on whether we're showing the expanded or default version of the element

style={{
display:
!isExpanded && index >= cardsShownByDefault
!isExpanded &&
index >= cardsShownByDefault &&
!showAllProducts
? 'none'
: 'block',
}}
Expand All @@ -95,7 +99,7 @@ export const StackedProducts = ({
))}
</div>

{products.length > cardsShownByDefault && (
{products.length > cardsShownByDefault && !showAllProducts && (
<LinkButton
onClick={() => setIsExpanded(!isExpanded)}
cssOverrides={showAllButtonStyles}
Expand Down
2 changes: 2 additions & 0 deletions dotcom-rendering/src/components/StackedProducts.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const meta = {
display: ArticleDisplay.Standard,
theme: Pillar.Lifestyle,
},
showAllProducts: false,
},
decorators: [centreColumnDecorator],
} satisfies Meta<typeof StackedProducts>;
Expand All @@ -34,5 +35,6 @@ export const FourProducts = {
display: ArticleDisplay.Standard,
theme: Pillar.Lifestyle,
},
showAllProducts: false,
},
} satisfies Story;
3 changes: 2 additions & 1 deletion dotcom-rendering/src/frontend/schemas/feArticle.json
Original file line number Diff line number Diff line change
Expand Up @@ -4774,7 +4774,8 @@
"variant": {
"enum": [
"carousel",
"stacked"
"stacked-default",
"stacked-expanded"
],
"type": "string"
}
Expand Down
3 changes: 2 additions & 1 deletion dotcom-rendering/src/model/block-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4256,7 +4256,8 @@
"variant": {
"enum": [
"carousel",
"stacked"
"stacked-default",
"stacked-expanded"
],
"type": "string"
}
Expand Down
14 changes: 12 additions & 2 deletions dotcom-rendering/src/model/enhance-product-summary.test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,22 @@ export const findCarousel = (
el.variant === 'carousel',
);

export const findStacked = (
export const findStackedDefault = (
elements: FEElement[],
): ProductSummaryElement | undefined =>
elements.find(
(el): el is ProductSummaryElement =>
el._type ===
'model.dotcomrendering.pageElements.ProductSummaryElement' &&
el.variant === 'stacked',
el.variant === 'stacked-default',
);

export const findStackedExpanded = (
elements: FEElement[],
): ProductSummaryElement | undefined =>
elements.find(
(el): el is ProductSummaryElement =>
el._type ===
'model.dotcomrendering.pageElements.ProductSummaryElement' &&
el.variant === 'stacked-expanded',
);
77 changes: 65 additions & 12 deletions dotcom-rendering/src/model/enhance-product-summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
atAGlanceHeading,
dividerElement,
findCarousel,
findStacked,
findStackedDefault,
findStackedExpanded,
linkElement,
productElement,
textElement,
Expand Down Expand Up @@ -222,7 +223,9 @@ describe('enhanceProductSummary', () => {

const output = enhanceProductSummary({
pageId: allowedPageId,
serverSideABTests: { 'thefilter-at-a-glance-redesign': 'carousel' },
serverSideABTests: {
'thefilter-at-a-glance-redesign-v2': 'carousel',
},
renderingTarget: 'Web',
filterAtAGlanceEnabled: true,
})(input);
Expand All @@ -232,7 +235,7 @@ describe('enhanceProductSummary', () => {
expect(carousel).toBeDefined();
});

it('enhances elements with a stacked product for allowlisted pages', () => {
it('enhances elements with a default stacked product component for allowlisted pages', () => {
const allowedPageId =
'thefilter/test-article-example-for-product-summary';

Expand Down Expand Up @@ -264,14 +267,60 @@ describe('enhanceProductSummary', () => {

const output = enhanceProductSummary({
pageId: allowedPageId,
serverSideABTests: { 'thefilter-at-a-glance-redesign': 'stacked' },
serverSideABTests: {
'thefilter-at-a-glance-redesign-v2': 'stacked-default',
},
renderingTarget: 'Web',
filterAtAGlanceEnabled: true,
})(input);

const stacked = findStacked(output);
const stackedDefault = findStackedDefault(output);

expect(stacked).toBeDefined();
expect(stackedDefault).toBeDefined();
});

it('enhances elements with an expanded stacked product component for allowlisted pages', () => {
const allowedPageId =
'thefilter/test-article-example-for-product-summary';

const input = [
atAGlanceHeading(),
linkElement(
'https://www.homebase.co.uk/en-uk/tower-airx-t17166-5l-grey-single-basket-air-fryer-digital-air-fryer/p/0757395',
'Buy now',
),
linkElement(
'https://www.lakeland.co.uk/27537/lakeland-slimline-air-fryer-black-8l',
'Buy now',
),
linkElement(
'https://ninjakitchen.co.uk/product/ninja-double-stack-xl-9-5l-air-fryer-sl400uk-zidSL400UK',
'Buy now',
),
dividerElement(),
productElement([
'https://www.homebase.co.uk/en-uk/tower-airx-t17166-5l-grey-single-basket-air-fryer-digital-air-fryer/p/0757395',
]),
productElement([
'https://www.lakeland.co.uk/27537/lakeland-slimline-air-fryer-black-8l',
]),
productElement([
'https://ninjakitchen.co.uk/product/ninja-double-stack-xl-9-5l-air-fryer-sl400uk-zidSL400UK',
]),
];

const output = enhanceProductSummary({
pageId: allowedPageId,
serverSideABTests: {
'thefilter-at-a-glance-redesign-v2': 'stacked-expanded',
},
renderingTarget: 'Web',
filterAtAGlanceEnabled: true,
})(input);

const stackedExpanded = findStackedExpanded(output);

expect(stackedExpanded).toBeDefined();
});

it('does not return stacked cards when the rendering target is apps', () => {
Expand Down Expand Up @@ -306,14 +355,16 @@ describe('enhanceProductSummary', () => {

const output = enhanceProductSummary({
pageId: allowedPageId,
serverSideABTests: { 'thefilter-at-a-glance-redesign': 'stacked' },
serverSideABTests: {
'thefilter-at-a-glance-redesign-v2': 'stacked-default',
},
renderingTarget: 'Apps',
filterAtAGlanceEnabled: true,
})(input);

const stacked = findStacked(output);
const stackedDefault = findStackedDefault(output);

expect(stacked).toBeUndefined();
expect(stackedDefault).toBeUndefined();
});

it('does not return stacked cards when the filterAtAGlance switch is OFF', () => {
Expand Down Expand Up @@ -348,13 +399,15 @@ describe('enhanceProductSummary', () => {

const output = enhanceProductSummary({
pageId: allowedPageId,
serverSideABTests: { 'thefilter-at-a-glance-redesign': 'stacked' },
serverSideABTests: {
'thefilter-at-a-glance-redesign-v2': 'stacked-default',
},
renderingTarget: 'Web',
filterAtAGlanceEnabled: false,
})(input);

const stacked = findStacked(output);
const stackedDefault = findStackedDefault(output);

expect(stacked).toBeUndefined();
expect(stackedDefault).toBeUndefined();
});
});
10 changes: 7 additions & 3 deletions dotcom-rendering/src/model/enhance-product-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { generateId } from './enhance-H2s';
* further up the rendering pipeline.
*/

export type ABTestVariant = 'carousel' | 'stacked';
export type ABTestVariant = 'carousel' | 'stacked-default' | 'stacked-expanded';

/**
* List of page IDs eligible for product carousel enhancement.
Expand Down Expand Up @@ -60,7 +60,11 @@ const isEligibleForSummary = (pageId: string) => {
};

const isCarouselOrStacked = (string: string) => {
return string === 'carousel' || string === 'stacked';
return (
string === 'carousel' ||
string === 'stacked-default' ||
string === 'stacked-expanded'
);
};

// Extract URLs from 'At a glance' section elements
Expand Down Expand Up @@ -202,7 +206,7 @@ export const enhanceProductSummary =
}) =>
(elements: FEElement[]): FEElement[] => {
const abTestVariant =
serverSideABTests?.['thefilter-at-a-glance-redesign'];
serverSideABTests?.['thefilter-at-a-glance-redesign-v2'];

// do nothing if article is not on allow list / not in the test / variant is 'control' / renderingTarget is Apps / filterAtAGlance switch is OFF
if (
Expand Down
2 changes: 1 addition & 1 deletion dotcom-rendering/src/types/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ export interface ProductBlockElement {
export interface ProductSummaryElement {
_type: 'model.dotcomrendering.pageElements.ProductSummaryElement';
matchedProducts: ProductBlockElement[];
variant: 'carousel' | 'stacked';
variant: 'carousel' | 'stacked-default' | 'stacked-expanded';
}

interface ProfileAtomBlockElement {
Expand Down
Loading