From 60326cfb0a828446d6daec0575e4a89ee88e9530 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 8 May 2026 17:10:20 +0200 Subject: [PATCH] refactor(dashboard): ReaderDraftsModule mint left-border, card-head, row structure (TDD, #483) Co-Authored-By: Claude Sonnet 4.6 --- .../dashboard/ReaderDraftsModule.svelte | 43 ++++++++++++---- .../ReaderDraftsModule.svelte.spec.ts | 49 +++++++++++++++++-- 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte b/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte index a03899e1..673ba7ec 100644 --- a/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte +++ b/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte @@ -12,24 +12,47 @@ interface Props { const { drafts }: Props = $props(); -
-

- {m.dashboard_reader_drafts_heading()} -

+
+ +
+

+ {m.dashboard_reader_drafts_heading()} +

+
+ {#if drafts.length === 0} -

{m.dashboard_reader_drafts_empty()}

+

{m.dashboard_reader_drafts_empty()}

{:else} -
    +
      {#each drafts as draft (draft.id)}
    • - {draft.title} - - {relativeTimeDe(new Date(draft.updatedAt))} + + {draft.title} + + {m.dashboard_reader_draft_meta({ relative: relativeTimeDe(new Date(draft.updatedAt)) })} + +
    • {/each} diff --git a/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte.spec.ts b/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte.spec.ts index 9d4beab8..44a8e43a 100644 --- a/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte.spec.ts +++ b/frontend/src/lib/shared/dashboard/ReaderDraftsModule.svelte.spec.ts @@ -36,10 +36,12 @@ describe('ReaderDraftsModule', () => { await expect.element(link2).toHaveAttribute('href', '/geschichten/g2/edit'); }); - it('shows heading "Meine Entwürfe"', async () => { + it('shows heading as h3 (not h2)', async () => { render(ReaderDraftsModule, { drafts: [draft1] }); - const heading = page.getByRole('heading', { name: /Meine Entwürfe/i }); - await expect.element(heading).toBeInTheDocument(); + const h3 = page.getByRole('heading', { level: 3 }); + await expect.element(h3).toBeInTheDocument(); + const h2 = page.getByRole('heading', { level: 2 }); + await expect.element(h2).not.toBeInTheDocument(); }); it('shows empty state when drafts is empty', async () => { @@ -53,4 +55,45 @@ describe('ReaderDraftsModule', () => { const emptyText = page.getByText(/Keine Entwürfe/i); await expect.element(emptyText).not.toBeInTheDocument(); }); + + it('card wrapper has mint left-border classes', async () => { + render(ReaderDraftsModule, { drafts: [draft1] }); + const h3 = page.getByRole('heading', { level: 3 }); + const card = ((await h3.element()) as HTMLElement).closest('div[class]'); + const rootCard = card?.parentElement; + const cls = rootCard?.className ?? ''; + expect(cls).toMatch(/border-l-\[3px\]/); + expect(cls).toMatch(/border-l-brand-mint/); + }); + + it('draft-row link has min-h-[44px] touch target', async () => { + render(ReaderDraftsModule, { drafts: [draft1] }); + const link = page.getByRole('link', { name: /Mein erster Entwurf/ }); + const cls = ((await link.element()) as HTMLElement).className; + expect(cls).toMatch(/min-h-\[44px\]/); + }); + + it('draft title has text-brand-navy class', async () => { + render(ReaderDraftsModule, { drafts: [draft1] }); + const link = page.getByRole('link', { name: /Mein erster Entwurf/ }); + const el = (await link.element()) as HTMLElement; + const titleEl = el.querySelector('[class*="text-brand-navy"]'); + expect(titleEl).not.toBeNull(); + expect(titleEl?.textContent?.trim()).toBe('Mein erster Entwurf'); + }); + + it('draft meta contains "Entwurf" text', async () => { + render(ReaderDraftsModule, { drafts: [draft1] }); + const link = page.getByRole('link', { name: /Mein erster Entwurf/ }); + const el = (await link.element()) as HTMLElement; + expect(el.textContent).toMatch(/Entwurf/); + }); + + it('chevron SVG is present in each draft row', async () => { + render(ReaderDraftsModule, { drafts: [draft1] }); + const link = page.getByRole('link', { name: /Mein erster Entwurf/ }); + const el = (await link.element()) as HTMLElement; + const svg = el.querySelector('svg'); + expect(svg).not.toBeNull(); + }); });