Skip to content

Commit

Permalink
Merge pull request #12534 from guardian/doml/guardian-different-mobile
Browse files Browse the repository at this point in the history
US Expandable Marketing Card component on small screens
  • Loading branch information
domlander authored Oct 18, 2024
2 parents 778a9fb + f5e3d90 commit 20dcda1
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ export const Default = {
chromatic: {
viewports: [
breakpoints.mobile,
breakpoints.mobileMedium,
breakpoints.phablet,
breakpoints.tablet,
breakpoints.desktop,
],
Expand Down
48 changes: 44 additions & 4 deletions dotcom-rendering/src/components/ExpandableMarketingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,44 @@ interface BannersIllustrationProps {
styles?: SerializedStyles;
}

const smallIllustrationStyles = css`
display: none;
${from.phablet} {
display: inline;
}
${from.leftCol} {
display: none;
}
`;
const largeIllustrationStyles = css`
display: inline;
${from.phablet} {
display: none;
}
${from.leftCol} {
display: inline;
}
`;

const BannersIllustration = ({ type, styles }: BannersIllustrationProps) => {
const { assetOrigin } = useConfig();
const src = `${assetOrigin}static/frontend/logos/red-blue-banner-${type}.svg`;
return <img src={src} alt="" css={styles} />;
const smallSrc = `${assetOrigin}static/frontend/logos/red-blue-small-banner-${type}.svg`;
const largeSrc = `${assetOrigin}static/frontend/logos/red-blue-large-banner-${type}.svg`;

return (
<>
<img
src={smallSrc}
alt=""
css={[styles, smallIllustrationStyles]}
/>
<img
src={largeSrc}
alt=""
css={[styles, largeIllustrationStyles]}
/>
</>
);
};

const fillBarStyles = css`
Expand Down Expand Up @@ -64,6 +98,10 @@ const summaryStyles = css`
z-index: 1;
width: 100%;
`;
const contractedSummaryStyles = css`
${summaryStyles}
cursor: pointer;
`;

const headingStyles = css`
display: flex;
Expand Down Expand Up @@ -162,7 +200,6 @@ interface Props {
setIsClosed: Dispatch<SetStateAction<boolean>>;
}

// todo - semantic html accordion-details?
export const ExpandableMarketingCard = ({
guardianBaseURL,
heading,
Expand All @@ -182,7 +219,8 @@ export const ExpandableMarketingCard = ({
styles={imageTopStyles}
/>
<section
css={summaryStyles}
data-link-name="us-expandable-marketing-card expand"
css={contractedSummaryStyles}
role="button"
tabIndex={0}
onClick={() => {
Expand Down Expand Up @@ -220,6 +258,7 @@ export const ExpandableMarketingCard = ({
<div css={headingStyles}>
<h2>{heading}</h2>
<button
data-link-name="us-expandable-marketing-card close"
onClick={() => {
setIsClosed(true);
}}
Expand Down Expand Up @@ -255,6 +294,7 @@ export const ExpandableMarketingCard = ({
</p>
</section>
<LinkButton
data-link-name="us-expandable-marketing-card cta-click"
priority="tertiary"
size="xsmall"
href={`${guardianBaseURL}/email-newsletters`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { css } from '@emotion/react';
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 { getZIndex } from '../lib/getZIndex';
import { useAB } from '../lib/useAB';
import { ExpandableMarketingCard } from './ExpandableMarketingCard';
import { Hide } from './Hide';

interface Props {
guardianBaseURL: string;
}
const isViewportBelowTopOfBody = (topOfBody: Element) =>
topOfBody.getBoundingClientRect().top < 0;

const isFirstArticle = () => {
const [dailyCount = {} as DailyArticle] = getDailyArticleCount() ?? [];
Expand All @@ -32,11 +34,43 @@ const isNewUSUser = async () => {
return !hasUserSelectedNonUSEdition && !isNewUser;
};

// todo - semantic html accordion-details?
const stickyContainerStyles = css`
position: sticky;
top: 0;
${getZIndex('expandableMarketingCardOverlay')};
animation: slidein 2.4s linear;
@keyframes slidein {
from {
transform: translateX(-1200px);
}
to {
transform: translateX(0);
}
}
`;

const absoluteContainerStyles = css`
position: absolute;
width: 100%;
`;

interface Props {
guardianBaseURL: string;
}

export const ExpandableMarketingCardWrapper = ({ guardianBaseURL }: Props) => {
const [isExpanded, setIsExpanded] = useState(false);
const [isClosed, setIsClosed] = useState(false);
const [isApplicableUser, setIsApplicableUser] = useState(false);
const [topOfBody, setTopOfBody] = useState<Element | null>(null);

/**
* On screen sizes below leftCol (<1140px), only display the card
* when the user scrolls past the top of the article.
*/
const [shouldDisplayCard, setShouldDisplayCard] = useState(false);

const abTestAPI = useAB()?.api;
const isInVariantFree = !!abTestAPI?.isUserInVariant(
Expand All @@ -57,6 +91,41 @@ export const ExpandableMarketingCardWrapper = ({ guardianBaseURL }: Props) => {
});
}, []);

useEffect(() => {
setTopOfBody(document.querySelector('[data-gu-name="body"]'));
}, []);

useEffect(() => {
if (!topOfBody) return;

/**
* It's possible that the viewport does not start at the top of the page.
*/
if (isViewportBelowTopOfBody(topOfBody)) {
setShouldDisplayCard(true);
return;
}

/**
* Show the card when the top of the body moves out of the viewport.
*/
const observer = new window.IntersectionObserver(
([entry]) => {
if (!entry) return;
if (entry.isIntersecting) {
setShouldDisplayCard(true);
}
},
{ rootMargin: '0px 0px -100%' },
);

observer.observe(topOfBody);

return () => {
observer.disconnect();
};
}, [topOfBody]);

if (!isInEitherVariant || !isApplicableUser || isClosed) {
return null;
}
Expand All @@ -70,13 +139,33 @@ export const ExpandableMarketingCardWrapper = ({ guardianBaseURL }: Props) => {
: 'Why the Guardian has no paywall';

return (
<ExpandableMarketingCard
guardianBaseURL={guardianBaseURL}
heading={heading}
kicker={kicker}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
setIsClosed={setIsClosed}
/>
<>
<Hide when="below" breakpoint="leftCol">
<ExpandableMarketingCard
guardianBaseURL={guardianBaseURL}
heading={heading}
kicker={kicker}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
setIsClosed={setIsClosed}
/>
</Hide>
<Hide when="above" breakpoint="leftCol">
{shouldDisplayCard && (
<div css={stickyContainerStyles}>
<div css={absoluteContainerStyles}>
<ExpandableMarketingCard
guardianBaseURL={guardianBaseURL}
heading={heading}
kicker={kicker}
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
setIsClosed={setIsClosed}
/>
</div>
</div>
)}
</Hide>
</>
);
};
18 changes: 18 additions & 0 deletions dotcom-rendering/src/layouts/CommentLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,24 @@ export const CommentLayout = (props: WebProps | AppsProps) => {
</div>
</GridItem>
<GridItem area="body">
{isWeb && (
<Hide when="above" breakpoint="leftCol">
<Island
priority="enhancement"
/**
* We display the card immediately if the viewport is below the top of
* the article body, so we must use "idle" instead of "visible".
*/
defer={{ until: 'idle' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
)}
<ArticleContainer format={format}>
<div css={maxWidth}>
<ArticleBody
Expand Down
18 changes: 18 additions & 0 deletions dotcom-rendering/src/layouts/ImmersiveLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,24 @@ export const ImmersiveLayout = (props: WebProps | AppProps) => {
</div>
</GridItem>
<GridItem area="body">
{isWeb && (
<Hide when="above" breakpoint="leftCol">
<Island
priority="enhancement"
/**
* We display the card immediately if the viewport is below the top of
* the article body, so we must use "idle" instead of "visible".
*/
defer={{ until: 'idle' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
)}
<ArticleContainer format={format}>
<ArticleBody
format={format}
Expand Down
17 changes: 0 additions & 17 deletions dotcom-rendering/src/layouts/PictureLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { Carousel } from '../components/Carousel.importable';
import { ContributorAvatar } from '../components/ContributorAvatar';
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 { HeaderAdSlot } from '../components/HeaderAdSlot';
Expand Down Expand Up @@ -566,22 +565,6 @@ export const PictureLayout = (props: WebProps | AppsProps) => {
/>
</ArticleContainer>
</GridItem>
{isWeb && (
<GridItem area="uscard" element="aside">
<Hide until="leftCol">
<Island
priority="enhancement"
defer={{ until: 'visible' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
</GridItem>
)}
</PictureGrid>
</Section>

Expand Down
18 changes: 18 additions & 0 deletions dotcom-rendering/src/layouts/ShowcaseLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,24 @@ export const ShowcaseLayout = (props: WebProps | AppsProps) => {
</div>
</GridItem>
<GridItem area="body">
{isWeb && (
<Hide from="leftCol">
<Island
priority="enhancement"
/**
* We display the card immediately if the viewport is below the top of
* the article body, so we must use "idle" instead of "visible".
*/
defer={{ until: 'idle' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
)}
<ArticleContainer format={format}>
<ArticleBody
format={format}
Expand Down
18 changes: 18 additions & 0 deletions dotcom-rendering/src/layouts/StandardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,24 @@ export const StandardLayout = (props: WebProps | AppProps) => {
)}
</GridItem>
<GridItem area="body">
{isWeb && (
<Hide from="leftCol">
<Island
priority="enhancement"
/**
* We display the card immediately if the viewport is below the top of
* the article body, so we must use "idle" instead of "visible".
*/
defer={{ until: 'idle' }}
>
<ExpandableMarketingCardWrapper
guardianBaseURL={
article.guardianBaseURL
}
/>
</Island>
</Hide>
)}
<ArticleContainer format={format}>
<ArticleBody
format={format}
Expand Down
Loading

0 comments on commit 20dcda1

Please sign in to comment.