Skip to content

Commit

Permalink
Improve Form Error Accessibility (#450)
Browse files Browse the repository at this point in the history
  • Loading branch information
delbaoliveira authored Nov 16, 2023
1 parent 0a3dda2 commit 45fcde4
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 74 deletions.
61 changes: 26 additions & 35 deletions dashboard/final-example/app/ui/invoices/create-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
<UserCircleIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500" />
</div>

{state.errors?.customerId ? (
<div
id="customer-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.customerId.map((error: string) => (
<p key={error}>{error}</p>
<div id="customer-error" aria-live="polite" aria-atomic="true">
{state.errors?.customerId &&
state.errors.customerId.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Amount */}
Expand All @@ -77,17 +74,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
</div>
</div>

{state.errors?.amount ? (
<div
id="amount-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.amount.map((error: string) => (
<p key={error}>{error}</p>
<div id="amount-error" aria-live="polite" aria-atomic="true">
{state.errors?.amount &&
state.errors.amount.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Status */}
Expand Down Expand Up @@ -129,24 +123,21 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
</div>
</div>
</div>
{state.errors?.status ? (
<div
aria-describedby="status-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.status.map((error: string) => (
<p key={error}>{error}</p>
<div id="status-error" aria-live="polite" aria-atomic="true">
{state.errors?.status &&
state.errors.status.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</fieldset>

{state.message ? (
<div aria-live="polite" className="my-2 text-sm text-red-500">
<p>{state.message}</p>
</div>
) : null}
<div aria-live="polite" aria-atomic="true">
{state.message ? (
<p className="mt-2 text-sm text-red-500">{state.message}</p>
) : null}
</div>
</div>
<div className="mt-6 flex justify-end gap-4">
<Link
Expand Down
61 changes: 26 additions & 35 deletions dashboard/final-example/app/ui/invoices/edit-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,14 @@ export default function EditInvoiceForm({
<UserCircleIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500" />
</div>

{state.errors?.customerId ? (
<div
id="customer-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.customerId.map((error: string) => (
<p key={error}>{error}</p>
<div id="customer-error" aria-live="polite" aria-atomic="true">
{state.errors?.customerId &&
state.errors.customerId.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Amount */}
Expand All @@ -84,17 +81,14 @@ export default function EditInvoiceForm({
</div>
</div>

{state.errors?.amount ? (
<div
id="amount-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.amount.map((error: string) => (
<p key={error}>{error}</p>
<div id="amount-error" aria-live="polite" aria-atomic="true">
{state.errors?.amount &&
state.errors.amount.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</div>

{/* Invoice Status */}
Expand Down Expand Up @@ -138,24 +132,21 @@ export default function EditInvoiceForm({
</div>
</div>
</div>
{state.errors?.status ? (
<div
aria-describedby="status-error"
aria-live="polite"
className="mt-2 text-sm text-red-500"
>
{state.errors.status.map((error: string) => (
<p key={error}>{error}</p>
<div id="status-error" aria-live="polite" aria-atomic="true">
{state.errors?.status &&
state.errors.status.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
) : null}
</div>
</fieldset>

{state.message ? (
<div aria-live="polite" className="my-2 text-sm text-red-500">
<p>{state.message}</p>
</div>
) : null}
<div aria-live="polite" aria-atomic="true">
{state.message ? (
<p className="my-2 text-sm text-red-500">{state.message}</p>
) : null}
</div>
</div>
<div className="mt-6 flex justify-end gap-4">
<Link
Expand Down
10 changes: 6 additions & 4 deletions dashboard/final-example/app/ui/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ export default function LoginForm() {
</div>
</div>
<LoginButton />
<div className="flex h-8 items-end space-x-1">
<div
className="flex h-8 items-end space-x-1"
aria-live="polite"
aria-atomic="true"
>
{state === 'CredentialsSignin' && (
<>
<ExclamationCircleIcon className="h-5 w-5 text-red-500" />
<p aria-live="polite" className="text-sm text-red-500">
Invalid credentials
</p>
<p className="text-sm text-red-500">Invalid credentials</p>
</>
)}
</div>
Expand Down

1 comment on commit 45fcde4

@vercel
Copy link

@vercel vercel bot commented on 45fcde4 Nov 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

next-learn-dashboard – ./dashboard/final-example

next-learn-dashboard.vercel.sh
next-learn-dashboard-git-main.vercel.sh

Please sign in to comment.