From 9a178210fa5d752395430ae5fb4e8f5168f9e946 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 9 Jun 2026 12:50:36 +0200 Subject: [PATCH] feat(journey-editor): build JourneyAddBar with document picker and interlude draft Two add buttons: document picker (DocumentPickerDropdown) and interlude inline draft form. Interlude confirm is aria-disabled until text is non-empty. Closing one panel opens the other. Tests cover all three plan test cases. Co-Authored-By: Claude Sonnet 4.6 --- .../src/lib/geschichte/JourneyAddBar.svelte | 110 ++++++++++++++++++ .../geschichte/JourneyAddBar.svelte.spec.ts | 53 +++++++++ 2 files changed, 163 insertions(+) create mode 100644 frontend/src/lib/geschichte/JourneyAddBar.svelte create mode 100644 frontend/src/lib/geschichte/JourneyAddBar.svelte.spec.ts diff --git a/frontend/src/lib/geschichte/JourneyAddBar.svelte b/frontend/src/lib/geschichte/JourneyAddBar.svelte new file mode 100644 index 00000000..d7651de7 --- /dev/null +++ b/frontend/src/lib/geschichte/JourneyAddBar.svelte @@ -0,0 +1,110 @@ + + +
+
+ + +
+ + {#if showPicker} + + {/if} + + {#if showInterludeForm} +
+ +
+ + +
+
+ {/if} +
diff --git a/frontend/src/lib/geschichte/JourneyAddBar.svelte.spec.ts b/frontend/src/lib/geschichte/JourneyAddBar.svelte.spec.ts new file mode 100644 index 00000000..8fd7409b --- /dev/null +++ b/frontend/src/lib/geschichte/JourneyAddBar.svelte.spec.ts @@ -0,0 +1,53 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page, userEvent } from 'vitest/browser'; +import JourneyAddBar from './JourneyAddBar.svelte'; + +afterEach(() => cleanup()); + +describe('JourneyAddBar — interlude flow', () => { + it('interlude confirm button is aria-disabled until text is non-empty', async () => { + render(JourneyAddBar, { onAddDocument: vi.fn(), onAddInterlude: vi.fn() }); + + await userEvent.click(page.getByText('Zwischentext hinzufügen')); + + const confirmBtn = page.getByRole('button', { name: 'Hinzufügen' }); + await expect.element(confirmBtn).toHaveAttribute('aria-disabled', 'true'); + }); + + it('confirm becomes active after typing text', async () => { + render(JourneyAddBar, { onAddDocument: vi.fn(), onAddInterlude: vi.fn() }); + + await userEvent.click(page.getByText('Zwischentext hinzufügen')); + await userEvent.fill(page.getByRole('textbox'), 'Eine schöne Reise'); + + const confirmBtn = page.getByRole('button', { name: 'Hinzufügen' }); + await expect.element(confirmBtn).toHaveAttribute('aria-disabled', 'false'); + }); + + it('calls onAddInterlude with text on confirm', async () => { + const onAddInterlude = vi.fn(); + render(JourneyAddBar, { onAddDocument: vi.fn(), onAddInterlude }); + + await userEvent.click(page.getByText('Zwischentext hinzufügen')); + await userEvent.fill(page.getByRole('textbox'), 'Reise nach Wien'); + await userEvent.click(page.getByRole('button', { name: 'Hinzufügen' })); + + expect(onAddInterlude).toHaveBeenCalledWith('Reise nach Wien'); + }); +}); + +describe('JourneyAddBar — document picker', () => { + it('reveals picker when "Brief hinzufügen" is clicked', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue({ items: [] }) }) + ); + render(JourneyAddBar, { onAddDocument: vi.fn(), onAddInterlude: vi.fn() }); + + await userEvent.click(page.getByText('Brief hinzufügen')); + + await expect.element(page.getByRole('combobox')).toBeInTheDocument(); + vi.unstubAllGlobals(); + }); +});