Das Aufgaben-Formular: was aufklappt, bekommt jetzt auch Platz
Filipe, 22.09.2026, zum Bildschirmfoto: „wie scheisse sieht das aus,
verbesser das bitte." Zu sehen: „Anlegen" und „Abbrechen" lagen mitten
in der Personenliste.
GEMESSEN statt geraten, bei 1280 px mit aufgeklappter Liste:
Zelle Zeilen Hoehe Inhalt
feld-verant 24px 44px 68 107
feld-mehrere 43px 44px 87 261 <-- 174 zu viel
Jede Zelle des Formularrasters bekommt zwei Zeilen: Beschriftung oben,
Eingabe unten mit festen 44 px. Das ist richtig und der Grund, warum
alle Eingaben auf einer Linie sitzen (17.09.).
`feld-mehrere` ist aber keine Beschriftung mit Eingabe, sondern ein
Knopf mit einer Liste, die aufklappt. Ihr zweites Kind landet in der
44-Pixel-Zeile und laeuft heraus, sobald jemand sie oeffnet. Was
herauslaeuft, belegt keinen Platz -- also legt es sich ueber das
Naechste, und das Naechste sind die Knoepfe.
DIE MESSUNG HAT MICH VOR DEM NAHELIEGENDEN FEHLER BEWAHRT.
Erster Gedanke: „Zellen ohne eigene Eingabe brauchen die zwei Zeilen
nicht" -- `:has(> input, > select, > textarea)`. Die Messung sagt etwas
anderes: Von sieben Zellen haben SECHS ihre Eingabe nicht als direktes
Kind, weil der Auswahl-Baustein sie in ein <div> wickelt. Die Regel
haette fast das ganze Formular getroffen und genau die Ausrichtung
zerstoert, die sie schuetzen soll.
`aria-expanded` ist gemessen das einzige Merkmal, das nur bei dieser
Zelle steht -- und es ist das inhaltlich richtige: Es sagt „dieser
Bereich kann groesser werden". Etwas, das groesser werden kann, darf
keine feste Hoehe haben.
UND DIE PRUEFUNG, DIE ES HAETTE FINDEN MUESSEN
pruef-formulare war gruen -- sie klappt das Feld nie auf. Ein Zustand,
der nie hergestellt wird, kann nicht gemessen werden.
Neuer Abschnitt: Jedes Element mit `aria-expanded` im Formular wird
geoeffnet, danach darf keine Rasterzelle mehr Inhalt haben, als sie
hoch ist. Nicht diese eine Stelle, sondern die Eigenschaft -- damit
faellt auch die naechste auf, die es noch gar nicht gibt.
ZWEI ANLAEUFE DABEI WAREN FALSCH, und der zweite war der gefaehrliche:
1. `scrollHeight` der Zelle meldete elf Zellen als kaputt, die alle
in Ordnung sind: Der Hinweis unter einem Feld haengt seit dem
17.09. absichtlich absolut darunter. Eine Warnung, die bei
richtigem Verhalten anschlaegt, wird abgeschaltet.
2. Nur die direkten Kinder im Fluss -- das war gruen, AUCH MIT DEM
ECHTEN FEHLER. Nachgemessen mit zurueckgenommener Behebung:
immer noch gruen. Das Raster staucht das Kind auf die feste
Zeilenhoehe, das Kind bleibt also brav in der Zelle; was
herauslaeuft, ist der Inhalt darin.
Jetzt misst sie in die Tiefe (ohne Teilbaeume unter absolut gesetzten
Elementen und ohne eigene Rollbereiche) und ist beidseitig belegt:
mit Behebung -> gruen
ohne Behebung -> FEHL „feld-mehrere: Inhalt reicht bis 169 px,
Zelle ist 87 px hoch"
Dazu eine Gegenprobe, die eine Zelle kuenstlich einklemmt.
Ueberlappungen im Formular: von 12 auf 4 -- und die vier sind Absicht
(der echte <select> liegt unsichtbar ueber seinem Knopf).
pruef-formulare, pruef-aufgabenbrett, pruef-css-klassen gruen.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -310,6 +310,181 @@ console.log("\n=== Kein Feld ohne Gestaltung");
|
||||
await kontext.close();
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
WAS AUFKLAPPT, MUSS AUCH PLATZ BEKOMMEN (22.09.2026)
|
||||
=====================================================================
|
||||
Filipe zum Bildschirmfoto: „wie scheisse sieht das aus, verbesser das
|
||||
bitte." Zu sehen war das Aufgaben-Formular: „Anlegen" und
|
||||
„Abbrechen" lagen mitten in der Personenliste.
|
||||
|
||||
WARUM DIE PRUEFUNG DARUEBER DAS NICHT FINDEN KONNTE: Sie oeffnet das
|
||||
Formular und misst die Felder -- im zugeklappten Zustand. Der Fehler
|
||||
entsteht erst beim AUFKLAPPEN. Ein Zustand, der nie hergestellt
|
||||
wird, kann nicht gemessen werden; die Pruefung war gruen und hat
|
||||
dabei nie hingesehen.
|
||||
|
||||
Gemessen war es dann eindeutig, bei 1280 px:
|
||||
|
||||
Zelle Zeilen Hoehe Inhalt
|
||||
feld-verant 24px 44px 68 107
|
||||
feld-mehrere 43px 44px 87 261 <-- 174 zu viel
|
||||
|
||||
Die Zelle steckte in einer festen 44-Pixel-Zeile. Was herauslaeuft,
|
||||
belegt keinen Platz -- also legt es sich ueber das Naechste.
|
||||
|
||||
HIER WIRD NICHT DIESE EINE STELLE GEPRUEFT, sondern die Eigenschaft:
|
||||
Jedes Element mit `aria-expanded` im Formular wird aufgeklappt, und
|
||||
danach darf keine Rasterzelle mehr Inhalt haben, als sie hoch ist.
|
||||
Damit faellt auch die naechste auf, die es noch gar nicht gibt.
|
||||
===================================================================== */
|
||||
console.log("\n=== Was aufklappt, bekommt auch Platz");
|
||||
{
|
||||
const kontext = await browser.newContext({ viewport: { width: 1280, height: 1000 } });
|
||||
const seite = await kontext.newPage();
|
||||
await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" });
|
||||
await seite.click('.rolle[data-rolle="admin"]');
|
||||
await seite.fill("#code", "CODE-DOGI-0001");
|
||||
await seite.click("button[type=submit]");
|
||||
await seite.waitForTimeout(900);
|
||||
|
||||
let zellenGesamt = 0, aufgeklapptGesamt = 0;
|
||||
const schlimm = [];
|
||||
|
||||
for (const [wohin, knopf] of FORMULARE) {
|
||||
await seite.goto(`${BASIS}/workspace/${wohin}`, { waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(900);
|
||||
const auf = await seite.$(knopf);
|
||||
if (!auf) continue;
|
||||
await auf.click().catch(() => {});
|
||||
await seite.waitForTimeout(700);
|
||||
|
||||
/* ALLES AUFKLAPPEN, was sich aufklappen laesst. */
|
||||
const wieViele = await seite.evaluate(() => {
|
||||
const f = document.querySelector(".neu__raster");
|
||||
if (!f) return 0;
|
||||
const knoepfe = [...f.querySelectorAll("[aria-expanded]")]
|
||||
.filter((b) => b.offsetParent !== null);
|
||||
for (const b of knoepfe) b.click();
|
||||
return knoepfe.length;
|
||||
});
|
||||
await seite.waitForTimeout(600);
|
||||
aufgeklapptGesamt += wieViele;
|
||||
|
||||
const befund = await seite.evaluate(() => {
|
||||
const r = document.querySelector(".neu__raster");
|
||||
if (!r) return { zellen: 0, zuEng: [] };
|
||||
const zellen = [...r.children].filter((c) => !c.hidden && c.offsetParent !== null);
|
||||
|
||||
/* WIE TIEF REICHT DER INHALT WIRKLICH -- zwei Anlaeufe davor
|
||||
waren falsch, und der zweite war der gefaehrlichere:
|
||||
|
||||
(1) `scrollHeight` der Zelle. Meldete elf Zellen als kaputt,
|
||||
die alle in Ordnung sind: Der Hinweis unter einem Feld
|
||||
haengt seit dem 17.09. ABSICHTLICH absolut unter der Zelle
|
||||
(`top: 100%`). Er ragt hinaus, das ist seine Aufgabe. Eine
|
||||
Warnung, die bei richtigem Verhalten anschlaegt, wird
|
||||
abgeschaltet.
|
||||
|
||||
(2) Nur die DIREKTEN Kinder im Fluss. Das war gruen -- auch
|
||||
mit dem echten Fehler. Nachgemessen mit
|
||||
zurueckgenommener Behebung: immer noch gruen. Der Grund
|
||||
ist das Raster selbst: Es staucht das Kind auf die feste
|
||||
Zeilenhoehe, das Kind bleibt also brav in der Zelle. Was
|
||||
herauslaeuft, ist der Inhalt DARIN. Eine Pruefung, die
|
||||
nur eine Ebene tief sieht, sieht davon nichts.
|
||||
|
||||
Also in die Tiefe -- aber nicht blind: Teilbaeume unter
|
||||
absolut gesetzten Elementen bleiben aussen vor (siehe 1), und
|
||||
wer selbst scrollt (`overflow` nicht `visible`), darf Inhalt
|
||||
unterhalb haben; das ist der Sinn eines Rollbereichs. */
|
||||
const unten = (c) => {
|
||||
const oben = c.getBoundingClientRect().top;
|
||||
let tiefste = 0;
|
||||
const geh = (el) => {
|
||||
for (const k of el.children) {
|
||||
const st = getComputedStyle(k);
|
||||
if (st.position === "absolute" || st.position === "fixed") continue;
|
||||
if (st.display === "none" || k.hidden) continue;
|
||||
tiefste = Math.max(tiefste, k.getBoundingClientRect().bottom - oben);
|
||||
if (st.overflow === "visible" && st.overflowY === "visible") geh(k);
|
||||
}
|
||||
};
|
||||
geh(c);
|
||||
return Math.round(tiefste);
|
||||
};
|
||||
const zuEng = zellen
|
||||
/* 4 px Luft fuer Abrundungen im Rastermass. 174 px faellt
|
||||
hier nicht durch. */
|
||||
.filter((c) => unten(c) > Math.round(c.getBoundingClientRect().height) + 4)
|
||||
.map((c) => `${c.id || "(ohne Kennung)"}: Inhalt reicht bis ${unten(c)} px, `
|
||||
+ `Zelle ist ${Math.round(c.getBoundingClientRect().height)} px hoch`);
|
||||
return { zellen: zellen.length, zuEng };
|
||||
});
|
||||
zellenGesamt += befund.zellen;
|
||||
for (const z of befund.zuEng) schlimm.push(`${wohin} — ${z}`);
|
||||
}
|
||||
|
||||
/* DIE ANZAHL GEHOERT IN DIE BEDINGUNG. Ohne sie waere „nichts laeuft
|
||||
heraus" auch dann gruen, wenn gar nichts aufgeklappt wurde -- und
|
||||
genau das war der Fehler der Pruefung darueber. */
|
||||
ok(zellenGesamt > 10, `${zellenGesamt} Rasterzellen angesehen`);
|
||||
ok(aufgeklapptGesamt > 0,
|
||||
`${aufgeklapptGesamt} aufklappbare Bereiche wirklich geoeffnet`);
|
||||
ok(schlimm.length === 0 && aufgeklapptGesamt > 0,
|
||||
schlimm.length
|
||||
? `${schlimm.length} Zelle(n) laufen aus ihrer Zeile heraus:\n `
|
||||
+ schlimm.join("\n ")
|
||||
: "keine Zelle laeuft aus ihrer Zeile heraus");
|
||||
|
||||
/* GEGENPROBE: Die Messung muss ein Herauslaufen auch SEHEN. Dafuer
|
||||
wird eine Zelle kuenstlich eingeklemmt -- und danach wieder
|
||||
freigegeben. Ohne diese Zeile waere „nichts laeuft heraus" die
|
||||
Aussage einer Messung, von der niemand weiss, ob sie etwas
|
||||
bemerken wuerde. */
|
||||
/* Fuer die Gegenprobe zurueck auf ein Formular, das offen ist -- der
|
||||
letzte Durchlauf oben kann auf einer Seite geendet haben, die gar
|
||||
keines hat. Eine Gegenprobe, die mangels Gelegenheit ausfaellt,
|
||||
ist keine. */
|
||||
await seite.goto(`${BASIS}/workspace/aufgaben.html`, { waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(900);
|
||||
await seite.click("#neu-oeffnen").catch(() => {});
|
||||
await seite.waitForTimeout(700);
|
||||
|
||||
const probe = await seite.evaluate(() => {
|
||||
/* Dieselbe Rechnung wie oben -- sonst prueft die Gegenprobe eine
|
||||
andere Messung als die, um die es geht. */
|
||||
const unten = (c) => {
|
||||
const oben = c.getBoundingClientRect().top;
|
||||
let tiefste = 0;
|
||||
const geh = (el) => {
|
||||
for (const k of el.children) {
|
||||
const st = getComputedStyle(k);
|
||||
if (st.position === "absolute" || st.position === "fixed") continue;
|
||||
if (st.display === "none" || k.hidden) continue;
|
||||
tiefste = Math.max(tiefste, k.getBoundingClientRect().bottom - oben);
|
||||
if (st.overflow === "visible" && st.overflowY === "visible") geh(k);
|
||||
}
|
||||
};
|
||||
geh(c);
|
||||
return Math.round(tiefste);
|
||||
};
|
||||
const r = document.querySelector(".neu__raster");
|
||||
const c = r && [...r.children].find((x) => !x.hidden && x.offsetParent !== null && unten(x) > 40);
|
||||
if (!c) return null;
|
||||
const vorher = c.style.height;
|
||||
c.style.height = "12px";
|
||||
const erkannt = unten(c) > Math.round(c.getBoundingClientRect().height) + 4;
|
||||
c.style.height = vorher;
|
||||
const wiederGut = unten(c) <= Math.round(c.getBoundingClientRect().height) + 4;
|
||||
return { erkannt, wiederGut };
|
||||
});
|
||||
ok(!!probe && probe.erkannt && probe.wiederGut,
|
||||
probe ? "Gegenprobe: eine kuenstlich eingeklemmte Zelle wird erkannt"
|
||||
: "KONNTE NICHT NACHSEHEN: keine Zelle fuer die Gegenprobe gefunden");
|
||||
|
||||
await kontext.close();
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
||||
console.log(fehler ? `\n${fehler} Formular(e) fehlerhaft.` : "\nAlles in Ordnung.");
|
||||
|
||||
Reference in New Issue
Block a user