Skip to content

Commit a964e13

Browse files
authored
Merge pull request #14227 from guardian/jm/choice-cards-configurable-destination
feat: add destinationUrl handling in DesignableBanner
2 parents 78a6ede + b60bac5 commit a964e13

File tree

10 files changed

+323
-150
lines changed

10 files changed

+323
-150
lines changed

dotcom-rendering/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"@guardian/shimport": "1.0.2",
4444
"@guardian/source": "9.0.0",
4545
"@guardian/source-development-kitchen": "18.1.1",
46-
"@guardian/support-dotcom-components": "7.5.0",
46+
"@guardian/support-dotcom-components": "7.6.2",
4747
"@guardian/tsconfig": "0.2.0",
4848
"@playwright/test": "1.52.0",
4949
"@sentry/browser": "7.75.1",

dotcom-rendering/src/components/marketing/banners/designableBanner/DesignableBanner.tsx

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
} from '../../../../lib/useMatchMedia';
3131
import { getChoiceCards } from '../../lib/choiceCards';
3232
import type { ReactComponent } from '../../lib/ReactComponent';
33-
import { addChoiceCardsProductParams } from '../../lib/tracking';
33+
import { getChoiceCardUrl } from '../../lib/tracking';
3434
import { ThreeTierChoiceCards } from '../../shared/ThreeTierChoiceCards';
3535
import { bannerWrapper, validatedBannerWrapper } from '../common/BannerWrapper';
3636
import type { BannerRenderProps } from '../common/types';
@@ -108,19 +108,6 @@ const buildChoiceCardSettings = (
108108
return undefined;
109109
};
110110

111-
const buildUrlForThreeTierChoiceCards = (
112-
baseUrl: string,
113-
selectedProduct: ChoiceCard['product'],
114-
) => {
115-
return selectedProduct.supportTier === 'OneOff'
116-
? baseUrl
117-
: addChoiceCardsProductParams(
118-
baseUrl,
119-
selectedProduct.supportTier,
120-
selectedProduct.ratePlan,
121-
);
122-
};
123-
124111
const DesignableBanner: ReactComponent<BannerRenderProps> = ({
125112
content,
126113
onCloseClick,
@@ -160,11 +147,11 @@ const DesignableBanner: ReactComponent<BannerRenderProps> = ({
160147
}, [iosAppBannerPresent, submitComponentEvent]);
161148

162149
const choiceCards = getChoiceCards(isTabletOrAbove, choiceCardsSettings);
163-
const defaultProduct = choiceCards?.find((cc) => cc.isDefault)?.product;
164-
const [
165-
threeTierChoiceCardSelectedProduct,
166-
setThreeTierChoiceCardSelectedProduct,
167-
] = useState<ChoiceCard['product'] | undefined>(defaultProduct);
150+
const defaultChoiceCard = choiceCards?.find((cc) => cc.isDefault);
151+
152+
const [selectedChoiceCard, setSelectedChoiceCard] = useState<
153+
ChoiceCard | undefined
154+
>(defaultChoiceCard);
168155

169156
// We can't render anything without a design
170157
if (!design) {
@@ -379,7 +366,7 @@ const DesignableBanner: ReactComponent<BannerRenderProps> = ({
379366
</div>
380367
)}
381368

382-
{!threeTierChoiceCardSelectedProduct && (
369+
{!selectedChoiceCard && (
383370
<div css={styles.outerImageCtaContainer}>
384371
<div css={styles.innerImageCtaContainer}>
385372
<DesignableBannerCtas
@@ -405,25 +392,21 @@ const DesignableBanner: ReactComponent<BannerRenderProps> = ({
405392
</div>
406393

407394
{choiceCards &&
408-
threeTierChoiceCardSelectedProduct &&
395+
selectedChoiceCard &&
409396
mainOrMobileContent.primaryCta && (
410397
<div css={styles.threeTierChoiceCardsContainer}>
411398
<ThreeTierChoiceCards
412-
selectedProduct={
413-
threeTierChoiceCardSelectedProduct
414-
}
415-
setSelectedProduct={
416-
setThreeTierChoiceCardSelectedProduct
417-
}
399+
selectedChoiceCard={selectedChoiceCard}
400+
setSelectedChoiceCard={setSelectedChoiceCard}
418401
choices={choiceCards}
419402
id={'banner'}
420403
/>
421404

422405
<div css={styles.ctaContainer}>
423406
<LinkButton
424-
href={buildUrlForThreeTierChoiceCards(
407+
href={getChoiceCardUrl(
408+
selectedChoiceCard,
425409
mainOrMobileContent.primaryCta.ctaUrl,
426-
threeTierChoiceCardSelectedProduct,
427410
)}
428411
onClick={onCtaClick}
429412
priority="tertiary"

dotcom-rendering/src/components/marketing/banners/designableBanner/stories/DesignableBanner.stories.tsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import type {
55
} from '@guardian/support-dotcom-components/dist/shared/types';
66
import type { Meta, StoryObj } from '@storybook/react';
77
import lzstring from 'lz-string';
8-
import { choiceCardsSettings } from '../../../lib/storybook';
8+
import {
9+
choiceCardsSettings,
10+
choiceCardsWithDestinationUrl,
11+
choiceCardsWithDestinationUrlTwoCards,
12+
} from '../../../lib/storybook';
913
import {
1014
contentNoHeading,
1115
design,
@@ -281,3 +285,57 @@ export const NoChoiceCardOrImage: Story = {
281285
},
282286
},
283287
};
288+
289+
export const WithDestinationUrlAllCards: Story = {
290+
name: 'With destinationUrl on all choice cards',
291+
args: {
292+
...meta.args,
293+
design: {
294+
...design,
295+
visual: {
296+
kind: 'ChoiceCards',
297+
buttonColour: stringToHexColour('E5E5E5'),
298+
},
299+
},
300+
tracking: {
301+
...tracking,
302+
abTestVariant: 'THREE_TIER_CHOICE_CARDS',
303+
},
304+
choiceCardAmounts: regularChoiceCardAmounts,
305+
choiceCardsSettings: choiceCardsWithDestinationUrl,
306+
},
307+
parameters: {
308+
docs: {
309+
description: {
310+
story: 'All choice cards have a destinationUrl configured. The banner should use these custom URLs instead of constructing URLs with product parameters.',
311+
},
312+
},
313+
},
314+
};
315+
316+
export const WithDestinationUrlTwoCards: Story = {
317+
name: 'With destinationUrl in two choice cards',
318+
args: {
319+
...meta.args,
320+
design: {
321+
...design,
322+
visual: {
323+
kind: 'ChoiceCards',
324+
buttonColour: stringToHexColour('E5E5E5'),
325+
},
326+
},
327+
tracking: {
328+
...tracking,
329+
abTestVariant: 'THREE_TIER_CHOICE_CARDS',
330+
},
331+
choiceCardAmounts: regularChoiceCardAmounts,
332+
choiceCardsSettings: choiceCardsWithDestinationUrlTwoCards,
333+
},
334+
parameters: {
335+
docs: {
336+
description: {
337+
story: 'All choice cards have a destinationUrl configured. The banner should use these custom URLs instead of constructing URLs with product parameters.',
338+
},
339+
},
340+
},
341+
};

dotcom-rendering/src/components/marketing/epics/ContributionsEpic.stories.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import { SecondaryCtaType } from '@guardian/support-dotcom-components';
88
import type { Meta, StoryObj } from '@storybook/react';
99
import lzstring from 'lz-string';
1010
import React from 'react';
11-
import { choiceCardsSettings } from '../lib/storybook';
11+
import {
12+
choiceCardsSettings,
13+
choiceCardsWithDestinationUrl,
14+
} from '../lib/storybook';
1215
import { ContributionsEpicUnvalidated as ContributionsEpic } from './ContributionsEpic';
1316
import { props } from './utils/storybook';
1417

@@ -381,3 +384,17 @@ export const WithParagraphLinks: Story = {
381384
},
382385
},
383386
};
387+
388+
export const WithThreeTierChoiceCardsAndCustomDestinationUrl: Story = {
389+
name: 'ContributionsEpic with three tier choice cards and custom destination URL',
390+
args: {
391+
...meta.args,
392+
variant: {
393+
...props.variant,
394+
name: 'THREE_TIER_CHOICE_CARDS',
395+
secondaryCta: undefined,
396+
showChoiceCards: true,
397+
choiceCardsSettings: choiceCardsWithDestinationUrl,
398+
},
399+
},
400+
};

dotcom-rendering/src/components/marketing/epics/ctas/ContributionsEpicButtons.tsx

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ import { useEffect } from 'react';
1818
import { useIsInView } from '../../../../lib/useIsInView';
1919
import { hasSetReminder } from '../../lib/reminders';
2020
import {
21-
addChoiceCardsOneTimeParams,
22-
addChoiceCardsProductParams,
2321
enrichSupportUrl,
22+
getChoiceCardUrl,
2423
isSupportUrl,
2524
} from '../../lib/tracking';
2625
import {
@@ -139,7 +138,7 @@ interface ContributionsEpicButtonsProps {
139138
submitComponentEvent?: (event: ComponentEvent) => void;
140139
isReminderActive: boolean;
141140
isSignedIn: boolean;
142-
threeTierChoiceCardSelectedProduct?: ChoiceCard['product'];
141+
threeTierSelectedChoiceCard?: ChoiceCard;
143142
amountsTestName?: string;
144143
amountsVariantName?: string;
145144
promoCodes: string[];
@@ -153,7 +152,7 @@ export const ContributionsEpicButtons = ({
153152
submitComponentEvent,
154153
isReminderActive,
155154
isSignedIn,
156-
threeTierChoiceCardSelectedProduct,
155+
threeTierSelectedChoiceCard,
157156
amountsTestName,
158157
amountsVariantName,
159158
promoCodes,
@@ -179,25 +178,14 @@ export const ContributionsEpicButtons = ({
179178
}
180179

181180
const getCta = (cta: Cta): Cta => {
182-
if (threeTierChoiceCardSelectedProduct) {
183-
if (threeTierChoiceCardSelectedProduct.supportTier === 'OneOff') {
184-
return {
185-
text: cta.text,
186-
baseUrl: addChoiceCardsOneTimeParams(cta.baseUrl),
187-
};
188-
}
189-
190-
return {
191-
text: cta.text,
192-
baseUrl: addChoiceCardsProductParams(
193-
cta.baseUrl,
194-
threeTierChoiceCardSelectedProduct.supportTier,
195-
threeTierChoiceCardSelectedProduct.ratePlan,
196-
),
197-
};
181+
if (!threeTierSelectedChoiceCard?.product) {
182+
return cta;
198183
}
199184

200-
return cta;
185+
return {
186+
text: cta.text,
187+
baseUrl: getChoiceCardUrl(threeTierSelectedChoiceCard, cta.baseUrl),
188+
};
201189
};
202190

203191
const openReminder = () => {

dotcom-rendering/src/components/marketing/epics/ctas/ContributionsEpicCtasContainer.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ export const ContributionsEpicCtasContainer: ReactComponent<Props> = ({
4141
isTabletOrAbove,
4242
variant.choiceCardsSettings,
4343
);
44-
const defaultProduct = choiceCards?.find((cc) => cc.isDefault)?.product;
45-
const [
46-
threeTierChoiceCardSelectedProduct,
47-
setThreeTierChoiceCardSelectedProduct,
48-
] = useState<ChoiceCard['product'] | undefined>(defaultProduct);
44+
45+
const defaultChoiceCard = choiceCards?.find((cc) => cc.isDefault);
46+
const [selectedChoiceCard, setSelectedChoiceCard] = useState<
47+
ChoiceCard | undefined
48+
>(defaultChoiceCard);
4949

5050
return (
5151
<>
52-
{choiceCards && threeTierChoiceCardSelectedProduct && (
52+
{choiceCards && selectedChoiceCard && (
5353
<ThreeTierChoiceCards
54-
selectedProduct={threeTierChoiceCardSelectedProduct}
55-
setSelectedProduct={setThreeTierChoiceCardSelectedProduct}
54+
selectedChoiceCard={selectedChoiceCard}
55+
setSelectedChoiceCard={setSelectedChoiceCard}
5656
choices={choiceCards}
5757
id={'epic'}
5858
/>
@@ -76,9 +76,7 @@ export const ContributionsEpicCtasContainer: ReactComponent<Props> = ({
7676
submitComponentEvent={submitComponentEvent}
7777
isReminderActive={isReminderActive}
7878
isSignedIn={Boolean(fetchedEmail)}
79-
threeTierChoiceCardSelectedProduct={
80-
threeTierChoiceCardSelectedProduct
81-
}
79+
threeTierSelectedChoiceCard={selectedChoiceCard}
8280
amountsTestName={amountsTestName}
8381
amountsVariantName={amountsVariantName}
8482
promoCodes={variant.promoCodes ?? []}

0 commit comments

Comments
 (0)