diff --git a/frontend/src/lib/components/EnrichmentBlock.svelte b/frontend/src/lib/components/EnrichmentBlock.svelte
new file mode 100644
index 00000000..2743329f
--- /dev/null
+++ b/frontend/src/lib/components/EnrichmentBlock.svelte
@@ -0,0 +1,39 @@
+
+
+{#if showBlock}
+
+ {#if bannerCount > 0}
+
+ {/if}
+ {#if topDocs.length > 0}
+
+ {:else if showSkeleton}
+
+ {/if}
+
+{/if}
diff --git a/frontend/src/lib/components/EnrichmentBlock.svelte.spec.ts b/frontend/src/lib/components/EnrichmentBlock.svelte.spec.ts
new file mode 100644
index 00000000..be187cf5
--- /dev/null
+++ b/frontend/src/lib/components/EnrichmentBlock.svelte.spec.ts
@@ -0,0 +1,61 @@
+import { describe, it, expect, afterEach, vi } from 'vitest';
+import { cleanup, render } from 'vitest-browser-svelte';
+import { page } from 'vitest/browser';
+
+import EnrichmentBlock from './EnrichmentBlock.svelte';
+
+vi.mock('$app/stores', async () => {
+ const { writable } = await import('svelte/store');
+ return { navigating: writable(null) };
+});
+
+afterEach(cleanup);
+
+type Doc = { id: string; title: string; uploadedAt: string };
+
+function doc(id: string, title = 'Doc'): Doc {
+ return { id, title, uploadedAt: '2026-04-20T12:00:00' };
+}
+
+describe('EnrichmentBlock', () => {
+ it('renders nothing when topDocs is empty and banner count is 0', async () => {
+ render(EnrichmentBlock, {
+ topDocs: [],
+ totalCount: 0,
+ bannerCount: 0,
+ onBannerClose: vi.fn()
+ });
+ await expect.element(page.getByTestId('enrichment-block')).not.toBeInTheDocument();
+ });
+
+ it('renders the list component when topDocs is non-empty', async () => {
+ render(EnrichmentBlock, {
+ topDocs: [doc('d1')],
+ totalCount: 1,
+ bannerCount: 0,
+ onBannerClose: vi.fn()
+ });
+ await expect.element(page.getByTestId('dashboard-needs-metadata')).toBeInTheDocument();
+ });
+
+ it('renders the banner when bannerCount > 0', async () => {
+ render(EnrichmentBlock, {
+ topDocs: [],
+ totalCount: 0,
+ bannerCount: 3,
+ onBannerClose: vi.fn()
+ });
+ await expect.element(page.getByRole('status')).toBeInTheDocument();
+ });
+
+ it('composes banner + list when both are present', async () => {
+ render(EnrichmentBlock, {
+ topDocs: [doc('d1')],
+ totalCount: 1,
+ bannerCount: 2,
+ onBannerClose: vi.fn()
+ });
+ await expect.element(page.getByRole('status')).toBeInTheDocument();
+ await expect.element(page.getByTestId('dashboard-needs-metadata')).toBeInTheDocument();
+ });
+});