Skip to content

Commit 3d85832

Browse files
committed
fix(react,shadcn): Add description to SMS verification code input
1 parent 2b6c337 commit 3d85832

File tree

4 files changed

+142
-22
lines changed

4 files changed

+142
-22
lines changed

packages/react/src/auth/forms/phone-auth-form.test.tsx

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
18-
import { render, screen, fireEvent, renderHook, cleanup } from "@testing-library/react";
18+
import { render, screen, fireEvent, renderHook, cleanup, waitFor } from "@testing-library/react";
1919
import {
2020
PhoneAuthForm,
2121
usePhoneNumberFormAction,
@@ -80,25 +80,39 @@ import { registerLocale } from "@invertase/firebaseui-translations";
8080
import { FirebaseUIProvider } from "~/context";
8181

8282
vi.mock("~/components/country-selector", () => ({
83-
CountrySelector: vi.fn().mockImplementation(({ value, onChange }) => (
84-
<div data-testid="country-selector">
85-
<select
86-
onChange={(e) =>
87-
onChange &&
88-
onChange({
89-
code: e.target.value,
90-
name: e.target.value === "US" ? "United States" : "United Kingdom",
91-
dialCode: e.target.value === "US" ? "+1" : "+44",
92-
emoji: e.target.value === "US" ? "🇺🇸" : "🇬🇧",
93-
})
94-
}
95-
value={value?.code}
96-
>
97-
<option value="US">United States</option>
98-
<option value="GB">United Kingdom</option>
99-
</select>
100-
</div>
101-
)),
83+
CountrySelector: vi.fn().mockImplementation(({ value, onChange, ref }: any) => {
84+
if (ref && typeof ref === "object" && "current" in ref) {
85+
ref.current = {
86+
getCountry: () => ({
87+
code: "US",
88+
name: "United States",
89+
dialCode: "+1",
90+
emoji: "🇺🇸",
91+
}),
92+
setCountry: () => {},
93+
};
94+
}
95+
96+
return (
97+
<div data-testid="country-selector">
98+
<select
99+
onChange={(e) =>
100+
onChange &&
101+
onChange({
102+
code: e.target.value,
103+
name: e.target.value === "US" ? "United States" : "United Kingdom",
104+
dialCode: e.target.value === "US" ? "+1" : "+44",
105+
emoji: e.target.value === "US" ? "🇺🇸" : "🇬🇧",
106+
})
107+
}
108+
value={value?.code}
109+
>
110+
<option value="US">United States</option>
111+
<option value="GB">United Kingdom</option>
112+
</select>
113+
</div>
114+
);
115+
}),
102116
}));
103117

104118
describe("usePhoneNumberFormAction", () => {
@@ -545,4 +559,48 @@ describe("<PhoneAuthForm />", () => {
545559
expect(screen.getByRole("button", { name: "sendCode" })).toBeInTheDocument();
546560
expect(screen.getByTestId("country-selector")).toBeInTheDocument();
547561
});
562+
563+
it("should render the verification code form with description after phone number submission", async () => {
564+
const mockUI = createMockUI({
565+
locale: registerLocale("test", {
566+
labels: {
567+
phoneNumber: "Phone Number",
568+
sendCode: "Send Code",
569+
verificationCode: "verificationCode",
570+
verifyCode: "verifyCode",
571+
},
572+
prompts: {
573+
smsVerificationPrompt: "Enter the verification code sent to your phone number",
574+
},
575+
}),
576+
});
577+
578+
const mockVerificationId = "test-verification-id";
579+
vi.mocked(verifyPhoneNumber).mockResolvedValue(mockVerificationId);
580+
581+
const { container } = render(
582+
<FirebaseUIProvider ui={mockUI}>
583+
<PhoneAuthForm />
584+
</FirebaseUIProvider>
585+
);
586+
587+
const phoneInput = screen.getByRole("textbox", { name: /phone number/i });
588+
expect(phoneInput).toBeInTheDocument();
589+
590+
const sendCodeButton = screen.getByRole("button", { name: /send code/i });
591+
592+
await act(async () => {
593+
fireEvent.change(phoneInput, { target: { value: "1234567890" } });
594+
fireEvent.click(sendCodeButton);
595+
});
596+
597+
const verificationInput = await waitFor(() => {
598+
return screen.getByRole("textbox", { name: /verificationCode/i });
599+
});
600+
expect(verificationInput).toBeInTheDocument();
601+
602+
const description = container.querySelector("[data-input-description]");
603+
expect(description).toBeInTheDocument();
604+
expect(description).toHaveTextContent("Enter the verification code sent to your phone number");
605+
});
548606
});

packages/react/src/auth/forms/phone-auth-form.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,13 @@ function VerifyPhoneNumberForm(props: VerifyPhoneNumberFormProps) {
179179
<form.AppForm>
180180
<fieldset>
181181
<form.AppField name="verificationCode">
182-
{(field) => <field.Input label={getTranslation(ui, "labels", "verificationCode")} type="text" />}
182+
{(field) => (
183+
<field.Input
184+
label={getTranslation(ui, "labels", "verificationCode")}
185+
description={getTranslation(ui, "prompts", "smsVerificationPrompt")}
186+
type="text"
187+
/>
188+
)}
183189
</form.AppField>
184190
</fieldset>
185191
<fieldset>

packages/shadcn/src/components/phone-auth-form.test.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ vi.mock("@invertase/firebaseui-core", async (importOriginal) => {
4141
if (category === "labels" && key === "phoneNumber") return "Phone Number";
4242
if (category === "labels" && key === "verificationCode") return "Verification Code";
4343
if (category === "labels" && key === "verifyCode") return "Verify Code";
44+
if (category === "prompts" && key === "smsVerificationPrompt") return "Enter the verification code sent to your phone number";
4445
if (category === "errors" && key === "invalidPhoneNumber") return "Error: Invalid phone number format";
4546
if (category === "errors" && key === "missingPhoneNumber") return "Phone number is required";
4647
return key;
@@ -174,6 +175,60 @@ describe("<PhoneAuthForm />", () => {
174175
expect(container.querySelector("input[name='phoneNumber']")).not.toBeInTheDocument();
175176
});
176177

178+
it("should render the verification code form with description after phone number submission", async () => {
179+
const mockVerificationId = "test-verification-id";
180+
const mockAction = vi.fn().mockResolvedValue(mockVerificationId);
181+
vi.mocked(usePhoneNumberFormAction).mockReturnValue(mockAction);
182+
183+
const mockUI = createMockUI({
184+
locale: registerLocale("test", {
185+
labels: {
186+
sendCode: "Send Code",
187+
phoneNumber: "Phone Number",
188+
verificationCode: "Verification Code",
189+
verifyCode: "Verify Code",
190+
},
191+
prompts: {
192+
smsVerificationPrompt: "Enter the verification code sent to your phone number",
193+
},
194+
}),
195+
});
196+
197+
const { container } = render(
198+
<FirebaseUIProvider ui={mockUI}>
199+
<PhoneAuthForm />
200+
</FirebaseUIProvider>
201+
);
202+
203+
// Initially should show phone number form
204+
expect(container.querySelector("input[name='phoneNumber']")).toBeInTheDocument();
205+
206+
const phoneInput = container.querySelector("input[name='phoneNumber']")!;
207+
const submitButton = container.querySelector("button[type='submit']")!;
208+
209+
act(() => {
210+
fireEvent.change(phoneInput, { target: { value: "1234567890" } });
211+
});
212+
213+
await act(async () => {
214+
fireEvent.click(submitButton);
215+
});
216+
217+
await waitFor(() => {
218+
expect(mockAction).toHaveBeenCalled();
219+
});
220+
221+
// Wait for verification form to appear
222+
await waitFor(() => {
223+
expect(container.querySelector("input[name='verificationCode']")).toBeInTheDocument();
224+
});
225+
226+
// Check for description
227+
const description = container.querySelector('[data-slot="form-description"]');
228+
expect(description).toBeInTheDocument();
229+
expect(description).toHaveTextContent("Enter the verification code sent to your phone number");
230+
});
231+
177232
it("should call onSignIn callback when verification is successful", async () => {
178233
const mockVerificationId = "test-verification-id";
179234
const mockCredential = { credential: true } as unknown as UserCredential;

packages/shadcn/src/components/phone-auth-form.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
type PhoneAuthVerifyFormSchema,
2323
} from "@invertase/firebaseui-core";
2424

25-
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
25+
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
2626
import { Input } from "@/components/ui/input";
2727
import { Button } from "@/components/ui/button";
2828
import { Policies } from "@/components/policies";
@@ -66,6 +66,7 @@ function VerifyPhoneNumberForm(props: VerifyPhoneNumberFormProps) {
6666
render={({ field }) => (
6767
<FormItem>
6868
<FormLabel>{getTranslation(ui, "labels", "verificationCode")}</FormLabel>
69+
<FormDescription>{getTranslation(ui, "prompts", "smsVerificationPrompt")}</FormDescription>
6970
<FormControl>
7071
<InputOTP maxLength={6} {...field}>
7172
<InputOTPGroup>

0 commit comments

Comments
 (0)