fix(ui): Dialoge weichen der Bildschirmtastatur aus

Auf dem Telefon verdeckte die Tastatur den halben Dialog – bei der
Schnellerfassung genau das Eingabefeld, um das es geht.

Ursache ist der Unterschied zwischen Layout- und Sichtfenster: die Tastatur
schiebt sich darueber, ohne dass sich fuer die Seite etwas aendert.
"position: fixed" und "100vh" beziehen sich weiter auf das volle Fenster,
ein mittig stehender Dialog liegt damit zur Haelfte darunter.

Der Dialog richtet sich jetzt nach window.visualViewport – Hoehe und oberer
Rand kommen von dort und werden bei jeder Aenderung nachgefuehrt. Weil der
Behaelter scrollbar ist, holt der Browser das fokussierte Feld dann von
selbst ins Bild. Das gilt fuer alle Dialoge, nicht nur die Schnellerfassung.

Dazu, weil es beim Nachsehen auffiel: Aufgaben lassen sich in der
Uebersicht direkt bearbeiten. Bisher fuehrte von dort nur der Weg ins
Projekt, und wer eine Faelligkeit aendern wollte, musste die Aufgabe im
Brett wiederfinden. Der Dialog nimmt dafuer eine schlankere Form entgegen
als den vollstaendigen Prisma-Datensatz.

Geprueft mit einem nachgestellten Sichtfenster: Eingabefeld und Absende-
knopf liegen bei 375x667 und aufgeklappter Tastatur vollstaendig im freien
Bereich. Vorher lagen beide darunter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-15 00:34:56 +02:00
co-authored by Claude Opus 5
parent c9202fb65e
commit 6a7365946b
6 changed files with 223 additions and 10 deletions
+40
View File
@@ -163,6 +163,46 @@ test("Schnellerfassung legt Idee und Aufgabe mit einem Feld an", async ({ page }
await expect(page.getByText("Aus der Bahn notiert")).toBeVisible();
});
test("Dialoge weichen der Bildschirmtastatur aus", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.getByRole("button", { name: "Schnell erfassen" }).click();
await expect(page.getByRole("dialog")).toBeVisible();
const TASTATUR = 336;
/**
* Eine echte Tastatur laesst sich nicht einblenden. Stattdessen meldet
* visualViewport eine kleinere Hoehe – genau die Schnittstelle, an der
* der Dialog haengt. Ueberschrieben wird am echten Objekt: der Dialog
* merkt sich beim Oeffnen eine Referenz darauf.
*/
await page.evaluate((tastatur) => {
const fenster = window.visualViewport;
if (!fenster) throw new Error("visualViewport fehlt");
const hoehe = window.innerHeight - tastatur;
Object.defineProperty(fenster, "height", { get: () => hoehe, configurable: true });
Object.defineProperty(fenster, "offsetTop", { get: () => 0, configurable: true });
fenster.dispatchEvent(new Event("resize"));
}, TASTATUR);
await page.waitForTimeout(300);
// Eingabefeld und Absendeknopf muessen oberhalb der Tastatur liegen.
// Vorher stand der Dialog mittig im vollen Fenster – und damit zur
// Haelfte darunter.
const frei = 667 - TASTATUR;
for (const ziel of [
page.locator("#quick-title"),
page.getByRole("dialog").getByRole("button", { name: "Anlegen" }),
]) {
const kasten = await ziel.boundingBox();
expect(kasten).not.toBeNull();
expect(kasten!.y + kasten!.height).toBeLessThanOrEqual(frei);
}
await page.keyboard.press("Escape");
});
test("Aufgabenuebersicht sammelt ueber alle Projekte und hakt ab", async ({ page }) => {
await page.getByRole("link", { name: "Aufgaben", exact: true }).click();
await expect(page.getByRole("heading", { name: "Aufgaben", level: 1 })).toBeVisible();