WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 8743475

Browse files
author
Richard Bangay
committed
show error message on passcode input field error when the form is submitted via the onSubmit clientside handler.
fix some padding issues use postMessage to send link click info to the parent page when recaptcha privacy policy or terms linked are clicked (only if the site is being iframed)
1 parent 6c8bb58 commit 8743475

File tree

10 files changed

+101
-110
lines changed

10 files changed

+101
-110
lines changed

src/client/components/MainForm.tsx

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import {
1919
RecaptchaWrapper,
2020
UseRecaptchaReturnValue,
2121
} from '@/client/lib/hooks/useRecaptcha';
22-
import { CaptchaErrors, GatewayError } from '@/shared/model/Errors';
22+
import {
23+
CaptchaErrors,
24+
GatewayError,
25+
SubmitHandlerErrorObject,
26+
} from '@/shared/model/Errors';
2327
import { DetailedRecaptchaError } from '@/client/components/DetailedRecaptchaError';
2428
import { RefTrackingFormFields } from '@/client/components/RefTrackingFormFields';
2529
import { trackFormFocusBlur, trackFormSubmit } from '@/client/lib/ophan';
@@ -37,10 +41,6 @@ import locations from '@/shared/lib/locations';
3741
import { GatewayErrorSummary } from '@/client/components/GatewayErrorSummary';
3842
import { NoScript } from './NoScript';
3943

40-
interface SubmitHandlerErrorObject {
41-
errorOccurred: boolean;
42-
}
43-
4444
type TermsStyle = 'primary' | 'secondary';
4545

4646
export interface MainFormProps {
@@ -65,10 +65,11 @@ export interface MainFormProps {
6565
hasJobsTerms?: boolean;
6666
onSubmit?: (
6767
e: React.FormEvent<HTMLFormElement>,
68-
) => SubmitHandlerErrorObject | Promise<void> | undefined;
68+
) => SubmitHandlerErrorObject | Promise<SubmitHandlerErrorObject> | undefined;
6969
onInvalid?: React.FormEventHandler<HTMLFormElement> | undefined;
7070
formTrackingName?: string;
7171
disableOnSubmit?: boolean;
72+
isIframed?: boolean;
7273
largeFormMarginTop?: boolean;
7374
displayInline?: boolean;
7475
submitButtonLink?: boolean;
@@ -112,6 +113,7 @@ export const MainForm = ({
112113
onInvalid,
113114
formTrackingName,
114115
disableOnSubmit = false,
116+
isIframed = false,
115117
formErrorMessageFromParent,
116118
formErrorContextFromParent,
117119
displayInline = false,
@@ -147,7 +149,10 @@ export const MainForm = ({
147149
const showFormLevelReportUrl = !!formLevelErrorContext;
148150

149151
const submitHandlerResponseIsErrorObject = (
150-
submitHandler: SubmitHandlerErrorObject | Promise<void> | undefined,
152+
submitHandler:
153+
| SubmitHandlerErrorObject
154+
| Promise<SubmitHandlerErrorObject>
155+
| undefined,
151156
): submitHandler is SubmitHandlerErrorObject => {
152157
if (!submitHandler) {
153158
return false;
@@ -166,26 +171,29 @@ export const MainForm = ({
166171
if (formTrackingName) {
167172
trackFormSubmit(formTrackingName);
168173
}
174+
setIsFormDisabled(disableOnSubmit);
169175

170-
const submitHandler = onSubmit?.(event);
171-
const errorInSubmitHandler =
172-
submitHandlerResponseIsErrorObject(submitHandler);
176+
void (async () => {
177+
const submitHandler = await onSubmit?.(event);
178+
const errorInSubmitHandler =
179+
submitHandlerResponseIsErrorObject(submitHandler);
173180

174-
if (disableOnSubmit) {
175-
if (errorInSubmitHandler === undefined) {
176-
if (!isFormDisabled) {
177-
setIsFormDisabled(true);
181+
if (disableOnSubmit) {
182+
if (errorInSubmitHandler === undefined) {
183+
if (!isFormDisabled) {
184+
setIsFormDisabled(true);
185+
}
186+
} else {
187+
const formSubmitSuccess = !errorInSubmitHandler;
188+
setIsFormDisabled(formSubmitSuccess);
178189
}
179-
} else {
180-
const formSubmitSuccess = !errorInSubmitHandler;
181-
setIsFormDisabled(formSubmitSuccess);
182190
}
183-
}
184191

185-
if (recaptchaEnabled && !recaptchaState?.token) {
186-
event.preventDefault();
187-
recaptchaState?.executeCaptcha();
188-
}
192+
if (recaptchaEnabled && !recaptchaState?.token) {
193+
event.preventDefault();
194+
recaptchaState?.executeCaptcha();
195+
}
196+
})();
189197
},
190198
[
191199
formTrackingName,
@@ -299,7 +307,9 @@ export const MainForm = ({
299307
<InformationBoxText>{specificTermsItem}</InformationBoxText>
300308
);
301309
})}
302-
{recaptchaEnabled && !hideRecaptchaMessage && <RecaptchaTerms />}
310+
{recaptchaEnabled && !hideRecaptchaMessage && (
311+
<RecaptchaTerms isIframed={isIframed} />
312+
)}
303313
</BoxContainer>
304314
)}
305315
</>

src/client/components/Terms.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,39 @@ export const JobsTerms = () => (
3232
</InformationBoxText>
3333
);
3434

35-
export const RecaptchaTerms = () => (
35+
const handleIframedLinkClick =
36+
(linkID: string) => (event: React.MouseEvent<HTMLAnchorElement>) => {
37+
event.preventDefault();
38+
window.parent.postMessage(
39+
{
40+
context: 'supporterOnboarding',
41+
type: 'iframedLinkClicked',
42+
value: linkID,
43+
},
44+
'*',
45+
);
46+
};
47+
//export const RecaptchaTerms = (isIframed?: boolean = false) => (
48+
export const RecaptchaTerms = ({
49+
isIframed = false,
50+
}: {
51+
isIframed?: boolean;
52+
}) => (
3653
<InformationBoxText>
3754
This service is protected by reCAPTCHA and the Google{' '}
38-
<ExternalLink href="https://policies.google.com/privacy">
55+
<ExternalLink
56+
href="https://policies.google.com/privacy"
57+
onClick={
58+
isIframed ? handleIframedLinkClick('recaptchaPrivacyPolicy') : undefined
59+
}
60+
>
3961
privacy policy
4062
</ExternalLink>{' '}
4163
and{' '}
42-
<ExternalLink href="https://policies.google.com/terms">
64+
<ExternalLink
65+
href="https://policies.google.com/terms"
66+
onClick={isIframed ? handleIframedLinkClick('recaptchaTerms') : undefined}
67+
>
4368
terms of service
4469
</ExternalLink>{' '}
4570
apply.

src/client/layouts/MinimalLayout.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
headlineBold28,
77
headlineMedium28,
88
remSpace,
9-
space,
109
} from '@guardian/source/foundations';
1110
import useClientState from '@/client/lib/hooks/useClientState';
1211
import { SuccessSummary } from '@guardian/source-development-kitchen/react-components';
@@ -30,7 +29,6 @@ import { GatewayErrorSummary } from '@/client/components/GatewayErrorSummary';
3029
interface MinimalLayoutProps {
3130
children?: React.ReactNode;
3231
wide?: boolean;
33-
showGuardianHeader?: boolean;
3432
pageHeader: string;
3533
subduedHeadingStyle?: boolean;
3634
leadText?: React.ReactNode;
@@ -57,7 +55,6 @@ const mainStyles = (wide: boolean) => css`
5755
`;
5856

5957
const mainStylesStretch = css`
60-
padding: ${space[3]}px ${space[3]}px ${space[6]}px;
6158
display: flex;
6259
flex-direction: column;
6360
gap: ${CONTAINER_GAP};
@@ -78,7 +75,6 @@ const pageHeaderStyles = (subduedHeadingStyle: boolean) => css`
7875
export const MinimalLayout = ({
7976
children,
8077
wide = false,
81-
showGuardianHeader = true,
8278
pageHeader,
8379
subduedHeadingStyle = false,
8480
leadText,
@@ -103,6 +99,8 @@ export const MinimalLayout = ({
10399
return <Theme />;
104100
};
105101

102+
const amIIframed = overrideTheme?.includes('iframe');
103+
106104
const ConditionalIframeThemeWrapper = ({
107105
children,
108106
}: {
@@ -117,8 +115,8 @@ export const MinimalLayout = ({
117115
return (
118116
<>
119117
{getTheme()}
120-
{showGuardianHeader && <MinimalHeader />}
121-
<main css={showGuardianHeader ? mainStyles(wide) : mainStylesStretch}>
118+
{!amIIframed && <MinimalHeader />}
119+
<main css={amIIframed ? mainStylesStretch : mainStyles(wide)}>
122120
{imageId && <MinimalLayoutImage id={imageId} />}
123121
<ConditionalIframeThemeWrapper>
124122
{pageHeader && (

src/client/pages/IframedPasscodeEmailSent.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { EmailSentInformationBox } from '@/client/components/EmailSentInformatio
99
import { EmailSentProps } from '@/client/pages/EmailSent';
1010
import useClientState from '../lib/hooks/useClientState';
1111
import { record, trackFormSubmit } from '../lib/ophan';
12+
import { SubmitHandlerErrorObject } from '@/shared/model/Errors';
1213

1314
type Props = {
1415
passcodeAction: string;
@@ -84,6 +85,9 @@ export const IframedPasscodeEmailSent = ({
8485
const formRef = useRef<HTMLFormElement>(null);
8586
const clientState = useClientState();
8687
const formAction = `${passcodeAction}${queryString}`;
88+
const [clientErrorOverride, setClientErrorOverride] = useState<FieldError[]>(
89+
[],
90+
);
8791

8892
useEffect(() => {
8993
// we only want this to run in the browser as window is not
@@ -106,7 +110,9 @@ export const IframedPasscodeEmailSent = ({
106110
}
107111
}, [timeUntilTokenExpiry, expiredPage, queryString]);
108112

109-
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
113+
const handleFormSubmit = async (
114+
e: React.FormEvent<HTMLFormElement>,
115+
): Promise<SubmitHandlerErrorObject> => {
110116
e.preventDefault();
111117
try {
112118
const csrfToken = clientState.csrf?.token;
@@ -144,6 +150,16 @@ export const IframedPasscodeEmailSent = ({
144150
throw new Error('response status error');
145151
}
146152
const userStatus = deriveStatusFromResponseUrl(response.url);
153+
setClientErrorOverride(
154+
userStatus === 'authError'
155+
? [
156+
{
157+
field: 'code',
158+
message: 'Invalid code',
159+
},
160+
]
161+
: [],
162+
);
147163

148164
window.parent.postMessage(
149165
{
@@ -155,10 +171,12 @@ export const IframedPasscodeEmailSent = ({
155171
);
156172

157173
trackFormSubmit('passcode-submit');
174+
return { errorOccurred: userStatus === 'authError' };
158175
} catch (e) {
159176
record({
160177
experiences: 'passcode-submit-failure',
161178
});
179+
return { errorOccurred: true };
162180
}
163181
};
164182

@@ -170,7 +188,6 @@ export const IframedPasscodeEmailSent = ({
170188
}
171189
pageHeader={text.title}
172190
shortRequestId={shortRequestId}
173-
showGuardianHeader={false}
174191
subduedHeadingStyle={true}
175192
overrideTheme="iframe-light"
176193
>
@@ -188,12 +205,15 @@ export const IframedPasscodeEmailSent = ({
188205
submitButtonText={text.submitButtonText}
189206
onSubmit={handleFormSubmit}
190207
disableOnSubmit={true}
208+
isIframed={true}
191209
shortRequestId={shortRequestId}
192210
formRef={formRef}
193211
>
194212
<PasscodeInput
195213
passcode={passcode}
196-
fieldErrors={fieldErrors}
214+
fieldErrors={
215+
clientErrorOverride.length ? clientErrorOverride : fieldErrors
216+
}
197217
label={text.passcodeInputLabel}
198218
formRef={formRef}
199219
autoFocus

0 commit comments

Comments
 (0)