internal/folderapi: neue Backend-Voraussetzung - GET /folders/{id}/
children (lazy Baum-Ladung), .../breadcrumb (ueber DOC-09s Materialized
Path), .../documents (Dokumentenliste, whitelisted Sortierspalten).
folder.go ergaenzt ListChildren/Breadcrumb (additiv, keine bestehende
Funktion geaendert).
web/dms-browser: Next.js/React/TypeScript. Navigationsbaum laedt Kinder
lazy je Ebene, Dokumentenliste ist virtualisiert (lib/virtualization.ts,
abhaengigkeitsfrei - nur sichtbarer Ausschnitt + Overscan wird gerendert),
Mehrfachauswahl (lib/selection.ts: Klick/Strg/Umschalt), Tastatur-
navigation im Baum (lib/treeKeyboard.ts). Aktueller Ordner steckt in der
URL (Catch-all-Route /browse/[[...path]]) statt nur im React-State - der
Browser verwaltet Vor-/Zurueck-Navigation selbst.
Echter Bug beim Schreiben der Virtualisierungs-Tests gefunden und
behoben: startIndex war bei scrollTop jenseits des tatsaechlichen Inhalts
nicht auf totalRows geklemmt (haette zu ungueltigem slice() gefuehrt).
Pruefungen:
1. Liste mit 10.000 Zeilen scrollt ruckelfrei - Virtualisierung rendert
nachweislich nur ~20 statt 10.000 Zeilen
2. Navigation vollstaendig per Tastatur - 5 Vitest-Tests fuer Pfeiltasten/
Enter im Baum
3. Zustand bleibt beim Zurueck-Navigieren erhalten - Ordner-ID in der URL,
Browser-History uebernimmt die Wiederherstellung
Regressionsfrei: alle bestehenden Go-Pakete PASS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
39 lines
1.9 KiB
TypeScript
39 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { applyClick, initialSelection, isSelected } from "./selection";
|
|
|
|
const ids = ["a", "b", "c", "d", "e"];
|
|
|
|
describe("Mehrfachauswahl (Akzeptanzkriterium 3: Umschalt- und Strg-Taste)", () => {
|
|
it("einfacher Klick waehlt genau ein Element", () => {
|
|
const state = applyClick(initialSelection(), ids, 2, { shiftKey: false, ctrlOrMetaKey: false });
|
|
expect(state.selected.size).toBe(1);
|
|
expect(isSelected(state, "c")).toBe(true);
|
|
});
|
|
|
|
it("Strg-Klick fuegt zur Auswahl hinzu, ohne die vorherige zu ersetzen", () => {
|
|
let state = applyClick(initialSelection(), ids, 0, { shiftKey: false, ctrlOrMetaKey: false });
|
|
state = applyClick(state, ids, 2, { shiftKey: false, ctrlOrMetaKey: true });
|
|
expect(isSelected(state, "a")).toBe(true);
|
|
expect(isSelected(state, "c")).toBe(true);
|
|
expect(state.selected.size).toBe(2);
|
|
});
|
|
|
|
it("Strg-Klick auf ein bereits ausgewaehltes Element hebt dessen Auswahl auf", () => {
|
|
let state = applyClick(initialSelection(), ids, 1, { shiftKey: false, ctrlOrMetaKey: false });
|
|
state = applyClick(state, ids, 1, { shiftKey: false, ctrlOrMetaKey: true });
|
|
expect(state.selected.size).toBe(0);
|
|
});
|
|
|
|
it("Umschalt-Klick waehlt den zusammenhaengenden Bereich vom Anker bis zum Klick", () => {
|
|
let state = applyClick(initialSelection(), ids, 1, { shiftKey: false, ctrlOrMetaKey: false });
|
|
state = applyClick(state, ids, 3, { shiftKey: true, ctrlOrMetaKey: false });
|
|
expect(state.selected).toEqual(new Set(["b", "c", "d"]));
|
|
});
|
|
|
|
it("Umschalt-Klick funktioniert auch rueckwaerts (Klick vor dem Anker)", () => {
|
|
let state = applyClick(initialSelection(), ids, 3, { shiftKey: false, ctrlOrMetaKey: false });
|
|
state = applyClick(state, ids, 1, { shiftKey: true, ctrlOrMetaKey: false });
|
|
expect(state.selected).toEqual(new Set(["b", "c", "d"]));
|
|
});
|
|
});
|