Skip to content

Commit 65f11b5

Browse files
authored
Merge branch 'main' into jm/feat-sign-in-gate-v2
2 parents 97ca352 + ee336a4 commit 65f11b5

File tree

11 files changed

+355
-154
lines changed

11 files changed

+355
-154
lines changed

dotcom-rendering/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@
137137
"jest": "29.7.0",
138138
"jest-environment-jsdom": "29.7.0",
139139
"jsdom": "22.1.0",
140-
"load-json-file": "6.2.0",
141140
"lodash.debounce": "4.0.8",
142141
"log4js": "6.9.1",
143142
"lz-string": "1.5.0",

dotcom-rendering/scripts/test/build-check.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@
55
// 1. That the manifest files are output
66
// 2. That the manifest files contain at least the entry points under the expected property
77

8+
const { readFile } = require('node:fs').promises;
89
const find = require('find');
9-
const loadJsonFile = require('load-json-file');
1010
const { BUILD_VARIANT } = require('../../webpack/bundles');
1111

12+
/**
13+
* Loads a JSON file.
14+
* Inspired by https://github.com/sindresorhus/load-json-file/blob/de8256b9010db73c75a1e2036ff96025e94c0b6e/index.js#L6
15+
* @param {string} filePath The path to the JSON file.
16+
* @returns {Promise<Object>} The parsed JSON object.
17+
*/
18+
async function loadJsonFile(filePath) {
19+
const buffer = await readFile(filePath);
20+
// Unlike `buffer.toString()` and `fs.readFile(path, 'utf8')`, `TextDecoder` will remove BOM.
21+
return JSON.parse(new TextDecoder().decode(buffer));
22+
}
23+
1224
const errorAndThrow = (error) => {
1325
console.error(error);
1426
throw new Error(error);

dotcom-rendering/src/components/Masthead/Masthead.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Props = {
2222
hasPageSkin?: boolean;
2323
hasPageSkinContentSelfConstrain?: boolean;
2424
pageId?: string;
25+
wholePictureLogoSwitch?: boolean;
2526
};
2627

2728
/**
@@ -56,6 +57,7 @@ export const Masthead = ({
5657
hasPageSkin = false,
5758
hasPageSkinContentSelfConstrain = false,
5859
pageId,
60+
wholePictureLogoSwitch = false,
5961
}: Props) => (
6062
<header data-component="header">
6163
<Section
@@ -112,6 +114,7 @@ export const Masthead = ({
112114
showSlimNav={showSlimNav}
113115
hasPageSkin={hasPageSkin}
114116
pageId={pageId}
117+
wholePictureLogoSwitch={wholePictureLogoSwitch}
115118
/>
116119
</Island>
117120
</header>
Lines changed: 147 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,152 @@
1-
/**
2-
* @file
3-
* This file was largely copied from src/components/Logo.tsx
4-
*/
51
import { css } from '@emotion/react';
6-
import { visuallyHidden } from '@guardian/source/foundations';
7-
import { SvgGuardianLogo } from '@guardian/source/react-components';
2+
import { from, space, visuallyHidden } from '@guardian/source/foundations';
3+
import { Hide, SvgGuardianLogo } from '@guardian/source/react-components';
84
import { nestedOphanComponents } from '../../../lib/ophan-helpers';
95
import { palette } from '../../../palette';
6+
import TheWholePictureGuardianLogoSmall from '../../../static/icons/the-guardian-whole-picture-logo-small.svg';
7+
import TheWholePictureGuardianLogo from '../../../static/icons/the-guardian-whole-picture-logo.svg';
108

11-
export const Logo = () => (
12-
<a href="/" data-link-name={nestedOphanComponents('header', 'logo')}>
13-
<span
14-
css={css`
15-
${visuallyHidden};
16-
`}
17-
>
18-
The Guardian - Back to home
19-
</span>
20-
<SvgGuardianLogo textColor={palette('--masthead-nav-link-text')} />
21-
</a>
9+
const gridMainColumn = css`
10+
grid-column: main-column-start / main-column-end;
11+
`;
12+
13+
const veggieBurgerDiameter = 40;
14+
15+
const logoStyles = css`
16+
${gridMainColumn}
17+
grid-row: 1;
18+
position: relative;
19+
display: flex;
20+
justify-self: end;
21+
align-self: end;
22+
margin-top: ${space[2]}px;
23+
margin-bottom: 6px;
24+
right: ${veggieBurgerDiameter + space[3]}px;
25+
26+
${from.mobileMedium} {
27+
right: 0;
28+
}
29+
${from.mobileLandscape} {
30+
margin-bottom: ${space[2]}px;
31+
}
32+
33+
svg {
34+
width: 152px;
35+
${from.mobileMedium} {
36+
width: 207px;
37+
}
38+
${from.tablet} {
39+
width: 252px;
40+
}
41+
${from.desktop} {
42+
width: 291px;
43+
}
44+
}
45+
`;
46+
47+
const logoStylesFromLeftCol = css`
48+
svg {
49+
${from.leftCol} {
50+
width: 324px;
51+
}
52+
}
53+
`;
54+
55+
const theWholePictureStyles = css`
56+
margin-bottom: 2px;
57+
${from.tablet} {
58+
margin-top: 10px;
59+
margin-bottom: 6px;
60+
}
61+
62+
svg {
63+
height: auto;
64+
width: 173px;
65+
${from.tablet} {
66+
width: 246px;
67+
}
68+
}
69+
`;
70+
71+
const slimNavLogoOverrides = css`
72+
position: relative;
73+
margin-top: ${space[2]}px;
74+
margin-bottom: ${space[2]}px;
75+
right: ${veggieBurgerDiameter + 6}px;
76+
77+
${from.mobileLandscape} {
78+
margin-top: ${space[1]}px;
79+
margin-bottom: ${space[2]}px;
80+
}
81+
${from.tablet} {
82+
right: ${space[8]}px;
83+
}
84+
${from.desktop} {
85+
right: ${space[10]}px;
86+
}
87+
svg {
88+
width: 130px;
89+
${from.tablet} {
90+
width: 86px;
91+
}
92+
${from.desktop} {
93+
width: 130px;
94+
}
95+
${from.leftCol} {
96+
width: 140px;
97+
}
98+
${from.wide} {
99+
width: 145px;
100+
}
101+
}
102+
`;
103+
104+
type Props = {
105+
/**
106+
* We are running a campaign in the US called "The Whole Picture"
107+
* We will use a different logo for the US edition for the duration of this campaign.
108+
*/
109+
showWholePictureLogo: boolean;
110+
hasPageSkin: boolean;
111+
showSlimNav: boolean;
112+
};
113+
114+
export const Logo = ({
115+
showWholePictureLogo,
116+
hasPageSkin,
117+
showSlimNav,
118+
}: Props) => (
119+
<div
120+
css={[
121+
logoStyles,
122+
!hasPageSkin && logoStylesFromLeftCol,
123+
showWholePictureLogo && theWholePictureStyles,
124+
showSlimNav && slimNavLogoOverrides,
125+
]}
126+
>
127+
<a href="/" data-link-name={nestedOphanComponents('header', 'logo')}>
128+
<span
129+
css={css`
130+
${visuallyHidden};
131+
`}
132+
>
133+
The Guardian - Back to home
134+
</span>
135+
136+
{showWholePictureLogo ? (
137+
<>
138+
<Hide from="tablet">
139+
<TheWholePictureGuardianLogoSmall />
140+
</Hide>
141+
<Hide until="tablet">
142+
<TheWholePictureGuardianLogo />
143+
</Hide>
144+
</>
145+
) : (
146+
<SvgGuardianLogo
147+
textColor={palette('--masthead-nav-link-text')}
148+
/>
149+
)}
150+
</a>
151+
</div>
22152
);

dotcom-rendering/src/components/SecureSignup.importable.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { css } from '@emotion/react';
22
import { isString } from '@guardian/libs';
33
import type { ComponentEvent, TAction } from '@guardian/ophan-tracker-js';
4-
import { space, until } from '@guardian/source/foundations';
4+
import { space, textSans14, until } from '@guardian/source/foundations';
55
import {
66
Button,
7+
Checkbox,
8+
CheckboxGroup,
79
InlineError,
810
InlineSuccess,
911
Link,
@@ -19,6 +21,7 @@ import { useEffect, useRef, useState } from 'react';
1921
import ReactGoogleRecaptcha from 'react-google-recaptcha';
2022
import { submitComponentEvent } from '../client/ophan/ophan';
2123
import { lazyFetchEmailWithTimeout } from '../lib/fetchEmail';
24+
import { useIsSignedIn } from '../lib/useAuthStatus';
2225
import { palette } from '../palette';
2326
import type { RenderingTarget } from '../types/renderingTarget';
2427
import { useConfig } from './ConfigContext';
@@ -96,6 +99,13 @@ const errorContainerStyles = css`
9699
}
97100
`;
98101

102+
const optInCheckboxTextSmall = css`
103+
label > div {
104+
${textSans14};
105+
line-height: 16px;
106+
}
107+
`;
108+
99109
const ErrorMessageWithAdvice = ({ text }: { text?: string }) => (
100110
<InlineError>
101111
<span>
@@ -124,6 +134,7 @@ const buildFormData = (
124134
emailAddress: string,
125135
newsletterId: string,
126136
token: string,
137+
marketingOptIn?: boolean,
127138
): FormData => {
128139
const pageRef = window.location.origin + window.location.pathname;
129140
const refViewId = window.guardian.ophan?.pageViewId ?? '';
@@ -139,6 +150,10 @@ const buildFormData = (
139150
formData.append('g-recaptcha-response', token); // TO DO - PR on form handlers - is the token verified?
140151
}
141152

153+
if (marketingOptIn !== undefined) {
154+
formData.append('marketing', marketingOptIn ? 'true' : 'false');
155+
}
156+
142157
return formData;
143158
};
144159

@@ -267,6 +282,16 @@ export const SecureSignup = ({
267282
const [errorMessage, setErrorMessage] = useState<string | undefined>(
268283
undefined,
269284
);
285+
const [marketingOptIn, setMarketingOptIn] = useState<boolean | undefined>(
286+
undefined,
287+
);
288+
const isSignedIn = useIsSignedIn();
289+
290+
useEffect(() => {
291+
if (isSignedIn !== 'Pending' && !isSignedIn) {
292+
setMarketingOptIn(true);
293+
}
294+
}, [isSignedIn]);
270295

271296
useEffect(() => {
272297
setCaptchaSiteKey(window.guardian.config.page.googleRecaptchaSiteKey);
@@ -282,9 +307,17 @@ export const SecureSignup = ({
282307
const emailAddress: string = input?.value ?? '';
283308

284309
sendTracking(newsletterId, 'form-submission', renderingTarget, abTest);
310+
311+
const formData = buildFormData(
312+
emailAddress,
313+
newsletterId,
314+
token,
315+
marketingOptIn,
316+
);
317+
285318
const response = await postFormData(
286319
window.guardian.config.page.ajaxUrl + '/email',
287-
buildFormData(emailAddress, newsletterId, token),
320+
formData,
288321
);
289322

290323
// The response body could be accessed with await response.text()
@@ -383,6 +416,23 @@ export const SecureSignup = ({
383416
type="email"
384417
value={signedInUserEmail}
385418
/>
419+
{isSignedIn === false && (
420+
<CheckboxGroup
421+
name="marketing-preferences"
422+
label="Marketing preferences"
423+
hideLabel={true}
424+
cssOverrides={optInCheckboxTextSmall}
425+
>
426+
<Checkbox
427+
label="Get updates about our journalism and ways to support and enjoy our work."
428+
value="marketing-opt-in"
429+
checked={marketingOptIn}
430+
onChange={(e) =>
431+
setMarketingOptIn(e.target.checked)
432+
}
433+
/>
434+
</CheckboxGroup>
435+
)}
386436
<Button onClick={handleClick} size="small" type="submit">
387437
Sign up
388438
</Button>

0 commit comments

Comments
 (0)