Skip to content

Commit

Permalink
Display US marketing card to US users in ab test
Browse files Browse the repository at this point in the history
  • Loading branch information
domlander committed Oct 10, 2024
1 parent f9bf7d3 commit 3d2892f
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { getCookie } from '@guardian/libs';
import { useEffect, useState } from 'react';
import type { DailyArticle } from '../lib/dailyArticleCount';
import { getDailyArticleCount } from '../lib/dailyArticleCount';
import { getLocaleCode } from '../lib/getCountryCode';
import { useAB } from '../lib/useAB';
import { ExpandableMarketingCard } from './ExpandableMarketingCard';

interface Props {
guardianBaseURL: string;
}

const isFirstArticle = () => {
const [dailyCount = {} as DailyArticle] = getDailyArticleCount() ?? [];
return Object.keys(dailyCount).length === 0 || dailyCount.count <= 1;
};

const isNewUSUser = async () => {
const isUserInUS = (await getLocaleCode()) === 'US';
if (!isUserInUS) {
return false;
}

// Exclude users who have selected a non-US edition.
const editionCookie = getCookie({ name: 'GU_EDITION' });
const hasUserSelectedNonUSEdition =
!!editionCookie && editionCookie !== 'US';

// This check must happen AFTER we've ensured that the user is in the US.
const isNewUser = isFirstArticle();

return !hasUserSelectedNonUSEdition && !isNewUser;
};

// todo - semantic html accordion-details?
export const ExpandableMarketingCardWrapper = ({ guardianBaseURL }: Props) => {
const [isExpanded, setIsExpanded] = useState(false);
const [isClosed, setIsClosed] = useState(false);
const [isApplicableUser, setIsApplicableUser] = useState(false);

const abTestAPI = useAB()?.api;
const isInVariantFree = !!abTestAPI?.isUserInVariant(
'UsaExpandableMarketingCard',
'variant-free',
);
const isInVariantBubble = !!abTestAPI?.isUserInVariant(
'UsaExpandableMarketingCard',
'variant-bubble',
);
const isInEitherVariant = isInVariantFree || isInVariantBubble;

useEffect(() => {
void isNewUSUser().then((show) => {
if (show) {
setIsApplicableUser(true);
}
});
}, []);

if (!isInEitherVariant || !isApplicableUser || isClosed) {
return null;
}

const heading = isInVariantBubble
? 'Pop your US news bubble'
: 'Yes, this story is free';

const kicker = isInVariantBubble
? 'How the Guardian is different'
: 'Why the Guardian has no paywall';

return (
<ExpandableMarketingCard
guardianBaseURL={guardianBaseURL}
heading={heading}
kicker={kicker}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
setIsClosed={setIsClosed}
/>
);
};
13 changes: 13 additions & 0 deletions dotcom-rendering/src/components/GridItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { css } from '@emotion/react';
import { from, space } from '@guardian/source/foundations';
import { getZIndex } from '../lib/getZIndex';

type Props = {
Expand Down Expand Up @@ -28,6 +29,17 @@ const bodyStyles = css`
${getZIndex('bodyArea')}
`;

const usCardStyles = css`
${from.leftCol} {
margin-top: ${space[6]}px;
margin-left: 1px; /* To align with rich links */
}
${from.wide} {
margin-left: 0;
}
`;

const gridArea = css`
grid-area: var(--grid-area);
`;
Expand All @@ -41,6 +53,7 @@ export const GridItem = ({
css={[
area === 'body' && bodyStyles,
area === 'right-column' && rightColumnStyles,
area === 'uscard' && usCardStyles,
gridArea,
]}
style={{
Expand Down
3 changes: 1 addition & 2 deletions dotcom-rendering/src/components/SignInGate/displayRule.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// use the dailyArticleCount from the local storage to see how many articles the user has viewed in a day
import { onConsent } from '@guardian/libs';
import type { ConsentState } from '@guardian/libs';
import type { CountryCode } from '@guardian/libs';
import type { ConsentState, CountryCode } from '@guardian/libs';
import type { DailyArticle } from '../../lib/dailyArticleCount';
import { getDailyArticleCount } from '../../lib/dailyArticleCount';
import type { TagType } from '../../types/tag';
Expand Down
2 changes: 2 additions & 0 deletions dotcom-rendering/src/experiments/ab-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { mpuWhenNoEpic } from './tests/mpu-when-no-epic';
import { optimiseSpacefinderInline } from './tests/optimise-spacefinder-inline';
import { signInGateMainControl } from './tests/sign-in-gate-main-control';
import { signInGateMainVariant } from './tests/sign-in-gate-main-variant';
import { UsaExpandableMarketingCard } from './tests/usa-expandable-marketing-card';

// keep in sync with ab-tests in frontend
// https://github.com/guardian/frontend/tree/main/static/src/javascripts/projects/common/modules/experiments/ab-tests.ts
Expand All @@ -19,4 +20,5 @@ export const tests: ABTest[] = [
mpuWhenNoEpic,
adBlockAsk,
optimiseSpacefinderInline,
UsaExpandableMarketingCard,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { ABTest } from '@guardian/ab-core';

export const UsaExpandableMarketingCard: ABTest = {
id: 'UsaExpandableMarketingCard',
start: '2024-10-02',
expiry: '2024-12-18',
author: 'dotcom.platform@guardian.co.uk',
description:
'Test the impact of showing the user a component that highlights the Guardians journalism.',
audience: 0 / 100,
audienceOffset: 0 / 100,
audienceCriteria: 'US-based users that see the US edition.',
successMeasure: 'Users are more likely to engage with the site.',
canRun: () => true,
variants: [
{
id: 'control',
test: (): void => {
/* no-op */
},
},
{
id: 'variant-free',
test: (): void => {
/* no-op */
},
},
{
id: 'variant-bubble',
test: (): void => {
/* no-op */
},
},
],
};
57 changes: 39 additions & 18 deletions dotcom-rendering/src/layouts/CommentLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Border } from '../components/Border';
import { Carousel } from '../components/Carousel.importable';
import { ContributorAvatar } from '../components/ContributorAvatar';
import { DiscussionLayout } from '../components/DiscussionLayout';
import { ExpandableMarketingCardWrapper } from '../components/ExpandableMarketingCardWrapper.importable';
import { Footer } from '../components/Footer';
import { GridItem } from '../components/GridItem';
import { HeaderAdSlot } from '../components/HeaderAdSlot';
Expand Down Expand Up @@ -96,18 +97,20 @@ const StandardGrid = ({
'title border headline headline headline'
'lines border headline headline headline'
'meta border standfirst standfirst standfirst'
'meta border media media media'
'. border body . right-column'
'. border . . right-column';
'uscard border standfirst standfirst standfirst'
'uscard border media media media'
'uscard border body . right-column'
'uscard border . . right-column';
`
: css`
grid-template-areas:
'title border headline . right-column'
'lines border headline . right-column'
'meta border standfirst . right-column'
'meta border media . right-column'
'. border body . right-column'
'. border . . right-column';
'uscard border standfirst . right-column'
'uscard border media . right-column'
'uscard border body . right-column'
'uscard border . . right-column';
`}
}
Expand All @@ -125,21 +128,23 @@ const StandardGrid = ({
${display === ArticleDisplay.Showcase
? css`
grid-template-areas:
'title border headline headline'
'lines border headline headline'
'meta border standfirst standfirst'
'meta border media media'
'. border body right-column'
'. border . right-column';
'title border headline headline'
'lines border headline headline'
'meta border standfirst standfirst'
'uscard border standfirst standfirst'
'uscard border media media'
'uscard border body right-column'
'uscard border . right-column';
`
: css`
grid-template-areas:
'title border headline right-column'
'lines border headline right-column'
'meta border standfirst right-column'
'meta border media right-column'
'. border body right-column'
'. border . right-column';
'title border headline right-column'
'lines border headline right-column'
'meta border standfirst right-column'
'uscard border standfirst right-column'
'uscard border media right-column'
'uscard border body right-column'
'uscard border . right-column';
`}
}
Expand Down Expand Up @@ -547,6 +552,22 @@ export const CommentLayout = (props: WebProps | AppsProps) => {
)}
</div>
</GridItem>
{isWeb && (
<GridItem area="uscard" element="aside">
<Hide when="below" breakpoint="leftCol">
<Island
priority="enhancement"
defer={{ until: 'visible' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
</GridItem>
)}
<GridItem area="body">
<ArticleContainer format={format}>
<div css={maxWidth}>
Expand Down
37 changes: 30 additions & 7 deletions dotcom-rendering/src/layouts/ImmersiveLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Caption } from '../components/Caption';
import { Carousel } from '../components/Carousel.importable';
import { DecideLines } from '../components/DecideLines';
import { DiscussionLayout } from '../components/DiscussionLayout';
import { ExpandableMarketingCardWrapper } from '../components/ExpandableMarketingCardWrapper.importable';
import { Footer } from '../components/Footer';
import { GridItem } from '../components/GridItem';
import { GuardianLabsLines } from '../components/GuardianLabsLines';
Expand Down Expand Up @@ -84,6 +85,8 @@ const ImmersiveGrid = ({ children }: { children: React.ReactNode }) => (
Vertical grey border
Main content
Right Column
Duplicate lines are required to ensure the left column does not have extra vertical space.
*/
${from.wide} {
grid-column-gap: 10px;
Expand All @@ -95,18 +98,21 @@ const ImmersiveGrid = ({ children }: { children: React.ReactNode }) => (
'. border byline . right-column'
'lines border body . right-column'
'meta border body . right-column'
'meta border body . right-column'
'. border body . right-column'
'. border . . right-column';
'uscard border body . right-column'
'uscard border . . right-column'
'uscard border . . right-column'
'uscard border . . right-column';
}
/*
Explanation of each unit of grid-template-columns
Left Column (220 - 1px border)
Vertical grey border
Vertical grey borders
Main content
Right Column
Duplicate lines are required to ensure the left column does not have extra vertical space.
*/
${until.wide} {
grid-column-gap: 10px;
Expand All @@ -118,9 +124,10 @@ const ImmersiveGrid = ({ children }: { children: React.ReactNode }) => (
'. border byline right-column'
'lines border body right-column'
'meta border body right-column'
'meta border body right-column'
'. border body right-column'
'. border . right-column';
'uscard border body right-column'
'uscard border . right-column'
'uscard border . right-column'
'uscard border . right-column';
}
/*
Expand Down Expand Up @@ -641,6 +648,22 @@ export const ImmersiveLayout = (props: WebProps | AppProps) => {
)}
</div>
</GridItem>
{isWeb && (
<GridItem area="uscard" element="aside">
<Hide when="below" breakpoint="leftCol">
<Island
priority="enhancement"
defer={{ until: 'visible' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
</GridItem>
)}
<GridItem area="body">
<ArticleContainer format={format}>
<ArticleBody
Expand Down
1 change: 0 additions & 1 deletion dotcom-rendering/src/layouts/InteractiveLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,6 @@ export const InteractiveLayout = (props: WebProps | AppsProps) => {
standfirst={article.standfirst}
/>
</GridItem>

<GridItem area="lines">
<div css={maxWidth}>
<div css={stretchLines}>
Expand Down
Loading

0 comments on commit 3d2892f

Please sign in to comment.