fix(a11y): bump progress bar text to text-xs minimum, add motion-safe to upload animation
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m39s
CI / OCR Service Tests (push) Successful in 35s
CI / Backend Unit Tests (push) Failing after 2m46s
CI / Unit & Component Tests (pull_request) Failing after 2m37s
CI / OCR Service Tests (pull_request) Successful in 36s
CI / Backend Unit Tests (pull_request) Failing after 2m50s
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m39s
CI / OCR Service Tests (push) Successful in 35s
CI / Backend Unit Tests (push) Failing after 2m46s
CI / Unit & Component Tests (pull_request) Failing after 2m37s
CI / OCR Service Tests (pull_request) Successful in 36s
CI / Backend Unit Tests (pull_request) Failing after 2m50s
- text-[9px]/text-[10px] in required-fields bar raised to text-xs (12px), meeting the project minimum for the 60+ audience (WCAG 1.4.4) - Upload animation now uses motion-safe: prefix so it stops for users with prefers-reduced-motion set (WCAG 2.1 SC 2.3.3) - Strengthened UploadZone tests: onCancel uses [role=status] button selector instead of first-button heuristic; added positive file selection test (valid PDF calls onFile), file-too-large test, and MIME rejection now also asserts the error message is visible Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,9 @@ function handleDrop(e: DragEvent) {
|
||||
{#if isUploading}
|
||||
<div role="status" class="flex flex-col items-center gap-3 text-center">
|
||||
<div class="h-0.5 w-48 overflow-hidden rounded-full bg-white/10">
|
||||
<div class="h-full animate-[slide_1.4s_ease-in-out_infinite] bg-brand-mint/70"></div>
|
||||
<div
|
||||
class="h-full bg-brand-mint/70 motion-safe:animate-[slide_1.4s_ease-in-out_infinite]"
|
||||
></div>
|
||||
</div>
|
||||
<p class="max-w-[200px] truncate text-xs font-medium text-brand-mint/70">{filename}</p>
|
||||
<p class="text-xs text-white/40">Wird hochgeladen …</p>
|
||||
|
||||
@@ -47,7 +47,8 @@ describe('UploadZone', () => {
|
||||
render(UploadZone, {
|
||||
props: { filename: 'scan.pdf', isUploading: true, isDragging: false, error: null, onCancel }
|
||||
});
|
||||
const btn = document.querySelector('button')!;
|
||||
// Click the button inside [role="status"] — more specific than querySelector('button')
|
||||
const btn = document.querySelector('[role="status"] button') as HTMLButtonElement;
|
||||
btn.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
expect(onCancel).toHaveBeenCalledOnce();
|
||||
});
|
||||
@@ -68,6 +69,18 @@ describe('UploadZone', () => {
|
||||
});
|
||||
|
||||
describe('file selection', () => {
|
||||
it('calls onFile for a valid PDF', () => {
|
||||
const onFile = vi.fn();
|
||||
render(UploadZone, {
|
||||
props: { filename: 'scan.pdf', isUploading: false, isDragging: false, error: null, onFile }
|
||||
});
|
||||
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
|
||||
const pdf = new File(['%PDF-1.4'], 'brief.pdf', { type: 'application/pdf' });
|
||||
Object.defineProperty(input, 'files', { value: [pdf], writable: false });
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
expect(onFile).toHaveBeenCalledWith(pdf);
|
||||
});
|
||||
|
||||
it('does not call onFile for an unsupported MIME type', async () => {
|
||||
const onFile = vi.fn();
|
||||
render(UploadZone, {
|
||||
@@ -80,6 +93,23 @@ describe('UploadZone', () => {
|
||||
Object.defineProperty(input, 'files', { value: [docxFile], writable: false });
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
expect(onFile).not.toHaveBeenCalled();
|
||||
await expect
|
||||
.element(page.getByText('Dieser Dateityp wird nicht unterstützt (PDF, JPG, PNG, TIFF).'))
|
||||
.toBeVisible();
|
||||
});
|
||||
|
||||
it('does not call onFile when file exceeds 50 MB', async () => {
|
||||
const onFile = vi.fn();
|
||||
render(UploadZone, {
|
||||
props: { filename: 'scan.pdf', isUploading: false, isDragging: false, error: null, onFile }
|
||||
});
|
||||
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
|
||||
const bigFile = new File(['x'.repeat(1)], 'huge.pdf', { type: 'application/pdf' });
|
||||
Object.defineProperty(bigFile, 'size', { value: 51 * 1024 * 1024 });
|
||||
Object.defineProperty(input, 'files', { value: [bigFile], writable: false });
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
expect(onFile).not.toHaveBeenCalled();
|
||||
await expect.element(page.getByText('Die Datei ist zu groß (max. 50 MB).')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,7 +120,7 @@ async function handleReplaceFile(e: Event) {
|
||||
|
||||
<!-- Required-fields progress bar -->
|
||||
<div class="flex items-center gap-3 border-b border-line bg-surface px-6 py-1.5">
|
||||
<span class="text-[9px] font-bold tracking-widest text-ink-3 uppercase">Pflichtfelder</span>
|
||||
<span class="text-xs font-bold tracking-widest text-ink-3 uppercase">Pflichtfelder</span>
|
||||
<div
|
||||
class="h-0.5 flex-1 rounded-full bg-line"
|
||||
role="progressbar"
|
||||
@@ -134,7 +134,7 @@ async function handleReplaceFile(e: Event) {
|
||||
style="width:{requiredPct}%"
|
||||
></div>
|
||||
</div>
|
||||
<span class="text-[10px] font-bold text-brand-navy">{requiredFilled} / 3</span>
|
||||
<span class="text-xs font-bold text-brand-navy">{requiredFilled} / 3</span>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
|
||||
Reference in New Issue
Block a user