Skip to content

Commit eb6660a

Browse files
authored
Merge pull request #14202 from guardian/jm/new-sign-in-gate
New Sign In Gate Phase 1
2 parents 8b4941c + 9ec01b6 commit eb6660a

File tree

14 files changed

+1129
-26
lines changed

14 files changed

+1129
-26
lines changed

dotcom-rendering/fixtures/manual/sign-in-gate.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ export const mockAuxiaResponseDismissible = {
66
treatmentTrackingId: 'tracking-001',
77
surface: 'signin-gate',
88
treatmentContent: JSON.stringify({
9-
title: 'Register for free and continue reading',
10-
subtitle: "It's still free to read – this is not a paywall",
11-
body: "We're committed to keeping our quality reporting open. By registering and providing us with insight into your preferences, you're helping us to engage with you more deeply.",
12-
first_cta_name: 'Register for free',
9+
title: 'Like uninterrupted reading?\nSo do we. Sign in.',
10+
subtitle:
11+
"Sign in to keep reading. It's free, and we'll bring you right back here in under a minute.",
12+
body: '',
13+
first_cta_name: 'Create a free account',
1314
first_cta_link: 'https://profile.theguardian.com/register',
1415
second_cta_name: "I'll do it later",
1516
}),
@@ -25,10 +26,11 @@ export const mockAuxiaResponseNonDismissible = {
2526
treatmentTrackingId: 'tracking-002',
2627
surface: 'signin-gate',
2728
treatmentContent: JSON.stringify({
28-
title: 'Complete registration to continue reading',
29-
subtitle: 'Registration is required to access this content',
30-
body: 'To continue reading this article and access our quality journalism, please complete your registration.',
31-
first_cta_name: 'Complete registration',
29+
title: 'Like uninterrupted reading?\nSo do we. Sign in.',
30+
subtitle:
31+
"Sign in to keep reading. It's free, and we'll bring you right back here in under a minute.",
32+
body: '',
33+
first_cta_name: 'Create a free account',
3234
first_cta_link: 'https://profile.theguardian.com/register',
3335
second_cta_name: '', // Empty makes it non-dismissible
3436
}),
@@ -44,10 +46,10 @@ export const mockAuxiaResponseLegacy = {
4446
treatmentTrackingId: 'legacy-tracking',
4547
surface: 'signin-gate',
4648
treatmentContent: JSON.stringify({
47-
title: 'Register to continue reading',
49+
title: 'Like uninterrupted reading?\nSo do we. Sign in.',
4850
subtitle: '',
4951
body: '',
50-
first_cta_name: 'Register',
52+
first_cta_name: 'Create a free account',
5153
first_cta_link: 'https://profile.theguardian.com/register',
5254
second_cta_name: 'Not now',
5355
}),
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @file AuthProviderButtons.stories.tsx
3+
* This file was migrated from:
4+
* https://github.com/guardian/gateway/blob/b980d008f91bd1abb108e50de9cdd1c364f37f4d/src/client/components/AuthProviderButtons.stories.tsx
5+
*/
6+
import type { Meta } from '@storybook/react';
7+
import { AuthProviderButtons } from './AuthProviderButtons';
8+
9+
export default {
10+
title: 'Components/AuthProviderButtons',
11+
component: AuthProviderButtons,
12+
parameters: {
13+
layout: 'padded',
14+
},
15+
} as Meta;
16+
17+
export const Default = () => (
18+
<AuthProviderButtons
19+
queryParams={{ returnUrl: 'https://www.theguardian.com/uk/' }}
20+
providers={['social']}
21+
/>
22+
);
23+
24+
export const NativeAppAndroid = () => (
25+
<AuthProviderButtons
26+
queryParams={{ returnUrl: 'https://www.theguardian.com/uk/' }}
27+
isNativeApp="android"
28+
providers={['social']}
29+
/>
30+
);
31+
NativeAppAndroid.storyName = 'Android native app';
32+
33+
export const NativeAppIos = () => (
34+
<AuthProviderButtons
35+
queryParams={{ returnUrl: 'https://www.theguardian.com/uk/' }}
36+
isNativeApp="ios"
37+
providers={['social']}
38+
/>
39+
);
40+
NativeAppIos.storyName = 'iOS native app';
41+
42+
export const WithEmail = () => (
43+
<AuthProviderButtons
44+
queryParams={{ returnUrl: 'https://www.theguardian.com/uk/' }}
45+
providers={['social', 'email']}
46+
/>
47+
);
48+
WithEmail.storyName = 'Default (with email)';
49+
50+
export const NativeAppAndroidWithEmail = () => (
51+
<AuthProviderButtons
52+
queryParams={{ returnUrl: 'https://www.theguardian.com/uk/' }}
53+
isNativeApp="android"
54+
providers={['social', 'email']}
55+
/>
56+
);
57+
NativeAppAndroidWithEmail.storyName = 'Android native app (with email)';
58+
59+
export const NativeAppIosWithEmail = () => (
60+
<AuthProviderButtons
61+
queryParams={{ returnUrl: 'https://www.theguardian.com/uk/' }}
62+
isNativeApp="ios"
63+
providers={['social', 'email']}
64+
/>
65+
);
66+
NativeAppIosWithEmail.storyName = 'iOS native app (with email)';
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/**
2+
* @file AuthProviderButtons.tsx
3+
* This file was migrated from:
4+
* https://github.com/guardian/gateway/blob/b980d008f91bd1abb108e50de9cdd1c364f37f4d/src/client/components/AuthProviderButtons.tsx
5+
*/
6+
import { css } from '@emotion/react';
7+
import { from, palette, remSpace } from '@guardian/source/foundations';
8+
import {
9+
LinkButton,
10+
SvgAppleBrand,
11+
SvgEnvelope,
12+
SvgGoogleBrand,
13+
} from '@guardian/source/react-components';
14+
import React from 'react';
15+
import { buildUrlWithQueryParams } from '../../lib/routeUtils';
16+
import type { IsNativeApp, QueryParams } from './types';
17+
18+
type AuthButtonProvider = 'social' | 'email';
19+
20+
type AuthProviderButtonsProps = {
21+
queryParams: QueryParams;
22+
isNativeApp?: IsNativeApp;
23+
providers: AuthButtonProvider[];
24+
onClick?: (provider: AuthButtonProvider) => void;
25+
};
26+
27+
type AuthProviderButtonProps = {
28+
label: string;
29+
icon: React.ReactElement;
30+
socialProvider: string;
31+
queryParams: QueryParams;
32+
onClick?: (provider: AuthButtonProvider) => void;
33+
};
34+
35+
// The gap between elements in the main section of MinimalLayout.
36+
export const SECTION_GAP = remSpace[3]; // 12px
37+
38+
export const mainSectionStyles = css`
39+
display: flex;
40+
flex-direction: column;
41+
gap: ${SECTION_GAP};
42+
43+
${from.phablet} {
44+
flex-direction: row;
45+
}
46+
`;
47+
48+
type ButtonWidth = 'full' | 'half';
49+
50+
const sharedButtonStyles = (width: ButtonWidth = 'full') => css`
51+
width: 100%;
52+
${from.tablet} {
53+
max-width: ${width === 'full' ? '100%' : '50%'};
54+
}
55+
margin: 0 auto;
56+
justify-content: center;
57+
:disabled {
58+
cursor: not-allowed;
59+
}
60+
`;
61+
62+
export const secondaryButtonStyles = (width: ButtonWidth = 'full') => css`
63+
${sharedButtonStyles(width)}
64+
background-color: ${palette.neutral[100]};
65+
border-color: ${palette.brand[400]};
66+
color: ${palette.brand[400]};
67+
&:hover {
68+
background-color: ${palette.neutral[93]};
69+
}
70+
`;
71+
72+
const appleIconOverrides = css`
73+
svg path {
74+
/* stylelint-disable-next-line declaration-no-important */
75+
fill: ${palette.brand[400]} !important;
76+
}
77+
`;
78+
79+
const SocialButton = ({
80+
label,
81+
icon,
82+
socialProvider,
83+
queryParams,
84+
onClick,
85+
}: AuthProviderButtonProps) => {
86+
return (
87+
<>
88+
<LinkButton
89+
priority="tertiary"
90+
cssOverrides={secondaryButtonStyles()}
91+
icon={icon}
92+
href={buildUrlWithQueryParams(
93+
'https://profile.theguardian.com/signin/:social',
94+
{
95+
social: socialProvider,
96+
},
97+
queryParams,
98+
)}
99+
onClick={() => onClick?.(socialProvider as AuthButtonProvider)}
100+
data-cy={`${socialProvider}-sign-in-button`}
101+
data-link-name={`${socialProvider}-social-button`}
102+
data-testid="sign-in-gate-main_signin"
103+
data-ignore="global-link-styling"
104+
>
105+
{authProviderButtonLabel(label)}
106+
</LinkButton>
107+
</>
108+
);
109+
};
110+
111+
const authProviderButtonLabel = (label: string) => {
112+
// We don't capitalize 'email', but we do capitalize 'google' and 'apple'
113+
const capitalisedLabel =
114+
label === 'email'
115+
? label
116+
: label.charAt(0).toUpperCase() + label.slice(1);
117+
118+
return `Sign in with ${capitalisedLabel}`;
119+
};
120+
121+
const socialButtonIcon = (socialProvider: string): React.ReactElement => {
122+
switch (socialProvider) {
123+
case 'google':
124+
return <SvgGoogleBrand />;
125+
case 'apple':
126+
return (
127+
<div css={appleIconOverrides}>
128+
<SvgAppleBrand />
129+
</div>
130+
);
131+
default:
132+
// null is the officially recommended way to return nothing from a React component,
133+
// but LinkButton doesn't accept it, so we return an empty JSX element instead
134+
// cf. https://stackoverflow.com/a/47192387
135+
return <></>;
136+
}
137+
};
138+
139+
const getButtonOrder = (isNativeApp?: IsNativeApp): string[] => {
140+
switch (isNativeApp) {
141+
case 'ios':
142+
return ['apple', 'google'];
143+
case 'android':
144+
return ['google', 'apple'];
145+
default:
146+
return ['google', 'apple'];
147+
}
148+
};
149+
150+
export const AuthProviderButtons = ({
151+
queryParams,
152+
isNativeApp,
153+
providers,
154+
onClick,
155+
}: AuthProviderButtonsProps) => {
156+
const buttonOrder = getButtonOrder(isNativeApp);
157+
return (
158+
<div css={mainSectionStyles}>
159+
{providers.includes('social') &&
160+
buttonOrder.map((socialProvider) => (
161+
<SocialButton
162+
key={socialProvider}
163+
label={socialProvider}
164+
icon={socialButtonIcon(socialProvider)}
165+
socialProvider={socialProvider}
166+
queryParams={queryParams}
167+
onClick={onClick}
168+
/>
169+
))}
170+
{providers.includes('email') && (
171+
<LinkButton
172+
icon={<SvgEnvelope />}
173+
cssOverrides={secondaryButtonStyles()}
174+
priority="tertiary"
175+
href={buildUrlWithQueryParams(
176+
'https://profile.theguardian.com/register/email',
177+
{},
178+
queryParams,
179+
)}
180+
onClick={() => onClick?.('email')}
181+
data-testid="sign-in-gate-main_signin"
182+
data-ignore="global-link-styling"
183+
>
184+
{authProviderButtonLabel('email')}
185+
</LinkButton>
186+
)}
187+
</div>
188+
);
189+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @file AuthProviderButtons.tsx
3+
* This file was migrated from:
4+
* https://github.com/guardian/gateway/blob/b980d008f91bd1abb108e50de9cdd1c364f37f4d/src/shared/model/QueryParams.ts
5+
*/
6+
export type Stringifiable = string | boolean | number | null | undefined;
7+
export type StringifiableRecord = Record<
8+
string,
9+
Stringifiable | readonly Stringifiable[]
10+
>;
11+
12+
export interface TrackingQueryParams {
13+
ref?: string;
14+
refViewId?: string;
15+
componentEventParams?: string;
16+
listName?: string;
17+
}
18+
19+
export type ValidClientId =
20+
| 'members'
21+
| 'recurringContributions'
22+
| 'jobs'
23+
| 'comments'
24+
| 'subscriptions';
25+
26+
export interface PersistableQueryParams
27+
extends TrackingQueryParams,
28+
StringifiableRecord {
29+
returnUrl: string;
30+
clientId?: ValidClientId;
31+
fromURI?: string;
32+
appClientId?: string;
33+
useOktaClassic?: boolean;
34+
usePasswordSignIn?: boolean;
35+
useSetPassword?: boolean;
36+
}
37+
38+
export interface QueryParams
39+
extends PersistableQueryParams,
40+
StringifiableRecord {
41+
emailVerified?: boolean;
42+
emailSentSuccess?: boolean;
43+
csrfError?: boolean;
44+
recaptchaError?: boolean;
45+
encryptedEmail?: string;
46+
error?: string;
47+
error_description?: string;
48+
maxAge?: number;
49+
signInEmail?: string;
50+
}
51+
52+
export type IsNativeApp = 'android' | 'ios' | undefined;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @file ExternalLink.tsx
3+
* This file was migrated from:
4+
* https://github.com/guardian/gateway/blob/b980d008f91bd1abb108e50de9cdd1c364f37f4d/src/client/components/ExternalLink.tsx
5+
*/
6+
import type {
7+
LinkButtonProps,
8+
LinkProps,
9+
} from '@guardian/source/react-components';
10+
import { LinkButton } from '@guardian/source/react-components';
11+
import { ThemedLink } from '../ThemedLink/ThemedLink';
12+
13+
export const ExternalLink = (props: LinkProps) => (
14+
<ThemedLink {...props} rel="noopener noreferrer" />
15+
);
16+
17+
export const ExternalLinkButton = (props: LinkButtonProps) => (
18+
<LinkButton {...props} rel="noopener noreferrer" />
19+
);

0 commit comments

Comments
 (0)