Skip to content

Commit b8ce109

Browse files
author
Juarez Mota
committed
feat: Enhance StickyBottomBanner with AB testing support and improve message handling in Storybook
1 parent 3180236 commit b8ce109

File tree

6 files changed

+374
-396
lines changed

6 files changed

+374
-396
lines changed

dotcom-rendering/.storybook/preview.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ style.appendChild(document.createTextNode(css));
5656
// Could this be better? Sure, we could investigate ways to really, truly server side
5757
// render in Storybook but for now this (and some of the other steps we take around
5858
// hydration) achieve what we need
59-
window.guardian = {
59+
(window as any).guardian = {
6060
config: {
6161
ophan: {
6262
pageViewId: 'mockPageViewId',

dotcom-rendering/src/components/StickyBottomBanner.importable.tsx

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ABTestAPI } from '@guardian/ab-core';
12
import type {
23
BrazeArticleContext,
34
BrazeMessagesInterface,
@@ -9,12 +10,14 @@ import type { BannerProps } from '@guardian/support-dotcom-components/dist/share
910
import { useEffect, useState } from 'react';
1011
import { getArticleCounts } from '../lib/articleCount';
1112
import type { ArticleCounts } from '../lib/articleCount';
13+
import type { EditionId } from '../lib/edition';
1214
import type {
1315
CandidateConfig,
1416
MaybeFC,
1517
SlotConfig,
1618
} from '../lib/messagePicker';
1719
import { pickMessage } from '../lib/messagePicker';
20+
import { useAB } from '../lib/useAB';
1821
import { useIsSignedIn } from '../lib/useAuthStatus';
1922
import { useBraze } from '../lib/useBraze';
2023
import { useCountryCode } from '../lib/useCountryCode';
@@ -50,6 +53,7 @@ type Props = {
5053

5154
pageId: string;
5255
host?: string;
56+
abTestAPI?: ABTestAPI; // Optional prop for testing (e.g., Storybook)
5357
};
5458

5559
type BrazeMeta = {
@@ -75,19 +79,43 @@ const DEFAULT_BANNER_TIMEOUT_MILLIS = 2000;
7579
const buildCmpBannerConfig = (): CandidateConfig<void> => ({
7680
candidate: {
7781
id: 'cmpUi',
78-
canShow: () =>
79-
cmp
80-
.willShowPrivacyMessage()
81-
.then((result) =>
82-
result ? { show: true, meta: undefined } : { show: false },
83-
),
82+
canShow: () => {
83+
// In Storybook environment, CMP APIs may not be available
84+
// Return false immediately to avoid hanging
85+
if (
86+
typeof window !== 'undefined' &&
87+
(window.location.hostname === 'localhost' ||
88+
window.location.hostname === '127.0.0.1')
89+
) {
90+
// eslint-disable-next-line no-console -- Required for debugging CMP issues
91+
console.log(
92+
'CMP: Detected Storybook/localhost environment, skipping CMP check',
93+
);
94+
return Promise.resolve({ show: false as const });
95+
}
96+
97+
try {
98+
return cmp
99+
.willShowPrivacyMessage()
100+
.then((result) => {
101+
return result
102+
? { show: true as const, meta: undefined }
103+
: { show: false as const };
104+
})
105+
.catch(() => {
106+
return { show: false as const };
107+
});
108+
} catch (error) {
109+
return Promise.resolve({ show: false as const });
110+
}
111+
},
84112
show: () => {
85113
// New CMP is not a react component and is shown outside of react's world
86114
// so render nothing if it will show
87115
return null;
88116
},
89117
},
90-
timeoutMillis: null,
118+
timeoutMillis: 1, // Add 1-millisecond timeout to prevent hanging in problematic environments
91119
});
92120

93121
const buildRRBannerConfigWith = ({
@@ -183,12 +211,23 @@ const buildSignInGateConfig = (
183211
contentType: string,
184212
sectionId: string,
185213
tags: TagType[],
214+
pageId: string,
215+
contributionsServiceUrl: string,
216+
editionId: EditionId,
217+
idUrl: string,
186218
host?: string,
219+
abTestAPI?: ABTestAPI,
187220
): CandidateConfig<void> => ({
188221
candidate: {
189222
id: 'sign-in-gate-portal',
190223
canShow: () =>
191-
canShowSignInGatePortal(isSignedIn, isPaidContent, isPreview),
224+
canShowSignInGatePortal(
225+
isSignedIn,
226+
isPaidContent,
227+
isPreview,
228+
pageId,
229+
abTestAPI,
230+
),
192231
show: () => () => (
193232
<SignInGatePortal
194233
host={host}
@@ -197,6 +236,11 @@ const buildSignInGateConfig = (
197236
tags={tags}
198237
isPaidContent={isPaidContent}
199238
isPreview={isPreview}
239+
pageId={pageId}
240+
contributionsServiceUrl={contributionsServiceUrl}
241+
editionId={editionId}
242+
idUrl={idUrl}
243+
abTestAPI={abTestAPI}
200244
/>
201245
),
202246
},
@@ -259,12 +303,17 @@ export const StickyBottomBanner = ({
259303
pageId,
260304
remoteBannerSwitch,
261305
host,
306+
abTestAPI: propAbTestAPI, // Optional prop for testing
262307
}: Props & {
263308
remoteBannerSwitch: boolean;
264309
isSensitive: boolean;
265310
}) => {
266-
const { renderingTarget } = useConfig();
311+
const { renderingTarget, editionId } = useConfig();
267312
const { brazeMessages } = useBraze(idApiUrl, renderingTarget);
313+
const hookAbTestAPI = useAB()?.api;
314+
315+
// Use prop abTestAPI if provided, otherwise fall back to hook
316+
const abTestAPI = propAbTestAPI ?? hookAbTestAPI;
268317

269318
const countryCode = useCountryCode('sticky-bottom-banner');
270319
const isSignedIn = useIsSignedIn();
@@ -330,7 +379,12 @@ export const StickyBottomBanner = ({
330379
contentType,
331380
sectionId,
332381
tags,
382+
pageId,
383+
contributionsServiceUrl,
384+
editionId,
385+
idApiUrl, // Using idApiUrl as idUrl
333386
host,
387+
abTestAPI,
334388
);
335389

336390
const bannerConfig: SlotConfig = {
@@ -359,6 +413,7 @@ export const StickyBottomBanner = ({
359413
asyncArticleCounts,
360414
contentType,
361415
contributionsServiceUrl,
416+
editionId,
362417
idApiUrl,
363418
isMinuteArticle,
364419
isPaidContent,
@@ -372,6 +427,7 @@ export const StickyBottomBanner = ({
372427
ophanPageViewId,
373428
pageId,
374429
host,
430+
abTestAPI,
375431
]);
376432

377433
if (SelectedBanner) {

0 commit comments

Comments
 (0)