feat: enhance accessibility and testing with data-test attributes and improve error handling
Some checks failed
Workflow / ⚫️ Test (push) Has been cancelled
Workflow / ʦ TypeScript (push) Has been cancelled

This commit is contained in:
T. Zehetbauer
2026-04-01 10:46:44 +02:00
parent 3bcc5c70a3
commit abac22feb1
55 changed files with 1622 additions and 128 deletions

View File

@@ -83,6 +83,7 @@ export function CreateProtocolForm({
<FormLabel>Titel *</FormLabel>
<FormControl>
<Input
data-test="protocol-title-input"
placeholder="z.B. Vorstandssitzung März 2026"
{...field}
/>
@@ -100,7 +101,11 @@ export function CreateProtocolForm({
<FormItem>
<FormLabel>Sitzungsdatum *</FormLabel>
<FormControl>
<Input type="date" {...field} />
<Input
data-test="protocol-date-input"
type="date"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
@@ -115,6 +120,7 @@ export function CreateProtocolForm({
<FormLabel>Sitzungsart *</FormLabel>
<FormControl>
<select
data-test="protocol-type-select"
{...field}
className="border-input bg-background flex h-10 w-full rounded-md border px-3 py-2 text-sm"
>
@@ -136,7 +142,11 @@ export function CreateProtocolForm({
<FormItem>
<FormLabel>Ort</FormLabel>
<FormControl>
<Input placeholder="z.B. Vereinsheim" {...field} />
<Input
data-test="protocol-location-input"
placeholder="z.B. Vereinsheim"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
@@ -223,10 +233,19 @@ export function CreateProtocolForm({
{/* Submit */}
<div className="flex justify-end gap-2">
<Button type="button" variant="outline" onClick={() => router.back()}>
<Button
type="button"
variant="outline"
data-test="protocol-cancel-btn"
onClick={() => router.back()}
>
Abbrechen
</Button>
<Button type="submit" disabled={isPending}>
<Button
type="submit"
disabled={isPending}
data-test="protocol-submit-btn"
>
{isPending ? 'Wird gespeichert...' : 'Protokoll erstellen'}
</Button>
</div>

View File

@@ -2,6 +2,7 @@
import Link from 'next/link';
import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils';
interface MeetingsTabNavigationProps {
@@ -10,9 +11,9 @@ interface MeetingsTabNavigationProps {
}
const TABS = [
{ id: 'overview', label: 'Übersicht', path: '' },
{ id: 'protocols', label: 'Protokolle', path: '/protocols' },
{ id: 'tasks', label: 'Offene Aufgaben', path: '/tasks' },
{ id: 'overview', i18nKey: 'meetings:nav.overview', path: '' },
{ id: 'protocols', i18nKey: 'meetings:nav.protocols', path: '/protocols' },
{ id: 'tasks', i18nKey: 'meetings:nav.tasks', path: '/tasks' },
] as const;
export function MeetingsTabNavigation({
@@ -25,7 +26,7 @@ export function MeetingsTabNavigation({
<div className="mb-6 border-b">
<nav
className="-mb-px flex space-x-1 overflow-x-auto"
aria-label="Sitzungsprotokolle Navigation"
aria-label="Meeting Protocols Navigation"
>
{TABS.map((tab) => {
const isActive = tab.id === activeTab;
@@ -34,6 +35,7 @@ export function MeetingsTabNavigation({
<Link
key={tab.id}
href={`${basePath}${tab.path}`}
data-test={`meetings-tab-${tab.id}`}
className={cn(
'border-b-2 px-4 py-2.5 text-sm font-medium whitespace-nowrap transition-colors',
isActive
@@ -41,7 +43,7 @@ export function MeetingsTabNavigation({
: 'text-muted-foreground hover:border-muted-foreground/30 hover:text-foreground border-transparent',
)}
>
{tab.label}
<Trans i18nKey={tab.i18nKey} />
</Link>
);
})}

View File

@@ -174,6 +174,7 @@ export function OpenTasksView({
</p>
<div className="flex gap-2">
<Button
data-test="tasks-prev-btn"
variant="outline"
size="sm"
disabled={page <= 1}
@@ -182,6 +183,7 @@ export function OpenTasksView({
Zurück
</Button>
<Button
data-test="tasks-next-btn"
variant="outline"
size="sm"
disabled={page >= totalPages}

View File

@@ -141,6 +141,7 @@ export function ProtocolItemsList({
</td>
<td className="p-3 text-center">
<Badge
data-test="item-status-toggle"
variant={
(ITEM_STATUS_COLORS[item.status] as
| 'default'
@@ -156,6 +157,7 @@ export function ProtocolItemsList({
</td>
<td className="p-3 text-right">
<Button
data-test="item-delete-btn"
variant="ghost"
size="sm"
className="text-destructive hover:text-destructive"

View File

@@ -69,16 +69,16 @@ export function ProtocolsDataTable({
);
const handleSearch = useCallback(
(e: React.FormEvent) => {
e.preventDefault();
(formEvent: React.FormEvent) => {
formEvent.preventDefault();
updateParams({ q: form.getValues('search') });
},
[form, updateParams],
);
const handleTypeChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
updateParams({ type: e.target.value });
(changeEvent: React.ChangeEvent<HTMLSelectElement>) => {
updateParams({ type: changeEvent.target.value });
},
[updateParams],
);
@@ -96,17 +96,24 @@ export function ProtocolsDataTable({
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<form onSubmit={handleSearch} className="flex gap-2">
<Input
data-test="protocols-search-input"
placeholder="Protokoll suchen..."
className="w-64"
{...form.register('search')}
/>
<Button type="submit" variant="outline" size="sm">
<Button
type="submit"
variant="outline"
size="sm"
data-test="protocols-search-btn"
>
Suchen
</Button>
</form>
<div className="flex items-center gap-3">
<select
data-test="protocols-type-filter"
value={currentType}
onChange={handleTypeChange}
className="border-input bg-background flex h-9 rounded-md border px-3 py-1 text-sm shadow-sm"
@@ -119,7 +126,7 @@ export function ProtocolsDataTable({
</select>
<Link href={`/home/${account}/meetings/protocols/new`}>
<Button size="sm">
<Button size="sm" data-test="protocols-new-btn">
<Plus className="mr-2 h-4 w-4" />
Neues Protokoll
</Button>