Skip to content

Commit 501cd15

Browse files
feat(auth): enhance forms with new field components and improve accessibility
1 parent b824985 commit 501cd15

File tree

5 files changed

+217
-138
lines changed

5 files changed

+217
-138
lines changed

resources/js/pages/auth/confirm-password.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import InputError from '@/components/input-error';
22
import { Button } from '@/components/ui/button';
3+
import { Field, FieldLabel } from '@/components/ui/field';
34
import { Input } from '@/components/ui/input';
4-
import { Label } from '@/components/ui/label';
5+
import { Spinner } from '@/components/ui/spinner';
56
import AuthLayout from '@/layouts/auth-layout';
67
import { store } from '@/routes/password/confirm';
78
import { Form, Head } from '@inertiajs/react';
8-
import { LoaderCircle } from 'lucide-react';
9-
import { Activity } from 'react';
109

1110
export default function ConfirmPassword() {
1211
return (
@@ -18,35 +17,35 @@ export default function ConfirmPassword() {
1817

1918
<Form
2019
{...store.form()}
21-
disableWhileProcessing
22-
className="inert:pointer-events-none inert:opacity-50 inert:grayscale-100"
2320
resetOnSuccess={['password']}
21+
disableWhileProcessing
22+
className="space-y-6 inert:pointer-events-none inert:opacity-60 inert:grayscale-100"
2423
>
2524
{({ processing, errors }) => (
26-
<div className="space-y-6">
27-
<div className="grid gap-2">
28-
<Label htmlFor="password">Password</Label>
25+
<>
26+
<Field>
27+
<FieldLabel htmlFor="password">Password</FieldLabel>
2928
<Input
30-
id="password"
3129
type="password"
30+
id="password"
3231
name="password"
33-
placeholder="Password"
3432
autoComplete="current-password"
35-
autoFocus
33+
placeholder="Password"
34+
required
35+
aria-required
36+
aria-invalid={!!errors.password}
3637
/>
3738

3839
<InputError message={errors.password} />
39-
</div>
40+
</Field>
4041

4142
<div className="flex items-center">
42-
<Button className="w-full" data-test="confirm-password-button">
43-
<Activity mode={processing ? 'visible' : 'hidden'}>
44-
<LoaderCircle className="size-4 animate-spin" />
45-
</Activity>
43+
<Button className="w-full" disabled={processing} data-test="confirm-password-button">
44+
{processing && <Spinner />}
4645
Confirm password
4746
</Button>
4847
</div>
49-
</div>
48+
</>
5049
)}
5150
</Form>
5251
</AuthLayout>

resources/js/pages/auth/forgot-password.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
// Components
2-
import PasswordResetLinkController from '@/actions/App/Http/Controllers/Auth/PasswordResetLinkController';
2+
import { login } from '@/routes';
3+
import { email } from '@/routes/password';
4+
import { Form, Head } from '@inertiajs/react';
35

46
import InputError from '@/components/input-error';
57
import TextLink from '@/components/text-link';
68
import { Button } from '@/components/ui/button';
9+
import { Field, FieldLabel } from '@/components/ui/field';
710
import { Input } from '@/components/ui/input';
8-
import { Label } from '@/components/ui/label';
11+
import { Spinner } from '@/components/ui/spinner';
912
import AuthLayout from '@/layouts/auth-layout';
10-
import { login } from '@/routes';
11-
import { Form, Head } from '@inertiajs/react';
12-
import { LoaderCircle } from 'lucide-react';
13-
import { Activity } from 'react';
1413

1514
export default function ForgotPassword({ status }: Readonly<{ status?: string }>) {
1615
return (
@@ -21,31 +20,36 @@ export default function ForgotPassword({ status }: Readonly<{ status?: string }>
2120

2221
<div className="space-y-6">
2322
<Form
24-
{...PasswordResetLinkController.store.form()}
23+
{...email.form()}
2524
disableWhileProcessing
26-
className="inert:pointer-events-none inert:opacity-50 inert:grayscale-100"
25+
className="inert:pointer-events-none inert:opacity-60 inert:grayscale-100"
2726
>
2827
{({ processing, errors }) => (
2928
<>
30-
<div className="grid gap-2">
31-
<Label htmlFor="email">Email address</Label>
29+
<Field>
30+
<FieldLabel htmlFor="email">Email address</FieldLabel>
3231
<Input
33-
id="email"
3432
type="email"
33+
id="email"
3534
name="email"
3635
autoComplete="off"
3736
autoFocus
3837
placeholder="[email protected]"
38+
required
39+
aria-required
40+
aria-invalid={!!errors.email}
3941
/>
4042

4143
<InputError message={errors.email} />
42-
</div>
44+
</Field>
4345

4446
<div className="my-6 flex items-center justify-start">
45-
<Button className="w-full" data-test="email-password-reset-link-button">
46-
<Activity mode={processing ? 'visible' : 'hidden'}>
47-
<LoaderCircle className="size-4 animate-spin" />
48-
</Activity>
47+
<Button
48+
className="w-full"
49+
disabled={processing}
50+
data-test="email-password-reset-link-button"
51+
>
52+
{processing && <Spinner />}
4953
Email password reset link
5054
</Button>
5155
</div>

resources/js/pages/auth/login.tsx

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,110 @@
1-
import AuthenticatedSessionController from '@/actions/App/Http/Controllers/Auth/AuthenticatedSessionController';
21
import InputError from '@/components/input-error';
32
import TextLink from '@/components/text-link';
43
import { Button } from '@/components/ui/button';
54
import { Checkbox } from '@/components/ui/checkbox';
5+
import { Field, FieldLabel } from '@/components/ui/field';
66
import { Input } from '@/components/ui/input';
77
import { Label } from '@/components/ui/label';
8+
import { Spinner } from '@/components/ui/spinner';
89
import AuthLayout from '@/layouts/auth-layout';
910
import { register } from '@/routes';
11+
import { store } from '@/routes/login';
1012
import { request } from '@/routes/password';
1113
import { Form, Head } from '@inertiajs/react';
12-
import { LoaderCircle } from 'lucide-react';
1314
import { Activity } from 'react';
1415

1516
interface LoginProps {
1617
status?: string;
1718
canResetPassword: boolean;
19+
canRegister: boolean;
1820
}
1921

20-
export default function Login({ status, canResetPassword }: Readonly<LoginProps>) {
22+
export default function Login({ status, canResetPassword, canRegister }: Readonly<LoginProps>) {
2123
return (
2224
<AuthLayout title="Log in to your account" description="Enter your email and password below to log in">
2325
<Head title="Log in" />
2426

2527
<Form
26-
{...AuthenticatedSessionController.store.form()}
28+
{...store.form()}
2729
resetOnSuccess={['password']}
30+
className="flex flex-col gap-6 inert:pointer-events-none inert:opacity-60 inert:grayscale-100"
2831
disableWhileProcessing
29-
className="space-y-6 inert:pointer-events-none inert:opacity-50 inert:grayscale-100"
3032
>
3133
{({ processing, errors }) => (
3234
<>
3335
<div className="grid gap-6">
34-
<div className="grid gap-2">
35-
<Label htmlFor="email">Email address</Label>
36+
<Field>
37+
<FieldLabel htmlFor="email">Email address</FieldLabel>
3638
<Input
37-
id="email"
3839
type="email"
40+
id="email"
3941
name="email"
40-
required
4142
autoFocus
4243
tabIndex={1}
4344
autoComplete="email"
4445
placeholder="[email protected]"
46+
required
47+
aria-required
48+
aria-invalid={!!errors.email}
4549
/>
4650
<InputError message={errors.email} />
47-
</div>
51+
</Field>
4852

49-
<div className="grid gap-2">
53+
<Field>
5054
<div className="flex items-center">
51-
<Label htmlFor="password">Password</Label>
55+
<FieldLabel htmlFor="password">Password</FieldLabel>
56+
5257
<Activity mode={canResetPassword ? 'visible' : 'hidden'}>
5358
<TextLink href={request()} className="ml-auto text-sm" tabIndex={5}>
5459
Forgot password?
5560
</TextLink>
5661
</Activity>
5762
</div>
63+
5864
<Input
59-
id="password"
6065
type="password"
66+
id="password"
6167
name="password"
62-
required
6368
tabIndex={2}
6469
autoComplete="current-password"
6570
placeholder="Password"
71+
required
72+
aria-required
73+
aria-invalid={!!errors.password}
6674
/>
6775
<InputError message={errors.password} />
68-
</div>
76+
</Field>
6977

7078
<div className="flex items-center space-x-3">
7179
<Checkbox id="remember" name="remember" tabIndex={3} />
7280
<Label htmlFor="remember">Remember me</Label>
7381
</div>
7482

75-
<Button type="submit" className="mt-4 w-full" tabIndex={4} data-test="login-button">
76-
<Activity mode={processing ? 'visible' : 'hidden'}>
77-
<LoaderCircle className="size-4 animate-spin" />
78-
</Activity>
83+
<Button
84+
type="submit"
85+
className="mt-4 w-full"
86+
tabIndex={4}
87+
disabled={processing}
88+
data-test="login-button"
89+
>
90+
{processing && <Spinner />}
7991
Log in
8092
</Button>
8193
</div>
8294

83-
<div className="text-center text-sm text-muted-foreground">
84-
Don't have an account?{' '}
85-
<TextLink href={register()} tabIndex={5}>
86-
Sign up
87-
</TextLink>
88-
</div>
95+
<Activity mode={canRegister ? 'visible' : 'hidden'}>
96+
<div className="text-center text-sm text-muted-foreground">
97+
Don't have an account?{' '}
98+
<TextLink href={register()} tabIndex={5}>
99+
Sign up
100+
</TextLink>
101+
</div>
102+
</Activity>
89103
</>
90104
)}
91105
</Form>
92106

93-
{status && <div className="mb-4 text-center text-sm font-medium text-green-600">{status}</div>}
107+
{status && <div className="mt-4 text-center text-sm font-medium text-green-600">{status}</div>}
94108
</AuthLayout>
95109
);
96110
}

0 commit comments

Comments
 (0)