From d57739c4c754bea00d755e018958f962aae38c77 Mon Sep 17 00:00:00 2001 From: Valerie Pomerleau Date: Mon, 3 Mar 2025 14:50:53 -0800 Subject: [PATCH] fix(settings): Prevent multiple SMS sends on form submission Because: * Double clicking on the submit button sends two SMS * Form submssion not disabled during submission This commit: * Disables form submission while initial handling in progress Closes #FXA-11235 --- packages/fxa-settings/src/components/FormChoice/index.tsx | 4 +++- packages/fxa-settings/src/components/FormChoice/mocks.tsx | 1 + .../fxa-settings/src/components/FormPhoneNumber/index.tsx | 4 ++++ .../src/pages/Signin/SigninRecoveryChoice/index.tsx | 5 ++++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/fxa-settings/src/components/FormChoice/index.tsx b/packages/fxa-settings/src/components/FormChoice/index.tsx index 76cf53224d6..ca9d48b607e 100644 --- a/packages/fxa-settings/src/components/FormChoice/index.tsx +++ b/packages/fxa-settings/src/components/FormChoice/index.tsx @@ -18,6 +18,7 @@ export type FormChoiceProps = { alignImage?: 'start' | 'end'; formChoices: FormChoiceOption[]; onSubmit: (data: FormChoiceData) => void; + isSubmitting: boolean; }; export const CHOICES = { @@ -34,6 +35,7 @@ const FormChoice = ({ alignImage = 'start', formChoices, onSubmit, + isSubmitting, }: FormChoiceProps) => { const { register, handleSubmit, watch } = useForm(); const selectedOption = watch('choice'); @@ -77,7 +79,7 @@ const FormChoice = ({ diff --git a/packages/fxa-settings/src/components/FormChoice/mocks.tsx b/packages/fxa-settings/src/components/FormChoice/mocks.tsx index be4f7f3b4e7..b0cc547434a 100644 --- a/packages/fxa-settings/src/components/FormChoice/mocks.tsx +++ b/packages/fxa-settings/src/components/FormChoice/mocks.tsx @@ -10,6 +10,7 @@ import { } from '../images'; export const commonFormChoiceProps = { + isSubmitting: false, onSubmit: (data: any) => console.log('Submitted with choice:', data), legendEl: ( diff --git a/packages/fxa-settings/src/components/FormPhoneNumber/index.tsx b/packages/fxa-settings/src/components/FormPhoneNumber/index.tsx index 9826fd78fb7..461a5c59580 100644 --- a/packages/fxa-settings/src/components/FormPhoneNumber/index.tsx +++ b/packages/fxa-settings/src/components/FormPhoneNumber/index.tsx @@ -32,6 +32,7 @@ const FormPhoneNumber = ({ gleanDataAttrs, }: FormPhoneNumberProps) => { const [hasErrors, setHasErrors] = React.useState(false); + const [isSubmitting, setIsSubmitting] = React.useState(false); const { control, formState, handleSubmit, register, setValue } = useForm({ mode: 'onChange', @@ -62,11 +63,13 @@ const FormPhoneNumber = ({ countryCode, }: InputPhoneNumberData) => { setHasErrors(false); + setIsSubmitting(true); const formattedPhoneNumber = formatPhoneNumber({ phoneNumber, countryCode, }); const result = await submitPhoneNumber(formattedPhoneNumber); + setIsSubmitting(false); if (result !== undefined && result.hasErrors) { setHasErrors(true); const phoneInput = document.querySelector( @@ -111,6 +114,7 @@ const FormPhoneNumber = ({ type="submit" className="cta-primary cta-xl" disabled={ + isSubmitting || !formState.isDirty || phoneNumberInput === undefined || phoneNumberInput.replace(/\D/g, '').length !== 10 diff --git a/packages/fxa-settings/src/pages/Signin/SigninRecoveryChoice/index.tsx b/packages/fxa-settings/src/pages/Signin/SigninRecoveryChoice/index.tsx index f2f4b86f12e..011b8f34cfa 100644 --- a/packages/fxa-settings/src/pages/Signin/SigninRecoveryChoice/index.tsx +++ b/packages/fxa-settings/src/pages/Signin/SigninRecoveryChoice/index.tsx @@ -45,6 +45,7 @@ const SigninRecoveryChoice = ({ const [errorBannerMessage, setErrorBannerMessage] = React.useState(''); const [errorBannerDescription, setErrorBannerDescription] = React.useState(''); + const [isSubmitting, setIsSubmitting] = React.useState(false); const ftlMsgResolver = useFtlMsgResolver(); const navigateWithQuery = useNavigateWithQuery(); @@ -75,12 +76,14 @@ const SigninRecoveryChoice = ({ const onSubmit = async ({ choice }: FormChoiceData) => { setErrorBannerMessage(''); setErrorBannerDescription(''); + setIsSubmitting(true); GleanMetrics.login.backupChoiceSubmit({ event: { reason: choice } }); switch (choice) { case CHOICES.phone: const error = await handlePhoneChoice(); if (error) { handlePhoneChoiceError(error); + setIsSubmitting(false); return; } navigateWithQuery('/signin_recovery_phone', { @@ -141,7 +144,7 @@ const SigninRecoveryChoice = ({ /> )} - + ); };