From d0576d77fbaa3747d491a4d8c526d6071e07c0d7 Mon Sep 17 00:00:00 2001 From: maphew <486200+maphew@users.noreply.github.com> Date: Fri, 11 Sep 2026 04:22:49 +0000 Subject: [PATCH 1/3] fix(web): label forms for screen readers Add accessible names and error links to six forms. Follow issue #4172. The password show/hide buttons now stay in the keyboard tab order. They have aria-label, aria-pressed, and aria-controls. The length rule is linked to the password input. The other changes link a label to its input in the Create Town dialog, mark the Invite Member email input invalid and link its error, name the app prompt and TTFB search inputs, and link the Wasteland name error and organization selector. Add a server-render test for the password fields. --- .../new/NewWastelandWizardClient.tsx | 24 +++++++++-- .../alerting-ttfb/TtfbAlertingContent.tsx | 1 + .../components/app-builder/PromptInput.tsx | 1 + .../deployments/PasswordFormFields.test.ts | 41 +++++++++++++++++++ .../deployments/PasswordFormFields.tsx | 13 ++++-- .../components/gastown/CreateTownDialog.tsx | 8 +++- .../members/InviteMemberDialog.tsx | 8 +++- 7 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 apps/web/src/components/deployments/PasswordFormFields.test.ts diff --git a/apps/web/src/app/(app)/wasteland/new/NewWastelandWizardClient.tsx b/apps/web/src/app/(app)/wasteland/new/NewWastelandWizardClient.tsx index 32f631d218..4f17ac70ab 100644 --- a/apps/web/src/app/(app)/wasteland/new/NewWastelandWizardClient.tsx +++ b/apps/web/src/app/(app)/wasteland/new/NewWastelandWizardClient.tsx @@ -661,14 +661,21 @@ function IntentStep({ onChange={e => setName(e.target.value)} maxLength={NAME_MAX_LENGTH} autoFocus + aria-invalid={Boolean(nameError)} + aria-describedby={nameError ? 'wasteland-name-error' : undefined} /> - {nameError &&

{nameError}

} + {nameError && ( + + )} {/* Ownership */}
- + { if (lockedOrgId) return; @@ -701,7 +708,12 @@ function IntentStep({

) : ( )} - {orgError &&

{orgError}

} + {orgError && ( + + )}
)} diff --git a/apps/web/src/app/admin/alerting-ttfb/TtfbAlertingContent.tsx b/apps/web/src/app/admin/alerting-ttfb/TtfbAlertingContent.tsx index 1956645046..39dbec164e 100644 --- a/apps/web/src/app/admin/alerting-ttfb/TtfbAlertingContent.tsx +++ b/apps/web/src/app/admin/alerting-ttfb/TtfbAlertingContent.tsx @@ -194,6 +194,7 @@ export function TtfbAlertingContent() { handleSearchChange(e.target.value)} className="pl-8" diff --git a/apps/web/src/components/app-builder/PromptInput.tsx b/apps/web/src/components/app-builder/PromptInput.tsx index a5c4d9e6d4..d342e51263 100644 --- a/apps/web/src/components/app-builder/PromptInput.tsx +++ b/apps/web/src/components/app-builder/PromptInput.tsx @@ -341,6 +341,7 @@ export function PromptInput({ onChange={handleChange} onKeyDown={handleKeyDown} placeholder={effectivePlaceholder} + aria-label={isLanding ? 'App prompt' : 'Message'} disabled={disabled || isSubmitting} className={cn( 'resize-none border-none bg-transparent px-0 shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0', diff --git a/apps/web/src/components/deployments/PasswordFormFields.test.ts b/apps/web/src/components/deployments/PasswordFormFields.test.ts new file mode 100644 index 0000000000..fc73df4971 --- /dev/null +++ b/apps/web/src/components/deployments/PasswordFormFields.test.ts @@ -0,0 +1,41 @@ +import React from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { describe, expect, it } from '@jest/globals'; +import { PasswordProtection } from './PasswordFormFields'; + +function render(enabled: boolean) { + return renderToStaticMarkup( + React.createElement(PasswordProtection, { + value: { password: '', confirmPassword: '', enabled }, + onChange: () => undefined, + }) + ); +} + +function firstVisibilityToggle(html: string): string { + return html.match(/ -

Minimum 8 characters

+

+ Minimum 8 characters +

@@ -131,8 +136,10 @@ export function PasswordProtection({ /> @@ -173,6 +175,7 @@ const BottomBar = memo(function BottomBar({ onClick={onSubmit} disabled={isSubmitDisabled} className="h-9 w-9" + aria-label="Send message" > diff --git a/apps/web/src/components/deployments/PasswordFormFields.test.ts b/apps/web/src/components/deployments/PasswordFormFields.test.ts index fc73df4971..9cac05068c 100644 --- a/apps/web/src/components/deployments/PasswordFormFields.test.ts +++ b/apps/web/src/components/deployments/PasswordFormFields.test.ts @@ -12,18 +12,20 @@ function render(enabled: boolean) { ); } -function firstVisibilityToggle(html: string): string { - return html.match(/
diff --git a/apps/web/src/components/organizations/members/InviteMemberDialog.tsx b/apps/web/src/components/organizations/members/InviteMemberDialog.tsx index d951f9db50..8202618ed0 100644 --- a/apps/web/src/components/organizations/members/InviteMemberDialog.tsx +++ b/apps/web/src/components/organizations/members/InviteMemberDialog.tsx @@ -273,10 +273,12 @@ export function InviteMemberDialog({