Skip to content

Commit

Permalink
Merge pull request #12530 from guardian/doml/guardian-different-desktop
Browse files Browse the repository at this point in the history
Add expandable marketing card component to standard articles on desktop behind 0% test
  • Loading branch information
domlander authored Oct 14, 2024
2 parents 7bfc4ed + 5b41020 commit eda6c16
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 135 deletions.
173 changes: 89 additions & 84 deletions dotcom-rendering/src/components/ExpandableMarketingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
SvgCross,
} from '@guardian/source/react-components';
import type { Dispatch, SetStateAction } from 'react';
import { getZIndex } from '../lib/getZIndex';
import { palette } from '../palette';
import { useConfig } from './ConfigContext';

Expand All @@ -43,17 +42,6 @@ const fillBarStyles = css`
}
`;

const containerStyles = css`
${getZIndex('expandableMarketingCardOverlay')}
position: sticky;
top: 0;
${from.leftCol} {
padding-bottom: ${space[5]}px;
margin-right: -1px; /* To align with rich link - if we move this feature to production, we should remove this and make rich link align with everything instead */
}
`;

const contentStyles = css`
position: relative;
display: flex;
Expand Down Expand Up @@ -121,8 +109,16 @@ const detailsStyles = css`
flex-direction: column;
gap: ${space[4]}px;
margin-bottom: ${space[2]}px;
`;

h2 {
const sectionStyles = css`
display: flex;
flex-direction: column;
gap: ${space[3]}px;
border-top: 1px solid ${neutral[100]};
padding-top: ${space[2]}px;
h3 {
${headlineBold17};
}
Expand All @@ -133,14 +129,6 @@ const detailsStyles = css`
}
`;

const sectionStyles = css`
display: flex;
flex-direction: column;
gap: ${space[3]}px;
border-top: 1px solid ${neutral[100]};
padding-top: ${space[2]}px;
`;

const imageTopStyles = css`
position: absolute;
top: 0;
Expand All @@ -154,6 +142,15 @@ const imageBottomStyles = css`

const buttonStyles = css`
z-index: 1;
background-color: ${palette(
'--expandable-marketing-card-button-background',
)};
width: fit-content;
${textSansBold12};
${from.wide} {
${textSansBold14};
}
`;

interface Props {
Expand All @@ -175,90 +172,98 @@ export const ExpandableMarketingCard = ({
setIsClosed,
}: Props) => {
return (
<div
css={containerStyles}
data-component="us-expandable-marketing-card"
>
<div data-component="us-expandable-marketing-card">
<div css={fillBarStyles} />
<div css={contentStyles}>
{!isExpanded ? (
<BannersIllustration type="faded" styles={imageTopStyles} />
) : (
<>
<BannersIllustration
type="top"
type="faded"
styles={imageTopStyles}
/>
<BannersIllustration
type="bottom"
styles={imageBottomStyles}
/>
</>
)}
<div css={summaryStyles}>
<div css={headingStyles}>
{heading}
<button
<section
css={summaryStyles}
role="button"
tabIndex={0}
onClick={() => {
if (!isExpanded) {
setIsExpanded(true);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
setIsExpanded(true);
} else {
}
if (event.key === 'Escape') {
setIsClosed(true);
}
}}
type="button"
css={arrowStyles}
>
{isExpanded ? (
<SvgCross />
) : (
<SvgChevronDownSingle />
)}
</button>
</div>
<div css={kickerStyles}>{kicker}</div>
</div>
{isExpanded && (
<div css={detailsStyles}>
<div css={sectionStyles}>
<h2>We're independent</h2>
<p>
With no billionaire owner or shareholders, our
journalism is funded by readers
</p>
</div>
<div css={sectionStyles}>
<h2>We're open</h2>
<p>
With misinformation threatening democracy, we
keep our fact-based news paywall-free
</p>
</div>
<div css={sectionStyles}>
<h2>We're global</h2>
<p>
With 200 years of history and staff across
America and the world, we offer an outsider
perspective on US news
</p>
</div>
<div css={buttonStyles}>
<div css={headingStyles}>
<h2>{heading}</h2>
<div css={arrowStyles}>
<SvgChevronDownSingle />
</div>
</div>
<div css={kickerStyles}>{kicker}</div>
</section>
</>
) : (
<>
<BannersIllustration
type="top"
styles={imageTopStyles}
/>
<BannersIllustration
type="bottom"
styles={imageBottomStyles}
/>
<section css={summaryStyles}>
<div css={headingStyles}>
<h2>{heading}</h2>
<button
onClick={() => {
setIsClosed(true);
}}
type="button"
css={arrowStyles}
>
<SvgCross />
</button>
</div>
<div css={kickerStyles}>{kicker}</div>
</section>
<div css={detailsStyles}>
<section css={sectionStyles}>
<h3>We’re independent</h3>
<p>
With no billionaire owner or shareholders,
our journalism is funded by readers
</p>
</section>
<section css={sectionStyles}>
<h3>We’re open</h3>
<p>
With misinformation threatening democracy,
we keep our fact-based news paywall-free
</p>
</section>
<section css={sectionStyles}>
<h3>We’re global</h3>
<p>
With 200 years of history and staff across
America and the world, we offer an outsider
perspective on US news
</p>
</section>
<LinkButton
priority="tertiary"
size="xsmall"
href={`${guardianBaseURL}/email-newsletters`}
cssOverrides={css`
background-color: white;
${textSansBold12};
${from.wide} {
${textSansBold14};
}
`}
cssOverrides={buttonStyles}
>
View newsletters
</LinkButton>
</div>
</div>
</>
)}
</div>
</div>
Expand Down
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}
/>
);
};
22 changes: 22 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,26 @@ const bodyStyles = css`
${getZIndex('bodyArea')}
`;

const usCardStyles = css`
align-self: start;
position: sticky;
top: 0;
${getZIndex('expandableMarketingCardOverlay')}
${from.leftCol} {
margin-top: ${space[6]}px;
margin-bottom: ${space[9]}px;
/* To align with rich links - if we move this feature to production, we should remove this and make rich link align with everything instead */
margin-left: 1px;
margin-right: -1px;
}
${from.wide} {
margin-left: 0;
}
`;

const gridArea = css`
grid-area: var(--grid-area);
`;
Expand All @@ -41,6 +62,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,
];
Loading

0 comments on commit eda6c16

Please sign in to comment.