@@ -123,12 +133,17 @@ const showDividers = $derived(groupedDocuments.length >= 2);
{/if}
- {#if snippet}
+ {#if snippetSegments}
- Fundstelle: {snippet}
+ Fundstelle:
+ {#each snippetSegments as seg, i (i)}{#if seg.highlight}{seg.text}{:else}{seg.text}{/if}{/each}
{/if}
@@ -140,7 +155,15 @@ const showDividers = $derived(groupedDocuments.length >= 2);
>{m.docs_list_from()}
{#if doc.sender}
- {doc.sender.displayName}
+ {#if senderMatched}
+ {doc.sender.displayName}
+ {:else}
+ {doc.sender.displayName}
+ {/if}
{:else}
{m.docs_list_unknown()}
{/if}
@@ -152,7 +175,18 @@ const showDividers = $derived(groupedDocuments.length >= 2);
>
{#if doc.receivers && doc.receivers.length > 0}
- {doc.receivers.map((p) => p.displayName).join(', ')}
+ {#each doc.receivers as receiver, ri (receiver.id ?? ri)}
+ {#if ri > 0}, {/if}
+ {#if receiver.id && matchedReceiverIds.has(receiver.id)}
+ {receiver.displayName}
+ {:else}
+ {receiver.displayName}
+ {/if}
+ {/each}
{:else}
{m.docs_list_unknown()}
@@ -166,14 +200,18 @@ const showDividers = $derived(groupedDocuments.length >= 2);
{#each doc.tags as tag (tag.id)}
{/each}
diff --git a/frontend/src/routes/DocumentList.svelte.spec.ts b/frontend/src/routes/DocumentList.svelte.spec.ts
index f3c1f293..538de391 100644
--- a/frontend/src/routes/DocumentList.svelte.spec.ts
+++ b/frontend/src/routes/DocumentList.svelte.spec.ts
@@ -23,8 +23,9 @@ type DocOverrides = {
id?: string;
title?: string;
documentDate?: string | null;
- sender?: { firstName?: string | null; lastName: string; displayName: string } | null;
- receivers?: { firstName?: string | null; lastName: string; displayName: string }[];
+ sender?: { id?: string; firstName?: string | null; lastName: string; displayName: string } | null;
+ receivers?: { id?: string; firstName?: string | null; lastName: string; displayName: string }[];
+ tags?: { id: string; name: string }[];
};
const makeDoc = (overrides: DocOverrides = {}) => ({
@@ -35,7 +36,12 @@ const makeDoc = (overrides: DocOverrides = {}) => ({
documentDate: '2024-03-15',
location: null,
sender: null,
- receivers: [] as { firstName?: string | null; lastName: string; displayName: string }[],
+ receivers: [] as {
+ id?: string;
+ firstName?: string | null;
+ lastName: string;
+ displayName: string;
+ }[],
tags: [],
...overrides
});
@@ -136,7 +142,8 @@ describe('DocumentList – match snippets and highlights', () => {
titleOffsets: [],
senderMatched: false,
matchedReceiverIds: [],
- matchedTagIds: []
+ matchedTagIds: [],
+ snippetOffsets: []
}
}
});
@@ -161,7 +168,8 @@ describe('DocumentList – match snippets and highlights', () => {
titleOffsets: [{ start: 0, length: 5 }], // "Brief"
senderMatched: false,
matchedReceiverIds: [],
- matchedTagIds: []
+ matchedTagIds: [],
+ snippetOffsets: []
}
}
});
@@ -183,11 +191,174 @@ describe('DocumentList – match snippets and highlights', () => {
titleOffsets: [],
senderMatched: false,
matchedReceiverIds: [],
- matchedTagIds: []
+ matchedTagIds: [],
+ snippetOffsets: []
}
}
});
await expect.element(page.getByRole('mark')).not.toBeInTheDocument();
await expect.element(page.getByText('Brief an Anna')).toBeInTheDocument();
});
+
+ it('renders inside snippet when snippetOffsets are present', async () => {
+ const doc = makeDoc({ id: 'doc1' });
+ render(DocumentList, {
+ ...baseProps,
+ documents: [doc],
+ total: 1,
+ matchData: {
+ doc1: {
+ transcriptionSnippet: 'Er schrieb einen Brief',
+ titleOffsets: [],
+ senderMatched: false,
+ matchedReceiverIds: [],
+ matchedTagIds: [],
+ snippetOffsets: [{ start: 17, length: 5 }] // "Brief"
+ }
+ }
+ });
+ const snippet = page.getByTestId('search-snippet');
+ await expect.element(snippet).toBeInTheDocument();
+ const mark = snippet.getByRole('mark');
+ await expect.element(mark).toBeInTheDocument();
+ await expect.element(mark).toHaveTextContent('Brief');
+ });
+
+ it('renders snippet as plain text when snippetOffsets is empty', async () => {
+ const doc = makeDoc({ id: 'doc1' });
+ render(DocumentList, {
+ ...baseProps,
+ documents: [doc],
+ total: 1,
+ matchData: {
+ doc1: {
+ transcriptionSnippet: 'Er schrieb einen Brief',
+ titleOffsets: [],
+ senderMatched: false,
+ matchedReceiverIds: [],
+ matchedTagIds: [],
+ snippetOffsets: []
+ }
+ }
+ });
+ const snippet = page.getByTestId('search-snippet');
+ await expect.element(snippet).toBeInTheDocument();
+ // No mark elements inside the snippet when offsets is empty
+ await expect.element(snippet.getByRole('mark')).not.toBeInTheDocument();
+ });
+
+ it('visually marks sender when senderMatched is true', async () => {
+ const doc = makeDoc({
+ id: 'doc1',
+ sender: {
+ id: 'sender-1',
+ firstName: 'Walter',
+ lastName: 'Raddatz',
+ displayName: 'Walter Raddatz'
+ }
+ });
+ render(DocumentList, {
+ ...baseProps,
+ documents: [doc],
+ total: 1,
+ matchData: {
+ doc1: {
+ transcriptionSnippet: undefined,
+ titleOffsets: [],
+ senderMatched: true,
+ matchedReceiverIds: [],
+ matchedTagIds: [],
+ snippetOffsets: []
+ }
+ }
+ });
+ const senderMark = page.getByTestId('sender-match');
+ await expect.element(senderMark).toBeInTheDocument();
+ await expect.element(senderMark).toHaveTextContent('Walter Raddatz');
+ });
+
+ it('does not mark sender when senderMatched is false', async () => {
+ const doc = makeDoc({
+ id: 'doc1',
+ sender: {
+ id: 'sender-1',
+ firstName: 'Walter',
+ lastName: 'Raddatz',
+ displayName: 'Walter Raddatz'
+ }
+ });
+ render(DocumentList, {
+ ...baseProps,
+ documents: [doc],
+ total: 1,
+ matchData: {
+ doc1: {
+ transcriptionSnippet: undefined,
+ titleOffsets: [],
+ senderMatched: false,
+ matchedReceiverIds: [],
+ matchedTagIds: [],
+ snippetOffsets: []
+ }
+ }
+ });
+ await expect.element(page.getByTestId('sender-match')).not.toBeInTheDocument();
+ });
+
+ it('visually marks matched receiver when their id is in matchedReceiverIds', async () => {
+ const doc = makeDoc({
+ id: 'doc1',
+ receivers: [
+ { id: 'p-1', firstName: 'Anna', lastName: 'Schmidt', displayName: 'Anna Schmidt' },
+ { id: 'p-2', firstName: 'Karl', lastName: 'Bauer', displayName: 'Karl Bauer' }
+ ]
+ });
+ render(DocumentList, {
+ ...baseProps,
+ documents: [doc],
+ total: 1,
+ matchData: {
+ doc1: {
+ transcriptionSnippet: undefined,
+ titleOffsets: [],
+ senderMatched: false,
+ matchedReceiverIds: ['p-1'],
+ matchedTagIds: [],
+ snippetOffsets: []
+ }
+ }
+ });
+ // Only Anna Schmidt should be marked
+ const receiverMark = page.getByTestId('receiver-match');
+ await expect.element(receiverMark).toBeInTheDocument();
+ await expect.element(receiverMark).toHaveTextContent('Anna Schmidt');
+ });
+
+ it('visually marks matched tag when its id is in matchedTagIds', async () => {
+ const doc = makeDoc({
+ id: 'doc1',
+ tags: [
+ { id: 'tag-1', name: 'Familiengeschichte' },
+ { id: 'tag-2', name: 'Reise' }
+ ]
+ });
+ render(DocumentList, {
+ ...baseProps,
+ documents: [doc],
+ total: 1,
+ matchData: {
+ doc1: {
+ transcriptionSnippet: undefined,
+ titleOffsets: [],
+ senderMatched: false,
+ matchedReceiverIds: [],
+ matchedTagIds: ['tag-1'],
+ snippetOffsets: []
+ }
+ }
+ });
+ const tagMark = page.getByTestId('tag-match');
+ await expect.element(tagMark).toBeInTheDocument();
+ await expect.element(tagMark).toHaveTextContent('Familiengeschichte');
+ });
});