From 4c2495bb64d759e9e795bc3bda78b6cba39a7817 Mon Sep 17 00:00:00 2001 From: Schalli Date: Wed, 16 Sep 2026 13:55:18 +0200 Subject: [PATCH] fix(web): Notiz-Widget folgt dem Hell/Dunkel-Schalter von Tessera statt der Systemeinstellung - useTheme (next-themes) + mounted-Guard wie changelog-view.tsx - data-color-mode am Wurzel-Div folgt resolvedTheme statt fest 'auto' - zwei neue Tests (dark/light) via vi.hoisted-Themenzustand Co-Authored-By: Claude Opus 5 (1M context) --- .../dashboard/widgets/note-widget.test.tsx | 31 +++++++++++++++++++ .../dashboard/widgets/note-widget.tsx | 14 ++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/dashboard/widgets/note-widget.test.tsx b/apps/web/src/components/dashboard/widgets/note-widget.test.tsx index a6a9873..77fb4fc 100644 --- a/apps/web/src/components/dashboard/widgets/note-widget.test.tsx +++ b/apps/web/src/components/dashboard/widgets/note-widget.test.tsx @@ -12,6 +12,14 @@ vi.mock('next-intl', () => ({ }, })); +// quick-260916-j4f: gehobener Themenzustand, damit die Variable trotz +// Hoisting der Mocks und des statischen Imports weiter unten sicher +// initialisiert ist (Muster changelog-page.test.tsx). +const themeMock = vi.hoisted(() => ({ resolvedTheme: 'light' as 'light' | 'dark' })); +vi.mock('next-themes', () => ({ + useTheme: () => ({ resolvedTheme: themeMock.resolvedTheme }), +})); + // Mock @uiw/react-md-editor — provide a minimal textarea-based substitute. // Im Modus 'preview' wird stattdessen ein Vorschau-Div gerendert, das fuer // jede Aufgabenzeile ein Kaestchen enthaelt (quick-260916-iex) — nutzt die @@ -85,6 +93,7 @@ describe('NoteWidget', () => { beforeEach(() => { vi.useFakeTimers(); + themeMock.resolvedTheme = 'light'; fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, json: async () => ({}), @@ -290,4 +299,26 @@ describe('NoteWidget', () => { }); expect(fetchSpy).toHaveBeenCalledTimes(1); }); + + it('setzt data-color-mode auf dark, wenn Tessera auf Dunkel steht', () => { + themeMock.resolvedTheme = 'dark'; + const { container } = render( + , + ); + + expect(container.querySelector('[data-color-mode]')?.getAttribute('data-color-mode')).toBe( + 'dark', + ); + }); + + it('setzt data-color-mode auf light, wenn Tessera auf Hell steht', () => { + themeMock.resolvedTheme = 'light'; + const { container } = render( + , + ); + + expect(container.querySelector('[data-color-mode]')?.getAttribute('data-color-mode')).toBe( + 'light', + ); + }); }); diff --git a/apps/web/src/components/dashboard/widgets/note-widget.tsx b/apps/web/src/components/dashboard/widgets/note-widget.tsx index 3adc6c7..1ff1479 100644 --- a/apps/web/src/components/dashboard/widgets/note-widget.tsx +++ b/apps/web/src/components/dashboard/widgets/note-widget.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslations } from 'next-intl'; +import { useTheme } from 'next-themes'; import MDEditor, { commands } from '@uiw/react-md-editor'; import rehypeSanitize from 'rehype-sanitize'; import type { WidgetProps } from '../widget-registry'; @@ -41,6 +42,17 @@ const NOTE_COMMANDS = [ export function NoteWidget({ instanceId, config }: WidgetProps) { const t = useTranslations('widgets'); + const { resolvedTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + // quick-260916-j4f: folgt dem Tessera-Schalter statt der + // Betriebssystem-Einstellung, Muster changelog-view.tsx. + const colorMode: 'light' | 'dark' = mounted && resolvedTheme === 'dark' ? 'dark' : 'light'; + const [title, setTitle] = useState( (config.title as string) || t('note.defaultTitle'), ); @@ -128,7 +140,7 @@ export function NoteWidget({ instanceId, config }: WidgetProps) { ); return ( -
+
{/* Header: title + pencil toggle */}