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
40 changes: 39 additions & 1 deletion dotcom-rendering/src/components/EmailSignUpWrapper.island.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EmailSignup } from './EmailSignup';
import { InlineSkipToWrapper } from './InlineSkipToWrapper';
import { Island } from './Island';
import { NewsletterPrivacyMessage } from './NewsletterPrivacyMessage';
import { NewsletterSignupCardContainer } from './NewsletterSignupCardContainer';
import { Placeholder } from './Placeholder';
import { SecureSignup } from './SecureSignup.island';

Expand All @@ -22,11 +23,15 @@ interface EmailSignUpWrapperProps extends EmailSignUpProps {
listId: number;
identityName: string;
successDescription: string;
/** Illustration image URL (square crop) for the NewsletterSignupCard variant */
illustrationSquare?: string;
idApiUrl: string;
/** You should only set this to true if the privacy message will be shown elsewhere on the page */
hidePrivacyMessage?: boolean;
/** Feature flag to enable hiding newsletter signup for already subscribed users */
hideNewsletterSignupComponentForSubscribers?: boolean;
/** Feature flag to show the new NewsletterSignupCard design instead of EmailSignup */
showNewNewsletterSignupCard?: boolean;
}

/**
Expand All @@ -44,14 +49,47 @@ export const EmailSignUpWrapper = ({
listId,
idApiUrl,
hideNewsletterSignupComponentForSubscribers = false,
showNewNewsletterSignupCard = false,
...emailSignUpProps
}: EmailSignUpWrapperProps) => {
const shouldCheckSubscription =
hideNewsletterSignupComponentForSubscribers &&
!showNewNewsletterSignupCard;
const isSubscribed = useNewsletterSubscription(
listId,
idApiUrl,
hideNewsletterSignupComponentForSubscribers,
shouldCheckSubscription,
);
Comment thread
georgerichmond marked this conversation as resolved.

// When the new card design is enabled, always show it regardless of subscription status
if (showNewNewsletterSignupCard) {
Comment thread
georgerichmond marked this conversation as resolved.
return (
<InlineSkipToWrapper
id={`EmailSignup-skip-link-${index}`}
blockDescription="newsletter promotion"
>
<NewsletterSignupCardContainer
name={emailSignUpProps.name}
frequency={emailSignUpProps.frequency}
description={emailSignUpProps.description}
illustrationSquare={emailSignUpProps.illustrationSquare}
>
<Island priority="feature" defer={{ until: 'visible' }}>
<SecureSignup
newsletterId={emailSignUpProps.identityName}
successDescription={
emailSignUpProps.successDescription
}
/>
</Island>
{!emailSignUpProps.hidePrivacyMessage && (
<NewsletterPrivacyMessage />
)}
</NewsletterSignupCardContainer>
</InlineSkipToWrapper>
);
}

// Show placeholder while subscription status is being determined
// This prevents layout shift in both subscribed and non-subscribed cases
if (isSubscribed === undefined) {
Expand Down
41 changes: 41 additions & 0 deletions dotcom-rendering/src/components/NewsletterSignupCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { allModes } from '../../.storybook/modes';
import preview from '../../.storybook/preview';
import { NewsletterSignupCard } from './NewsletterSignupCard';
import { Section } from './Section';

const meta = preview.meta({
component: NewsletterSignupCard,
title: 'Components/Newsletter Signup Card',
parameters: {
chromatic: {
modes: {
'vertical mobile': allModes['vertical mobile'],
'vertical tablet': allModes['vertical tablet'],
},
},
},
decorators: [
(Story) => (
<Section
title="NewsletterSignupCard"
showTopBorder={true}
padContent={false}
centralBorder="partial"
>
<Story />
</Section>
),
],
});

export const Default = meta.story({
args: {
name: 'Saturday Edition',
description:
"An exclusive roundup of the week's best Guardian journalism from the editor-in-chief, Katharine Viner, free to your inbox every Saturday.",
frequency: 'Weekly',
illustrationSquare:
'https://i.guim.co.uk/img/uploads/2023/11/01/SaturdayEdition_-_5-3.jpg?width=220&dpr=2&s=none&crop=5%3A3',
children: <></>,
},
});
132 changes: 132 additions & 0 deletions dotcom-rendering/src/components/NewsletterSignupCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { css } from '@emotion/react';
import {
headlineMedium20,
space,
textSans14,
textSans15,
} from '@guardian/source/foundations';
import { SvgNewsletterFilled } from '@guardian/source/react-components';
import { palette as themePalette } from '../palette';

export type NewsletterSignupCardProps = {
name: string;
frequency: string;
description: string;
illustrationSquare?: string;
children?: React.ReactNode;
};

const containerStyles = css`
background-color: ${themePalette('--newsletter-card-background')};
margin-bottom: ${space[6]}px;
padding: ${space[2]}px ${space[2]}px ${space[4]}px ${space[2]}px;
`;

const dividerStyles = css`
clear: left;
border: none;
border-top: 1px solid ${themePalette('--newsletter-card-divider')};
margin: ${space[6]}px 0 ${space[2]}px;
`;

const headerStyles = css`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-start;
gap: ${space[2]}px;
margin-bottom: ${space[1]}px;
`;

const titleAndMetaStyles = css`
display: flex;
flex-direction: column;
`;

const titleStyles = css`
${headlineMedium20};
margin-bottom: ${space[2]}px;
color: ${themePalette('--newsletter-card-title')};
`;

const frequencyTagStyles = css`
display: flex;
align-items: center;
color: ${themePalette('--newsletter-card-frequency-tag')};
${textSans15};
margin-left: -1px;
margin-top: -1px;
margin-bottom: ${space[1]}px;

svg {
fill: currentColor;
height: 20px;
width: 20px;
}
`;

const descriptionStyles = css`
${textSans14};
line-height: 1.15;
margin-bottom: ${space[1]}px;
clear: both;
color: ${themePalette('--newsletter-card-description')};
`;

const illustrationStyles = css`
flex-shrink: 0;
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
`;

const NewsletterSignupHeader = (props: {
frequency: string;
name: string;
description: string;
illustrationSquare?: string;
}) => (
<div css={headerStyles}>
<div css={titleAndMetaStyles}>
<div css={frequencyTagStyles}>
<SvgNewsletterFilled />
Newsletter | {props.frequency}
</div>
<p css={titleStyles}>
Sign up to <span>{props.name}</span>
</p>
<p css={descriptionStyles}>{props.description}</p>
</div>
{!!props.illustrationSquare && (
<img
css={illustrationStyles}
src={props.illustrationSquare}
alt=""
loading="lazy"
decoding="async"
/>
)}
</div>
);

export const NewsletterSignupCard = ({
name,
frequency,
description,
illustrationSquare,
children,
}: NewsletterSignupCardProps) => (
<>
<hr css={dividerStyles} />
<aside css={containerStyles} aria-label="newsletter promotion">
<NewsletterSignupHeader
frequency={frequency}
name={name}
description={description}
illustrationSquare={illustrationSquare}
/>
{children}
</aside>
</>
);
21 changes: 21 additions & 0 deletions dotcom-rendering/src/components/NewsletterSignupCardContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { NewsletterSignupCardProps } from './NewsletterSignupCard';
import { NewsletterSignupCard } from './NewsletterSignupCard';

type Props = NewsletterSignupCardProps;

export const NewsletterSignupCardContainer = ({
name,
frequency,
description,
illustrationSquare,
children,
}: Props) => (
<NewsletterSignupCard
name={name}
frequency={frequency}
description={description}
illustrationSquare={illustrationSquare}
>
{children}
</NewsletterSignupCard>
);
6 changes: 6 additions & 0 deletions dotcom-rendering/src/frontend/schemas/feArticle.json
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@
},
"illustrationCard": {
"type": "string"
},
"illustrationSquare": {
"type": "string"
}
},
"required": [
Expand Down Expand Up @@ -3067,6 +3070,9 @@
},
"illustrationCard": {
"type": "string"
},
"illustrationSquare": {
"type": "string"
}
},
"required": [
Expand Down
6 changes: 5 additions & 1 deletion dotcom-rendering/src/lib/renderElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ export const renderElement = ({
caption={element.caption}
/>
);
case 'model.dotcomrendering.pageElements.NewsletterSignupBlockElement':
case 'model.dotcomrendering.pageElements.NewsletterSignupBlockElement': {
const emailSignUpProps = {
index,
listId: element.newsletter.listId,
Expand All @@ -581,16 +581,20 @@ export const renderElement = ({
frequency: element.newsletter.frequency,
successDescription: element.newsletter.successDescription,
theme: element.newsletter.theme,
illustrationSquare: element.newsletter.illustrationSquare,
idApiUrl: idApiUrl ?? '',
hideNewsletterSignupComponentForSubscribers:
!!switches.hideNewsletterSignupComponentForSubscribers,
showNewNewsletterSignupCard:
!!switches.showNewNewsletterSignupCard,
};
if (isListElement || isTimeline) return null;
return (
<Island priority="feature" defer={{ until: 'visible' }}>
<EmailSignUpWrapper {...emailSignUpProps} />
</Island>
);
}
case 'model.dotcomrendering.pageElements.AdPlaceholderBlockElement':
return renderAds && <AdPlaceholder />;
case 'model.dotcomrendering.pageElements.NumberedTitleBlockElement':
Expand Down
3 changes: 3 additions & 0 deletions dotcom-rendering/src/model/block-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2549,6 +2549,9 @@
},
"illustrationCard": {
"type": "string"
},
"illustrationSquare": {
"type": "string"
}
},
"required": [
Expand Down
3 changes: 3 additions & 0 deletions dotcom-rendering/src/model/newsletter-page-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
},
"illustrationCard": {
"type": "string"
},
"illustrationSquare": {
"type": "string"
}
},
"required": [
Expand Down
20 changes: 20 additions & 0 deletions dotcom-rendering/src/paletteDeclarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7605,6 +7605,26 @@ const paletteColours = {
light: navSearchBarText,
dark: navSearchBarText,
},
'--newsletter-card-background': {
light: () => '#F3F7FF',
dark: () => sourcePalette.brand[100],
},
'--newsletter-card-description': {
light: () => sourcePalette.neutral[20],
dark: () => sourcePalette.neutral[86],
},
'--newsletter-card-divider': {
light: () => sourcePalette.neutral[73],
dark: () => sourcePalette.neutral[46],
},
'--newsletter-card-frequency-tag': {
light: () => sourcePalette.neutral[38],
dark: () => sourcePalette.neutral[73],
},
'--newsletter-card-title': {
light: () => sourcePalette.neutral[7],
dark: () => sourcePalette.neutral[100],
},
'--numbered-list-heading': {
light: numberedListHeadingLight,
dark: numberedListHeadingDark,
Expand Down
1 change: 1 addition & 0 deletions dotcom-rendering/src/types/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,7 @@ export type Newsletter = {
group: string;
regionFocus?: string;
illustrationCard?: string;
illustrationSquare?: string;
};

export type NewsletterLayout = {
Expand Down
Loading