Skip to content
Merged
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
2 changes: 1 addition & 1 deletion dotcom-rendering/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@guardian/shimport": "1.0.2",
"@guardian/source": "12.2.1",
"@guardian/source-development-kitchen": "28.1.0",
"@guardian/support-dotcom-components": "10.0.1",
"@guardian/support-dotcom-components": "10.1.0",
"@guardian/tsconfig": "catalog:",
"@playwright/test": "1.60.0",
"@sentry/browser": "10.52.0",
Expand Down
135 changes: 68 additions & 67 deletions dotcom-rendering/src/components/LiveblogGutterAskWrapper.island.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ import { getCookie, isUndefined } from '@guardian/libs';
import type { ComponentEvent } from '@guardian/ophan-tracker-js';
import { palette, space } from '@guardian/source/foundations';
import { getGutterLiveblog } from '@guardian/support-dotcom-components';
import type {
ModuleData,
ModuleDataResponse,
} from '@guardian/support-dotcom-components/dist/dotcom/types';
import type { ModuleDataResponse } from '@guardian/support-dotcom-components/dist/dotcom/types';
import type {
GutterPayload,
GutterProps,
} from '@guardian/support-dotcom-components/dist/shared/types';
import type { Tracking } from '@guardian/support-dotcom-components/dist/shared/types/props/shared';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { submitComponentEvent } from '../client/ophan/ophan';
import { shouldHideSupportMessaging } from '../lib/contributions';
import { useAB } from '../lib/useAB';
Expand Down Expand Up @@ -41,95 +38,99 @@ const LiveblogGutterAskBuilder = ({
pageUrl,
pageId,
}: LiveblogGutterAskBuilderProps) => {
const [gutterVariantResponse, setGutterVariantResponse] =
useState<ModuleData<GutterProps> | null>(null);
const [GutterWrapperComponent, setGutterWrapperComponent] =
useState<React.ElementType<GutterProps>>();
const [gutterVariantResponse, setGutterVariantResponse] =
useState<ModuleDataResponse<GutterProps>>();

const { renderingTarget } = useConfig();
const isSignedIn = useIsSignedIn();
const abTests = useAB();

// get gutter props
useEffect((): void => {
if (isUndefined(countryCode) || isSignedIn === 'Pending') {
return;
}
const tagIds = tags.map((tag) => tag.id);
const tagIds = useMemo(() => tags.map((tag) => tag.id), [tags]);

const hideSupportMessagingForUser =
shouldHideSupportMessaging(isSignedIn);
if (hideSupportMessagingForUser === 'Pending') {
// We don't yet know the user's supporter status
return;
}
const hideSupportMessagingForUser =
isSignedIn === 'Pending'
? 'Pending'
: shouldHideSupportMessaging(isSignedIn);
Comment thread
AnastasiiaBalenko marked this conversation as resolved.

const inHoldbackGroup =
abTests?.isUserInTestGroup('growth-holdback-group', 'control') ??
false;
const inHoldbackGroup =
abTests?.isUserInTestGroup('growth-holdback-group', 'control') ?? false;

// CALL the API
const payload: GutterPayload = {
// Build payload for SDC request
const payload: GutterPayload = useMemo(
() => ({
targeting: {
showSupportMessaging: !hideSupportMessagingForUser,
showSupportMessaging:
hideSupportMessagingForUser === 'Pending'
? false
: !hideSupportMessagingForUser,
countryCode,
mvtId: Number(
getCookie({ name: 'GU_mvt_id', shouldMemoize: true }),
),
isSignedIn,
isSignedIn: isSignedIn === 'Pending' ? false : isSignedIn,
tagIds,
sectionId,
pageId,
inHoldbackGroup,
},
};
}),
[
hideSupportMessagingForUser,
countryCode,
isSignedIn,
tagIds,
sectionId,
pageId,
inHoldbackGroup,
],
);

// Fetch gutter data from SDC
useEffect((): void => {
if (hideSupportMessagingForUser === 'Pending') {
// We don't yet know the user's supporter status
return;
}

getGutterLiveblog(contributionsServiceUrl, payload)
.then((response: ModuleDataResponse<GutterProps>) => {
if (response.data) {
const { module } = response.data;
setGutterVariantResponse(module);

import(`./marketing/gutters/GutterAskWrapper`)
.then((gutterModule) => {
setGutterWrapperComponent(
() => gutterModule.GutterAskWrapper,
);
})
.catch((err) => {
const msg = `Error importing GutterLiveBlog: ${String(
err,
)}`;
window.guardian.modules.sentry.reportError(
new Error(msg),
'rr-gutter-liveblog-dynamic-import',
);
});
}
.then((response) => {
setGutterVariantResponse(response);
})
.catch((error) => {
const msg = `Error fetching gutter-ask tests: ${String(error)}`;

console.log(msg);
.catch((err) => {
const msg = `Error fetching gutter data: ${String(err)}`;
window.guardian.modules.sentry.reportError(
new Error(msg),
'rr-gutter-liveblog-ask-fetch',
'rr-gutter-liveblog-fetch',
);
});
}, [
countryCode,
isSignedIn,
contributionsServiceUrl,
pageViewId,
pageUrl,
sectionId,
tags,
pageId,
abTests,
]);

if (gutterVariantResponse && !isUndefined(GutterWrapperComponent)) {
const { props } = gutterVariantResponse;
}, [contributionsServiceUrl, payload, hideSupportMessagingForUser]);

// Dynamically import the wrapper component when we have data
useEffect((): void => {
if (gutterVariantResponse?.data) {
import(`./marketing/gutters/GutterAskWrapper`)
.then((gutterModule) => {
setGutterWrapperComponent(
() => gutterModule.GutterAskWrapper,
);
})
.catch((err) => {
const msg = `Error importing GutterLiveBlog: ${String(
err,
)}`;
window.guardian.modules.sentry.reportError(
new Error(msg),
'rr-gutter-liveblog-dynamic-import',
);
});
}
}, [gutterVariantResponse]);

if (gutterVariantResponse?.data && !isUndefined(GutterWrapperComponent)) {
const { module } = gutterVariantResponse.data;
const { props } = module;

const tracking: Tracking = {
...props.tracking,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,15 @@ export const StickyBottomBanner = ({

const hasForceBannerParam =
window.location.search.includes('force-banner');
const hasPreviewBannerParam =
window.location.search.includes('preview-banner');
const hasForceBrazeMessageParam = window.location.hash.includes(
'force-braze-message',
);

let candidates: SlotConfig['candidates'];

if (hasForceBannerParam) {
if (hasForceBannerParam || hasPreviewBannerParam) {
candidates = [CMP, readerRevenue];
} else if (hasForceBrazeMessageParam) {
candidates = [CMP, brazeBannersSystem, brazeBanner];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,10 @@ export const canShowRRBanner: CanShowFunctionType<
signInBannerLastClosedAt === undefined;

const hasForceBannerParam = window.location.search.includes('force-banner');
const hasPreviewBannerParam =
window.location.search.includes('preview-banner');

if (!showSignInPrompt && !hasForceBannerParam) {
if (!showSignInPrompt && !hasForceBannerParam && !hasPreviewBannerParam) {
// Don't show a banner if one was closed recently. This is to improve user experience by not showing banners on consecutive pageviews
if (
recentlyClosedBanner(engagementBannerLastClosedAt) ||
Expand Down
14 changes: 14 additions & 0 deletions dotcom-rendering/src/lib/readerRevenueDevUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,25 @@ const getForcedVariant = (type: 'epic' | 'banner'): string | null => {
return null;
};

const getPreviewVariant = (type: 'epic' | 'banner'): string | null => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/no-unnecessary-condition -- Safety in global,
if (URLSearchParams) {
const params = new URLSearchParams(window.location.search);
const value = params.get(`preview-${type}`);
if (value) {
return value;
}
}

return null;
};

export {
changeGeolocation,
showMeTheEpic,
showMeTheBanner,
showNextVariant,
showPreviousVariant,
getForcedVariant,
getPreviewVariant,
};
18 changes: 18 additions & 0 deletions dotcom-rendering/src/lib/sdcRequests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,24 @@ describe('sdcRequests', () => {
},
);
});

it('does not append force when neither is present', async () => {
const { getEpic } = await import('./sdcRequests');
await getEpic('https://contributions.guardianapis.com', {
targeting: {},
} as never);

expect(global.fetch).toHaveBeenCalledWith(
'https://contributions.guardianapis.com/epic',
{
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: '{"targeting":{}}',
},
);
});
});

describe('getBanner', () => {
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading