diff --git a/frontend/src/lib/components/DocumentRow.svelte b/frontend/src/lib/components/DocumentRow.svelte index d4e2bd5c..2b656a65 100644 --- a/frontend/src/lib/components/DocumentRow.svelte +++ b/frontend/src/lib/components/DocumentRow.svelte @@ -4,13 +4,14 @@ import type { components } from '$lib/generated/api'; import { applyOffsets } from '$lib/search'; import { formatDate } from '$lib/utils/date'; import * as m from '$lib/paraglide/messages.js'; +import { bulkSelectionStore } from '$lib/stores/bulkSelection.svelte'; import ProgressRing from './ProgressRing.svelte'; import ContributorStack from './ContributorStack.svelte'; import DocumentThumbnail from './DocumentThumbnail.svelte'; type DocumentSearchItem = components['schemas']['DocumentSearchItem']; -let { item }: { item: DocumentSearchItem } = $props(); +let { item, canWrite = false }: { item: DocumentSearchItem; canWrite?: boolean } = $props(); const doc = $derived(item.document); const titleText = $derived(doc.title || doc.originalFilename); @@ -55,6 +56,21 @@ function safeTagColor(color: string | null | undefined): string {
+ + {#if canWrite} + + {/if} diff --git a/frontend/src/lib/components/DocumentRow.svelte.spec.ts b/frontend/src/lib/components/DocumentRow.svelte.spec.ts index 274062a6..f7d0b92a 100644 --- a/frontend/src/lib/components/DocumentRow.svelte.spec.ts +++ b/frontend/src/lib/components/DocumentRow.svelte.spec.ts @@ -3,6 +3,7 @@ import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import { goto } from '$app/navigation'; import DocumentRow from './DocumentRow.svelte'; +import { bulkSelectionStore } from '$lib/stores/bulkSelection.svelte'; import type { components } from '$lib/generated/api'; vi.mock('$app/navigation', () => ({ goto: vi.fn() })); @@ -10,6 +11,7 @@ vi.mock('$app/navigation', () => ({ goto: vi.fn() })); afterEach(() => { cleanup(); vi.mocked(goto).mockClear(); + bulkSelectionStore.clear(); }); type DocumentSearchItem = components['schemas']['DocumentSearchItem']; @@ -265,6 +267,45 @@ describe('DocumentRow – tags', () => { }); }); +// ─── Bulk-selection checkbox ───────────────────────────────────────────────── + +describe('DocumentRow – bulk selection checkbox', () => { + it('does not render the checkbox when canWrite is false', async () => { + render(DocumentRow, { item: makeItem(), canWrite: false }); + await expect.element(page.getByTestId('bulk-select-checkbox')).not.toBeInTheDocument(); + }); + + it('renders the checkbox when canWrite is true', async () => { + render(DocumentRow, { item: makeItem(), canWrite: true }); + await expect.element(page.getByTestId('bulk-select-checkbox')).toBeInTheDocument(); + }); + + it('checkbox aria-label includes the document title', async () => { + const item = makeItem({ document: { ...makeItem().document, title: 'Brief an Anna' } }); + render(DocumentRow, { item, canWrite: true }); + await expect + .element(page.getByRole('checkbox', { name: /Brief an Anna/i })) + .toBeInTheDocument(); + }); + + it('toggling the checkbox calls bulkSelectionStore.toggle', async () => { + const item = makeItem({ document: { ...makeItem().document, id: 'doc-42' } }); + render(DocumentRow, { item, canWrite: true }); + expect(bulkSelectionStore.has('doc-42')).toBe(false); + + document.querySelector('input[type="checkbox"]')?.click(); + + await expect.poll(() => bulkSelectionStore.has('doc-42')).toBe(true); + }); + + it('checked state mirrors the store', async () => { + bulkSelectionStore.add('doc-99'); + const item = makeItem({ document: { ...makeItem().document, id: 'doc-99' } }); + render(DocumentRow, { item, canWrite: true }); + await expect.element(page.getByRole('checkbox')).toBeChecked(); + }); +}); + // ─── ProgressRing & ContributorStack ───────────────────────────────────────── describe('DocumentRow – progress ring and contributors', () => { diff --git a/frontend/src/routes/DocumentList.svelte b/frontend/src/routes/DocumentList.svelte index 74478c66..d982a46a 100644 --- a/frontend/src/routes/DocumentList.svelte +++ b/frontend/src/routes/DocumentList.svelte @@ -119,7 +119,7 @@ function groupByReceiver(docItems: DocumentSearchItem[]) {
diff --git a/frontend/src/routes/enrich/+page.server.ts b/frontend/src/routes/enrich/+page.server.ts index 7cb4ea02..65d86b8f 100644 --- a/frontend/src/routes/enrich/+page.server.ts +++ b/frontend/src/routes/enrich/+page.server.ts @@ -19,5 +19,5 @@ export async function load({ const documents = result.response.ok ? (result.data ?? []) : []; - return { documents }; + return { documents, canWrite }; } diff --git a/frontend/src/routes/enrich/+page.svelte b/frontend/src/routes/enrich/+page.svelte index b2130465..e6457afd 100644 --- a/frontend/src/routes/enrich/+page.svelte +++ b/frontend/src/routes/enrich/+page.svelte @@ -1,11 +1,13 @@
@@ -61,8 +63,24 @@ const count = $derived(documents.length);