fix(web): Kalender-Ladeeffekt an monthDate statt getTime(), Stoppuhr-Takt liest nur noch Einzelwerte

Befunde 1-6 der Biome-Regel useExhaustiveDependencies (quick-260921-gof):

- calendar-widget.tsx: useMemo um resolveCalendarConfig() entfernt
  (reine Funktion, spart nichts). showToday setzt monthDate jetzt
  identitaetserhaltend, wenn der aktuelle Monat schon angezeigt wird -
  erst danach durfte der Ladeeffekt von monthDate.getTime() auf
  monthDate umgestellt werden, sonst haette jeder Druck auf den
  Monatsknopf im laufenden Monat einen Termin-Abruf bis zum
  Exchange-Server ausgeloest (D-04).
- stopwatch-widget.tsx: neue reine Hilfsfunktion computeElapsedFrom()
  fuer den Takt-Effekt, der jetzt nur noch drei Einzelwerte statt des
  ganzen sw-Objekts liest - eine sw-Abhaengigkeit haette den 100-ms-Takt
  bei jeder aufgezeichneten Runde ab- und wiederaufgebaut.
- Testerweiterungen als Rueckfallsicherungen: 2x weiterblaettern -> 3
  Abrufe, 3x Monatsknopf im laufenden Monat -> kein Zusatzabruf; Runde
  waehrend die Stoppuhr laeuft unterbricht den Takt nicht, genau 1 PATCH
  je Klick.
- Wirkungslose eslint-disable-Zeilen fuer diese Regel entfallen (kein
  ESLint mehr im Projekt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 12:23:35 +02:00
parent 54fdf699dc
commit b3f0e3cdcd
4 changed files with 122 additions and 19 deletions
@@ -196,6 +196,39 @@ describe('StopwatchWidget', () => {
expect(totalSeconds).toBeGreaterThanOrEqual(5);
});
it('quick-260921-gof: Runde unterbricht den Takt nicht — Anzeige laeuft danach weiter statt zurueckzufallen, und pro Klick geht genau ein PATCH (Rueckfallsicherung gegen sw als Effekt-Abhaengigkeit)', async () => {
render(<StopwatchWidget instanceId="sw-lap-tick" config={{}} isEditMode={false} />);
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /stopwatch\.start/i }));
});
await act(async () => {
vi.advanceTimersByTime(2000);
});
const beforeLap = screen.getByTestId('stopwatch-display').textContent;
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /stopwatch\.lap/i }));
});
await act(async () => {
vi.advanceTimersByTime(2000);
});
const afterLap = screen.getByTestId('stopwatch-display').textContent;
// Anzeige muss ueber die Runde hinweg weiterlaufen, nicht auf 00:00
// zurueckfallen und nicht stehen bleiben.
expect(afterLap).not.toBe('00:00');
expect(afterLap).not.toBe(beforeLap);
// Genau ein PATCH je Klick: Start + Runde = 2, kein Dauerfeuer durch ein
// Ab- und Wiederaufbauen des Takt-Intervalls beim Aufzeichnen der Runde.
expect(updateWidgetConfig).toHaveBeenCalledTimes(2);
});
it('quick-260916-bwo: die Anzeige skaliert per Container-Query-Klasse, keine Fensterbreiten-Formel im Style', () => {
render(<StopwatchWidget instanceId="sw-cq" config={{}} isEditMode={false} />);