feat(ocr): add OcrProgressBar component with page-based ARIA semantics
Progress bar shows brand-mint fill on brand-sand background with smooth transition. Displays page counter with tabular-nums and skipped-pages warning in amber when applicable. Only renders when totalPages > 0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
39
frontend/src/lib/components/OcrProgressBar.svelte
Normal file
39
frontend/src/lib/components/OcrProgressBar.svelte
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let {
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
skippedPages = 0
|
||||||
|
}: {
|
||||||
|
currentPage: number;
|
||||||
|
totalPages: number;
|
||||||
|
skippedPages?: number;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let percentage = $derived((currentPage / totalPages) * 100);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if totalPages > 0}
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<div
|
||||||
|
class="bg-brand-sand mx-auto mt-4 h-2 w-full max-w-xs rounded-full"
|
||||||
|
role="progressbar"
|
||||||
|
aria-valuenow={currentPage}
|
||||||
|
aria-valuemax={totalPages}
|
||||||
|
aria-label="OCR progress"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="h-full rounded-full bg-brand-mint transition-all duration-500"
|
||||||
|
data-testid="progress-fill"
|
||||||
|
style="width: {percentage}%"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span class="mt-1 text-xs text-gray-400 tabular-nums">
|
||||||
|
{currentPage} / {totalPages}
|
||||||
|
</span>
|
||||||
|
{#if skippedPages > 0}
|
||||||
|
<span class="mt-1 text-xs text-amber-600" data-testid="skipped-warning">
|
||||||
|
{skippedPages} Seiten übersprungen
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
43
frontend/src/lib/components/OcrProgressBar.svelte.spec.ts
Normal file
43
frontend/src/lib/components/OcrProgressBar.svelte.spec.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, it, expect, afterEach } from 'vitest';
|
||||||
|
import { cleanup, render } from 'vitest-browser-svelte';
|
||||||
|
import { page } from 'vitest/browser';
|
||||||
|
import OcrProgressBar from './OcrProgressBar.svelte';
|
||||||
|
|
||||||
|
afterEach(cleanup);
|
||||||
|
|
||||||
|
describe('OcrProgressBar', () => {
|
||||||
|
it('renders progress bar with correct ARIA attributes', async () => {
|
||||||
|
render(OcrProgressBar, { currentPage: 2, totalPages: 5 });
|
||||||
|
const bar = page.getByRole('progressbar');
|
||||||
|
await expect.element(bar).toHaveAttribute('aria-valuenow', '2');
|
||||||
|
await expect.element(bar).toHaveAttribute('aria-valuemax', '5');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides progress bar when totalPages is zero', async () => {
|
||||||
|
render(OcrProgressBar, { currentPage: 0, totalPages: 0 });
|
||||||
|
await expect.element(page.getByRole('progressbar')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fills to 100 percent when current equals total', async () => {
|
||||||
|
render(OcrProgressBar, { currentPage: 5, totalPages: 5 });
|
||||||
|
const fill = page.getByTestId('progress-fill');
|
||||||
|
await expect.element(fill).toBeInTheDocument();
|
||||||
|
const el = fill.element() as HTMLElement;
|
||||||
|
expect(el.style.width).toBe('100%');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows page counter text', async () => {
|
||||||
|
render(OcrProgressBar, { currentPage: 3, totalPages: 7 });
|
||||||
|
await expect.element(page.getByText('3 / 7')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows skipped pages warning when skippedPages > 0', async () => {
|
||||||
|
render(OcrProgressBar, { currentPage: 5, totalPages: 5, skippedPages: 2 });
|
||||||
|
await expect.element(page.getByTestId('skipped-warning')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show warning when skippedPages is 0', async () => {
|
||||||
|
render(OcrProgressBar, { currentPage: 3, totalPages: 5, skippedPages: 0 });
|
||||||
|
await expect.element(page.getByTestId('skipped-warning')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user