From 25729dbefed65945096711be7cc2fd553b657f5f Mon Sep 17 00:00:00 2001 From: George Richmond Date: Tue, 26 May 2026 11:42:25 +0100 Subject: [PATCH] Add isSignedIn parameter and expand on input field to newsletter signup tracking events --- .../NewsletterSignupCardContainer.tsx | 13 +++-- .../NewsletterSignupForm.island.test.tsx | 4 ++ .../src/components/SecureSignup.island.tsx | 50 +++++++++++++++++-- .../src/lib/newsletterSignupTracking.ts | 2 + .../src/lib/useNewsletterSignupForm.ts | 43 +++++++++++++--- 5 files changed, 95 insertions(+), 17 deletions(-) diff --git a/dotcom-rendering/src/components/NewsletterSignupCardContainer.tsx b/dotcom-rendering/src/components/NewsletterSignupCardContainer.tsx index c7fc196ff71..c3e4d841eab 100644 --- a/dotcom-rendering/src/components/NewsletterSignupCardContainer.tsx +++ b/dotcom-rendering/src/components/NewsletterSignupCardContainer.tsx @@ -20,18 +20,20 @@ const sendPreviewTracking = ({ eventDescription, renderingTarget, renderUrl, + isSignedIn, }: { identityName: string; eventDescription: PreviewEventDescription; renderingTarget: RenderingTarget; renderUrl: string; + isSignedIn?: boolean | 'Pending'; }) => { sendNewsletterSignupEvent({ action: eventDescription === 'preview-open' ? 'EXPAND' : 'CLOSE', identityName, componentId: NEWSLETTER_SIGNUP_COMPONENT_ID.variant(identityName), renderingTarget, - value: { eventDescription, renderUrl }, + value: { eventDescription, renderUrl, isSignedIn }, }); }; @@ -90,12 +92,13 @@ export const NewsletterSignupCardContainer = ({ eventDescription: 'preview-open', renderingTarget, renderUrl, + isSignedIn, }); } return true; }); - }, [identityName, renderingTarget, renderUrl]); + }, [identityName, isSignedIn, renderingTarget, renderUrl]); const trackPreviewLinkOpen = useCallback(() => { if (!renderUrl) { @@ -107,8 +110,9 @@ export const NewsletterSignupCardContainer = ({ eventDescription: 'preview-open', renderingTarget, renderUrl, + isSignedIn, }); - }, [identityName, renderingTarget, renderUrl]); + }, [identityName, isSignedIn, renderingTarget, renderUrl]); const closePreview = useCallback(() => { setIsPreviewOpen((isOpen) => { @@ -118,12 +122,13 @@ export const NewsletterSignupCardContainer = ({ eventDescription: 'preview-close', renderingTarget, renderUrl, + isSignedIn, }); } return false; }); - }, [identityName, renderingTarget, renderUrl]); + }, [identityName, isSignedIn, renderingTarget, renderUrl]); const previewAction = hasPreviewUrl ? renderingTarget === 'Apps' diff --git a/dotcom-rendering/src/components/NewsletterSignupForm.island.test.tsx b/dotcom-rendering/src/components/NewsletterSignupForm.island.test.tsx index be56b95f2df..20425e0e882 100644 --- a/dotcom-rendering/src/components/NewsletterSignupForm.island.test.tsx +++ b/dotcom-rendering/src/components/NewsletterSignupForm.island.test.tsx @@ -186,6 +186,7 @@ describe('NewsletterSignupForm', () => { expect(params.get('browserId')).toBe('test-browser-id'); expectTrackedEventDescriptions([ + 'email-input-focused', 'click-button', 'open-captcha', 'captcha-passed', @@ -368,6 +369,7 @@ describe('NewsletterSignupForm', () => { }); expectTrackedEventDescriptions([ + 'email-input-focused', 'click-button', 'open-captcha', 'captcha-passed', @@ -402,6 +404,7 @@ describe('NewsletterSignupForm', () => { expect(global.fetch).not.toHaveBeenCalled(); expectTrackedEventDescriptions([ + 'email-input-focused', 'click-button', 'open-captcha', 'captcha-not-passed', @@ -433,6 +436,7 @@ describe('NewsletterSignupForm', () => { expect(global.fetch).not.toHaveBeenCalled(); expectTrackedEventDescriptions([ + 'email-input-focused', 'click-button', 'open-captcha', 'captcha-load-error', diff --git a/dotcom-rendering/src/components/SecureSignup.island.tsx b/dotcom-rendering/src/components/SecureSignup.island.tsx index d6975debab0..dcc97d8a73b 100644 --- a/dotcom-rendering/src/components/SecureSignup.island.tsx +++ b/dotcom-rendering/src/components/SecureSignup.island.tsx @@ -208,6 +208,7 @@ const sendTracking = ( newsletterId: string, eventDescription: NewsletterEventDescription, renderingTarget: RenderingTarget, + isSignedIn: boolean | 'Pending', abTest?: AbTest, ): void => { sendNewsletterSignupEvent({ @@ -215,7 +216,7 @@ const sendTracking = ( identityName: newsletterId, componentId: NEWSLETTER_SIGNUP_COMPONENT_ID.control(newsletterId), renderingTarget, - value: { eventDescription }, + value: { eventDescription, isSignedIn }, abTest, }); }; @@ -275,7 +276,13 @@ export const SecureSignup = ({ document.querySelector('input[type="email"]') ?? null; const emailAddress: string = input?.value ?? ''; - sendTracking(newsletterId, 'form-submission', renderingTarget, abTest); + sendTracking( + newsletterId, + 'form-submission', + renderingTarget, + isSignedIn, + abTest, + ); const formData = buildFormData( emailAddress, @@ -305,6 +312,7 @@ export const SecureSignup = ({ newsletterId, response.ok ? 'submission-confirmed' : 'submission-failed', renderingTarget, + isSignedIn, abTest, ); }; @@ -320,6 +328,7 @@ export const SecureSignup = ({ newsletterId, 'captcha-load-error', renderingTarget, + isSignedIn, abTest, ); setErrorMessage(`Sorry, the reCAPTCHA failed to load.`); @@ -332,11 +341,18 @@ export const SecureSignup = ({ newsletterId, 'captcha-not-passed', renderingTarget, + isSignedIn, abTest, ); return; } - sendTracking(newsletterId, 'captcha-passed', renderingTarget, abTest); + sendTracking( + newsletterId, + 'captcha-passed', + renderingTarget, + isSignedIn, + abTest, + ); setIsWaitingForResponse(true); submitForm(token).catch((error) => { console.error(error); @@ -344,6 +360,7 @@ export const SecureSignup = ({ newsletterId, 'form-submit-error', renderingTarget, + isSignedIn, abTest, ); setErrorMessage(`Sorry, there was an error signing you up.`); @@ -352,7 +369,23 @@ export const SecureSignup = ({ }; const handleClick = (): void => { - sendTracking(newsletterId, 'click-button', renderingTarget, abTest); + sendTracking( + newsletterId, + 'click-button', + renderingTarget, + isSignedIn, + abTest, + ); + }; + + const handleEmailFocus = (): void => { + sendTracking( + newsletterId, + 'email-input-focused', + renderingTarget, + isSignedIn, + abTest, + ); }; const handleSubmit = (event: FormEvent): void => { @@ -361,7 +394,13 @@ export const SecureSignup = ({ return; } setErrorMessage(undefined); - sendTracking(newsletterId, 'open-captcha', renderingTarget, abTest); + sendTracking( + newsletterId, + 'open-captcha', + renderingTarget, + isSignedIn, + abTest, + ); recaptchaRef.current?.execute(); }; @@ -387,6 +426,7 @@ export const SecureSignup = ({ type="email" value={userEmail ?? ''} onChange={(e) => setUserEmail(e.target.value)} + onFocus={handleEmailFocus} /> {isSignedIn === false && ( { sendNewsletterSignupEvent({ @@ -105,7 +106,7 @@ const sendTracking = ( identityName: newsletterId, componentId: NEWSLETTER_SIGNUP_COMPONENT_ID.variant(newsletterId), renderingTarget, - value: { eventDescription }, + value: { eventDescription, isSignedIn }, abTest, }); }; @@ -274,6 +275,7 @@ export const useNewsletterSignupForm = ( newsletterId, 'form-submission', renderingTarget, + isSignedIn, abTest, ); @@ -306,10 +308,11 @@ export const useNewsletterSignupForm = ( newsletterId, response.ok ? 'submission-confirmed' : 'submission-failed', renderingTarget, + isSignedIn, abTest, ); }, - [abTest, newsletterId, renderingTarget], + [abTest, isSignedIn, newsletterId, renderingTarget], ); const handleCaptchaComplete = useCallback( @@ -319,6 +322,7 @@ export const useNewsletterSignupForm = ( newsletterId, 'captcha-not-passed', renderingTarget, + isSignedIn, abTest, ); setIsValidationError(false); @@ -331,6 +335,7 @@ export const useNewsletterSignupForm = ( newsletterId, 'captcha-passed', renderingTarget, + isSignedIn, abTest, ); // Read the email that was validated at submit-time — not the @@ -344,6 +349,7 @@ export const useNewsletterSignupForm = ( newsletterId, 'form-submit-error', renderingTarget, + isSignedIn, abTest, ); setIsValidationError(false); @@ -356,7 +362,7 @@ export const useNewsletterSignupForm = ( setIsWaitingForResponse(false); }); }, - [abTest, newsletterId, renderingTarget, submitForm], + [abTest, isSignedIn, newsletterId, renderingTarget, submitForm], ); const handleCaptchaLoadError = useCallback((): void => { @@ -364,13 +370,14 @@ export const useNewsletterSignupForm = ( newsletterId, 'captcha-load-error', renderingTarget, + isSignedIn, abTest, ); setIsValidationError(false); setErrorMessage('Sorry, the reCAPTCHA failed to load.'); setIsWaitingForResponse(false); recaptchaRef.current?.reset(); - }, [abTest, newsletterId, renderingTarget]); + }, [abTest, isSignedIn, newsletterId, renderingTarget]); const handleSubmit = useCallback( (event: FormEvent): void => { @@ -388,11 +395,18 @@ export const useNewsletterSignupForm = ( setIsValidationError(false); setErrorMessage(undefined); setIsWaitingForResponse(true); - sendTracking(newsletterId, 'open-captcha', renderingTarget, abTest); + sendTracking( + newsletterId, + 'open-captcha', + renderingTarget, + isSignedIn, + abTest, + ); recaptchaRef.current?.execute(); }, [ abTest, + isSignedIn, isWaitingForResponse, newsletterId, renderingTarget, @@ -409,7 +423,14 @@ export const useNewsletterSignupForm = ( const handleEmailFocus = useCallback((): void => { setIsInteracted(true); - }, []); + sendTracking( + newsletterId, + 'email-input-focused', + renderingTarget, + isSignedIn, + abTest, + ); + }, [abTest, isSignedIn, newsletterId, renderingTarget]); const handleEmailInvalid = useCallback< React.FormEventHandler @@ -434,8 +455,14 @@ export const useNewsletterSignupForm = ( const handleSubmitButtonClick = useCallback((): void => { hasAttemptedSubmitRef.current = true; - sendTracking(newsletterId, 'click-button', renderingTarget, abTest); - }, [abTest, newsletterId, renderingTarget]); + sendTracking( + newsletterId, + 'click-button', + renderingTarget, + isSignedIn, + abTest, + ); + }, [abTest, isSignedIn, newsletterId, renderingTarget]); const handleReset = useCallback< ReactEventHandler