@@ -2,19 +2,37 @@ import { Trans, useLingui } from "@lingui/react/macro"
22import { Box , Button , CloseButton , Stack , Tabs } from "@mantine/core"
33import { FileInput , TextInput , Textarea } from "@mantine/core"
44import { isJSONString , isNotEmpty , useForm } from "@mantine/form"
5- import { useState } from "react"
65import * as icons from "#icons.ts"
76import * as settings from "#settings.ts"
87
8+ export type ActiveTab = "url" | "file" | "text"
9+
910export interface FormProps {
1011 onSubmit : ( value : string | File | Record < string , any > ) => void
12+ activeTab ?: ActiveTab
13+ onActiveTabChange ?: ( tab : ActiveTab ) => void
1114}
1215
16+ // Live-region props for inline validation errors so screen readers announce them
17+ // the moment the message appears (WCAG 4.1.3 Status Messages, 3.3.1 Error Identification).
18+ const errorProps = { role : "alert" , "aria-live" : "polite" as const }
19+
1320export function Form ( props : FormProps ) {
14- const [ activeTab , setActiveTab ] = useState < string | null > ( "url" )
21+ const activeTab = props . activeTab ?? "url"
22+
23+ const handleTabChange = ( value : string | null ) => {
24+ if ( value === "url" || value === "file" || value === "text" ) {
25+ props . onActiveTabChange ?.( value )
26+ }
27+ }
1528
1629 return (
17- < Tabs defaultValue = "url" value = { activeTab } onChange = { setActiveTab } >
30+ < Tabs
31+ value = { activeTab }
32+ onChange = { handleTabChange }
33+ activateTabWithKeyboard = { false }
34+ classNames = { { tab : "hover-bump-tab" } }
35+ >
1836 < Tabs . List >
1937 < Tabs . Tab
2038 value = "url"
@@ -94,11 +112,15 @@ function UrlForm(props: { onSubmit: (value: string) => void }) {
94112 < form onSubmit = { handleSubmit } >
95113 < Stack gap = "md" >
96114 < TextInput
115+ id = "input-url"
97116 placeholder = { t `Enter data package URL` }
98117 size = "lg"
99118 label = { t `Data Package URL` }
119+ errorProps = { errorProps }
100120 rightSection = {
101121 < CloseButton
122+ className = "hover-bump"
123+ aria-label = { t `Clear URL` }
102124 onClick = { ( ) => form . setFieldValue ( "url" , "" ) }
103125 disabled = { ! form . values . url }
104126 />
@@ -139,11 +161,15 @@ function FileForm(props: { onSubmit: (value: File) => void }) {
139161 < form onSubmit = { handleSubmit } >
140162 < Stack gap = "md" >
141163 < FileInput
164+ id = "input-file"
142165 placeholder = { t `Select data package file` }
143166 size = "lg"
144167 label = { t `Data Package File` }
168+ errorProps = { errorProps }
145169 rightSection = {
146170 < CloseButton
171+ className = "hover-bump"
172+ aria-label = { t `Clear file selection` }
147173 onClick = { ( ) => form . setFieldValue ( "file" , null ) }
148174 disabled = { ! form . values . file }
149175 />
@@ -180,14 +206,18 @@ function JsonForm(props: { onSubmit: (value: Record<string, any>) => void }) {
180206 < form onSubmit = { handleSubmit } >
181207 < Stack gap = "md" >
182208 < Textarea
209+ id = "input-text"
183210 placeholder = { t `Paste data package JSON` }
184211 size = "lg"
185212 label = { t `Data Package JSON` }
186213 autosize
187214 minRows = { 5 }
215+ errorProps = { errorProps }
188216 rightSection = {
189217 < Box mt = "xs" style = { { alignSelf : "flex-start" } } >
190218 < CloseButton
219+ className = "hover-bump"
220+ aria-label = { t `Clear JSON input` }
191221 onClick = { ( ) => form . setFieldValue ( "text" , "" ) }
192222 disabled = { ! form . values . text }
193223 />
@@ -202,13 +232,10 @@ function JsonForm(props: { onSubmit: (value: Record<string, any>) => void }) {
202232}
203233
204234function SubmitButton ( props : { disabled : boolean } ) {
235+ // Always render filled so contrast meets 4.5:1 in both light and dark.
236+ // The light variant fallback failed WCAG 1.4.3.
205237 return (
206- < Button
207- type = "submit"
208- size = "lg"
209- variant = { props . disabled ? "light" : "filled" }
210- disabled = { props . disabled }
211- >
238+ < Button type = "submit" size = "lg" variant = "filled" disabled = { props . disabled } >
212239 < Trans > Validate Data Package</ Trans >
213240 </ Button >
214241 )
0 commit comments