Skip to content

Commit a55259e

Browse files
authored
Login input type (#47)
1 parent 63a717f commit a55259e

3 files changed

Lines changed: 24 additions & 54 deletions

File tree

app/routes/_app+/_auth+/login.tsx

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
getFormProps,
33
getInputProps,
4-
getSelectProps,
54
useForm,
65
} from '@conform-to/react'
76
import { getZodConstraint, parseWithZod } from '@conform-to/zod/v4'
@@ -22,32 +21,22 @@ import {
2221
CheckboxField,
2322
ErrorList,
2423
Field,
25-
SelectField,
2624
} from '#app/components/forms.tsx'
2725
import { Spacer } from '#app/components/spacer.tsx'
2826
import { StatusButton } from '#app/components/ui/status-button.tsx'
2927
import { login, requireAnonymous } from '#app/utils/auth.server.ts'
3028
import { checkHoneypot } from '#app/utils/honeypot.server.ts'
3129
import { useIsPending } from '#app/utils/misc.tsx'
32-
import { PasswordSchema } from '#app/utils/user-validation.ts'
30+
import { PasswordSchema, UsernameSchema } from '#app/utils/user-validation.ts'
3331
import { handleNewSession } from './login.server.ts'
3432

3533
const LoginFormSchema = z.object({
36-
countryCode: z.string().min(1, 'Country code is required'),
37-
phoneNumber: z.string().min(1, 'Phone number is required'),
34+
username: UsernameSchema,
3835
password: PasswordSchema,
3936
redirectTo: z.string().optional(),
4037
remember: z.boolean().optional(),
4138
})
4239

43-
const countryCodes = [
44-
{ label: 'United States (+1)', value: '+1' },
45-
{ label: 'United Kingdom (+44)', value: '+44' },
46-
{ label: 'Czech Republic (+420)', value: '+420' },
47-
{ label: 'Canada (+1)', value: '+1' },
48-
{ label: 'Australia (+61)', value: '+61' },
49-
]
50-
5140
export async function loader({ request }: LoaderFunctionArgs) {
5241
await requireAnonymous(request)
5342
return json({})
@@ -63,14 +52,13 @@ export async function action({ request }: ActionFunctionArgs) {
6352
if (intent !== null) return { ...data, session: null }
6453

6554
const session = await login({
66-
identifier: data.phoneNumber,
67-
countryCode: data.countryCode,
55+
identifier: data.username,
6856
password: data.password,
6957
})
7058
if (!session) {
7159
ctx.addIssue({
7260
code: z.ZodIssueCode.custom,
73-
message: 'Invalid phone number or password',
61+
message: 'Invalid username or password',
7462
})
7563
return z.NEVER
7664
}
@@ -108,7 +96,6 @@ export default function LoginPage() {
10896
constraint: getZodConstraint(LoginFormSchema),
10997
defaultValue: {
11098
redirectTo,
111-
countryCode: countryCodes[0]?.value ?? '+1',
11299
},
113100
lastResult: actionData?.result,
114101
onValidate({ formData }) {
@@ -132,32 +119,15 @@ export default function LoginPage() {
132119
<div className="border-border bg-card mt-8 w-full max-w-lg rounded-[32px] border px-6 py-8 shadow-sm">
133120
<Form method="POST" {...getFormProps(form)} className="space-y-6">
134121
<HoneypotInputs />
135-
<div className="grid gap-4 md:grid-cols-[200px_1fr]">
136-
<SelectField
137-
labelProps={{ children: 'Country Code' }}
138-
selectProps={{
139-
...getSelectProps(fields.countryCode),
140-
children: countryCodes.map((code) => (
141-
<option
142-
key={`${code.value}-${code.label}`}
143-
value={code.value}
144-
>
145-
{code.label}
146-
</option>
147-
)),
148-
}}
149-
errors={fields.countryCode.errors}
150-
/>
151-
<Field
152-
labelProps={{ children: 'Phone Number' }}
153-
inputProps={{
154-
...getInputProps(fields.phoneNumber, { type: 'text' }),
155-
autoFocus: true,
156-
autoComplete: 'tel',
157-
}}
158-
errors={fields.phoneNumber.errors}
159-
/>
160-
</div>
122+
<Field
123+
labelProps={{ children: 'Username' }}
124+
inputProps={{
125+
...getInputProps(fields.username, { type: 'text' }),
126+
autoFocus: true,
127+
autoComplete: 'username',
128+
}}
129+
errors={fields.username.errors}
130+
/>
161131

162132
<Field
163133
labelProps={{ children: 'Password' }}

tests/e2e/2fa.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ test('Users can add 2FA to their account and use it when logging in', async ({
5555
await page.waitForLoadState('domcontentloaded')
5656
await expect(page).toHaveURL(`/login`)
5757

58-
const phoneNumberInput = page.getByRole('textbox', { name: /phone number/i })
59-
await phoneNumberInput.waitFor({ state: 'visible' })
60-
await phoneNumberInput.fill(user.phoneNumber)
58+
const usernameInput = page.getByRole('textbox', { name: /username/i })
59+
await usernameInput.waitFor({ state: 'visible' })
60+
await usernameInput.fill(user.username)
6161
await page.getByLabel(/^password$/i).fill(password)
6262
await page.getByRole('button', { name: /log in/i }).click()
6363

tests/e2e/onboarding.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ test('login as existing user', async ({ page, insertNewUser }) => {
185185
await page.goto('/login')
186186
await page.waitForLoadState('domcontentloaded')
187187

188-
const phoneNumberInput = page.getByRole('textbox', { name: /phone number/i })
189-
await phoneNumberInput.waitFor({ state: 'visible' })
190-
await phoneNumberInput.fill(user.phoneNumber)
188+
const usernameInput = page.getByRole('textbox', { name: /username/i })
189+
await usernameInput.waitFor({ state: 'visible' })
190+
await usernameInput.fill(user.username)
191191
await page.getByLabel(/^password$/i).fill(password)
192192
await page.getByRole('button', { name: /log in/i }).click()
193193

@@ -261,17 +261,17 @@ test('reset password with a link', async ({ page, insertNewUser }) => {
261261
await expect(page).toHaveURL('/login', { timeout: 15_000 })
262262
await page.waitForLoadState('domcontentloaded')
263263

264-
const loginPhoneInput = page.getByRole('textbox', { name: /phone number/i })
265-
await loginPhoneInput.waitFor({ state: 'visible' })
266-
await loginPhoneInput.fill(user.phoneNumber)
264+
const loginUsernameInput = page.getByRole('textbox', { name: /username/i })
265+
await loginUsernameInput.waitFor({ state: 'visible' })
266+
await loginUsernameInput.fill(user.username)
267267
await page.getByLabel(/^password$/i).fill(originalPassword)
268268
await page.getByRole('button', { name: /log in/i }).click()
269269

270270
await expect(
271-
page.getByText(/invalid phone number or password/i),
271+
page.getByText(/invalid username or password/i),
272272
).toBeVisible()
273273

274-
await loginPhoneInput.fill(user.phoneNumber)
274+
await loginUsernameInput.fill(user.username)
275275
await page.getByLabel(/^password$/i).fill(newPassword)
276276
await page.getByRole('button', { name: /log in/i }).click()
277277

0 commit comments

Comments
 (0)