feat(diagram): das Knoten-Fenster löst den nativen Tooltip überall ab
Das Fenster aus D52 war für Touch gebaut — damit standen zwei Darstellungen desselben Inhalts nebeneinander, und ausgerechnet die schlechtere bekam der Zeiger. Jetzt zeigt es überall: am Zeiger nach 350 ms beim Überfahren, bei Tastaturfokus sofort, auf Touch unverändert beim Tipp. Was ein `title` prinzipiell nicht kann: - keine Absätze (die Beschreibungen im Plan brechen bei ~76 Zeichen, das sah ausgefranst aus), - keine Linie (der Trennstrich war aus 24 `─` GEMALT, D40-Nachtrag), - nichts bei Tastaturfokus — wer mit Tab durch das Diagramm geht, sah bisher keine Beschreibung. Nachgemessen mit ECHTEM Tastendruck: Fokus von #not.line auf #not.status, Fenster folgt, 10 px unter dem Knoten, Ring am fokussierten Knoten. - Inhalt zieht von `title` nach `data-tip` (sonst zeigte der Browser seinen eigenen zusätzlich) — dasselbe Attribut unter anderem Namen, keine Verdopplung der 20 kB DOM-Text. - Die letzten drei nativen Tooltips IN Knoten mussten weichen: Das Warndreieck war redundant (Statusname steht in den Fakten), die Annahme „Größe fehlt, gilt als M" wandert in die Faktenzeile. Nachgemessen: 0 von 161 Knoten und 0 Kinder tragen noch einen `title`. - Absicht-Erkennung: 350 ms Verzögerung, beim Wechsel zwischen Knoten ohne Warten, 120 ms Nachfrist beim Verlassen (der Weg ins Fenster führt über einen Zwischenraum). - `aria-hidden` am Fenster: Der Inhalt steht schon im aria-label, sonst würde er doppelt vorgelesen; × und ↗ deshalb `tabindex="-1"`. Werkzeuggrenze notiert: `element.focus()` feuert ohne Fensterfokus keine Fokus-Ereignisse (`document.hasFocus()` false) — der erste Prüflauf zeigte fälschlich „öffnet nichts". SPEC §9 + D57; Plan: #ed.tipwindow [x] (161 Knoten, 0 Warnungen). 293 Tests grün, 5 Snapshots/Zusicherungen auf `data-tip` nachgezogen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f0fe877d45
commit
3fbc772d81
@@ -3974,3 +3974,74 @@ Rechnung wie D17-Nachtrag 5.** Nachgemessen statt geschätzt:
|
||||
|
||||
Ein `overflow` an der Kopfzeile bleibt weiterhin ausgeschlossen — es klippt die
|
||||
beiden Aufklapp-Menüs (D50).
|
||||
|
||||
## D57 — Das Knoten-Fenster löst den nativen Tooltip überall ab
|
||||
Das Fenster aus D52 war für Touch gebaut: Ohne Zeiger gibt es keinen `title`,
|
||||
und die Beschreibungen (D40) wären dort gar nicht zu sehen gewesen. Damit
|
||||
standen zwei Darstellungen desselben Inhalts nebeneinander — und ausgerechnet
|
||||
die schlechtere bekam der Zeiger. Jetzt zeigt das Fenster überall: am Zeiger
|
||||
nach kurzer Verzögerung beim Überfahren, bei **Tastaturfokus** sofort, auf
|
||||
Touch unverändert beim einfachen Tipp.
|
||||
|
||||
**Was der `title` nicht konnte, und zwar prinzipiell:**
|
||||
|
||||
- **Keine Absätze.** Die Beschreibungen im mitgelieferten Plan sind bei ~76
|
||||
Zeichen umgebrochen; ein `title` zeigt genau diese harten Umbrüche, in einem
|
||||
schmalen Fenster sah der Text ausgefranst aus. Das Fenster bricht um, wie es
|
||||
die Breite hergibt (D52 hatte das für Touch schon gelöst).
|
||||
- **Keine Linie.** Der Trennstrich zwischen Beschreibung und Kurz-Fakten musste
|
||||
aus 24 `─`-Zeichen **gemalt** werden (D40-Nachtrag) — im Fenster ist er eine
|
||||
echte Kante, und ohne Beschreibung entfällt er ganz.
|
||||
- **Nichts bei Tastaturfokus.** Kein Browser zeigt einen `title`, wenn man mit
|
||||
Tab auf ein Element springt. Wer das Diagramm mit der Tastatur durchgeht, sah
|
||||
bisher keine Beschreibung. Das ist der eigentliche Zugewinn und kein Beiwerk —
|
||||
nachgemessen mit einem **echten** Tastendruck: Fokus wandert von `#not.line`
|
||||
auf `#not.status`, das Fenster folgt mit dessen Text, 10 px unter dem Knoten,
|
||||
Petrol-Ring am fokussierten Knoten.
|
||||
|
||||
**Der Inhalt zieht von `title` nach `data-tip` um.** Bliebe er im `title`,
|
||||
zeigte der Browser seinen eigenen Tooltip **zusätzlich** — unterdrücken lässt er
|
||||
sich nicht. Es ist kein zweites Attribut, sondern dasselbe unter anderem Namen;
|
||||
die Sorge aus D52 („keine 20 kB DOM-Text verdoppeln") bleibt gewahrt.
|
||||
|
||||
**Dabei mussten die letzten drei nativen Tooltips *innerhalb* der Knoten
|
||||
weichen** — sonst wären sie neben dem Fenster ein zweites Mal erschienen:
|
||||
|
||||
- Das **Warndreieck** (`[!]`) trug „High Risk – Aufwand unklar". Das ist der
|
||||
Statusname und steht ohnehin in der Faktenzeile — der `title` war redundant
|
||||
und ist ersatzlos weg.
|
||||
- Das **implizite M-Badge** trug die Kostenannahme „Größe fehlt, gilt als M"
|
||||
(D18). Die ist nicht redundant, also wandert sie in die Faktenzeile und der
|
||||
`title` entfällt. Nachgemessen: `data-tip="st_geplant · implicitSizeTooltip ·
|
||||
jumpHint"`, Badge ohne Attribut.
|
||||
|
||||
Damit trägt im ganzen Diagramm kein Element mehr einen nativen Tooltip
|
||||
(nachgemessen: 0 von 160 Knoten, 0 Kinder) — außer dem Geister-Knoten, der kein
|
||||
`.node` ist.
|
||||
|
||||
**Absicht-Erkennung statt sofortigem Aufpoppen.** Über einen dichten Baum fährt
|
||||
man hinweg, ohne etwas wissen zu wollen: 350 ms Verzögerung. Steht schon ein
|
||||
Fenster offen, zeigt der nächste Knoten **ohne** Warten — wer liest, wartet
|
||||
nicht noch einmal. Beim Verlassen wird 120 ms gewartet, bevor zugemacht wird:
|
||||
Der Weg vom Knoten ins Fenster führt über einen Zwischenraum, in dem der Zeiger
|
||||
über keinem von beiden steht. Wer im Fenster ist, hält es offen (Text lässt
|
||||
sich markieren).
|
||||
|
||||
**Der ↗-Knopf bleibt Touch vorbehalten.** Am Zeiger ist der ganze Knoten der
|
||||
Link (§6) — ein Knopf im Fenster wäre ein zweiter Weg zum selben Ziel. Ebenso
|
||||
nennt die Faktenzeile nur auf Touch den langen Druck statt Alt+Klick.
|
||||
|
||||
**Für Screenreader ändert sich nichts, und das ist Absicht.** Das Fenster ist
|
||||
`aria-hidden`: Sein ganzer Inhalt steht bereits im `aria-label` des Knotens
|
||||
(SPEC §9), er würde sonst doppelt vorgelesen. Damit dürfen darin keine
|
||||
fokussierbaren Elemente liegen — × und ↗ tragen deshalb `tabindex="-1"`;
|
||||
erreichbar bleibt beides über den Knoten selbst und über Esc.
|
||||
|
||||
**Werkzeuggrenze, wieder dieselbe Sorte:** `element.focus()` setzt in einem
|
||||
nicht fokussierten Automatisierungsfenster zwar `document.activeElement`, feuert
|
||||
aber **keine** Fokus-Ereignisse (`document.hasFocus() === false`). Der erste
|
||||
Prüflauf zeigte deshalb „Tastaturfokus öffnet nichts", obwohl die Logik stimmte;
|
||||
mit synthetischem `focusin` öffnete es sofort. Erst das Fronten des Tabs samt
|
||||
echtem Klick und echter Tab-Taste hat es bewiesen. Wie D25 (synthetische
|
||||
`TouchEvent`s), D17-Nachtrag 4 (Bildschirmtastatur) und D53 (synthetisches
|
||||
Strg+Z): Was die Umgebung stellt, stellt der Emulator nicht.
|
||||
|
||||
+6
-5
@@ -335,11 +335,12 @@ spiegelt das `"`-Zeichen der Notation. Die Marke erscheint **nicht** im
|
||||
Grafikexport: Der Text selbst kann dort nicht angezeigt werden, eine Marke
|
||||
ohne Ziel wäre Rauschen. Siehe D40.
|
||||
|
||||
**Auf Touch-Geräten zeigt der einfache Tipp auf einen Knoten dasselbe als
|
||||
Fenster** — ohne Zeiger gibt es keinen Tooltip, die Beschreibung wäre dort sonst
|
||||
gar nicht zu sehen. Es ist derselbe Inhalt, nur mit dem, was ein `title` nicht
|
||||
kann: Die Beschreibung steht als Absatz, die Kurz-Fakten dahinter durch eine
|
||||
echte Linie abgesetzt.
|
||||
**Gezeigt wird das immer im eigenen Knoten-Fenster**, nicht im Tooltip des
|
||||
Browsers: am Zeiger nach kurzer Verzögerung beim Überfahren, bei Tastaturfokus
|
||||
sofort, auf Touch beim einfachen Tipp (§6). Ein `title` kann weder Absätze noch
|
||||
eine Linie und erscheint nie beim Tastaturfokus; das Fenster kann beides. Der
|
||||
Inhalt ist derselbe — die Beschreibung als Absatz, die Kurz-Fakten dahinter
|
||||
durch eine echte Linie abgesetzt. Siehe D57.
|
||||
|
||||
- Das Fenster hängt am angetippten Knoten (kleine Spitze zu ihm hin) und weicht
|
||||
nach oben aus, wenn unten kein Platz ist. Der Knoten trägt, solange es offen
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
- [^] #ed.a11y: Accessibility: aria labels, focus order (S)
|
||||
- [^] #ed.mobile: Works on a phone (S)
|
||||
- [^] #ed.mobile.tip: A tap on a node opens its tooltip as a window (XS) %% no pointer, no tooltip
|
||||
- [x] #ed.tipwindow: The same window at the pointer and on keyboard focus (XS) %% a title has no paragraphs and never shows on focus
|
||||
- [^] #ed.docs: Several documents in one editor (M)
|
||||
- [^] #ed.docs.picker: Switcher in the editor title bar (S)
|
||||
- [^] #ed.docs.url: Load a document from ?sourceUrl= (S)
|
||||
@@ -462,6 +463,11 @@
|
||||
at all. A tap opens the same content as a window instead; on a linked node
|
||||
the link becomes a button inside it, because one tap cannot do both.
|
||||
|
||||
#ed.tipwindow
|
||||
A native title has no paragraphs, cannot draw a line, and never appears on
|
||||
keyboard focus. The window built for touch can do all three, so it now shows
|
||||
everywhere and the title is gone.
|
||||
|
||||
#ed.docs
|
||||
Several notation texts side by side, kept in the browser. Each is only text
|
||||
plus a name — no invented structure format, so this maps onto a server later
|
||||
|
||||
+7
-2
@@ -256,8 +256,13 @@
|
||||
Es liegt auf <body> und ist `position:fixed` — in `#out` hinein würde es
|
||||
dessen CSS-`zoom` erben (der Text skalierte mit) und von dessen `overflow`
|
||||
beschnitten (die Falle aus D50). Inhalt wird bei jedem Öffnen ersetzt. -->
|
||||
<div id="nodeTip" class="nodetip" role="dialog" aria-modal="false" hidden>
|
||||
<button type="button" class="nodetip-x" id="nodeTipClose" data-i18n-title="tipClose" data-i18n-aria="tipClose" title="Schließen" aria-label="Schließen">×</button>
|
||||
<!-- `aria-hidden`: Das Fenster ist eine rein visuelle Hilfe — sein ganzer Inhalt
|
||||
steht bereits im `aria-label` des Knotens (SPEC §9), ein Screenreader läse
|
||||
ihn sonst doppelt. Die Knöpfe darin tragen deshalb `tabindex="-1"`
|
||||
(fokussierbare Elemente in einem `aria-hidden`-Bereich wären ein Fehler);
|
||||
erreichbar bleibt beides über den Knoten selbst und über Esc. Siehe D57. -->
|
||||
<div id="nodeTip" class="nodetip" aria-hidden="true" hidden>
|
||||
<button type="button" class="nodetip-x" id="nodeTipClose" tabindex="-1" data-i18n-title="tipClose" title="Schließen">×</button>
|
||||
<div class="nodetip-body" id="nodeTipBody"></div>
|
||||
<span class="nodetip-arrow" aria-hidden="true"></span>
|
||||
</div>
|
||||
|
||||
+59
-6
@@ -1246,15 +1246,21 @@ function closeNodeTip(){
|
||||
|
||||
function toggleNodeTip(el){
|
||||
if(tipNode === el){ closeNodeTip(); return; } /* zweiter Tipp schließt */
|
||||
showNodeTip(el, true);
|
||||
}
|
||||
|
||||
/* `touch` unterscheidet die beiden Anlässe: Am Zeiger ist der ganze Knoten der
|
||||
Link (§6), ein ↗-Knopf wäre dort ein zweiter Weg zum selben Ziel; und der
|
||||
Sprung-Hinweis nennt Alt+Klick, den es auf dem Telefon nicht gibt. */
|
||||
function showNodeTip(el, touch){
|
||||
closeNodeTip();
|
||||
const title = el.getAttribute('title') || '';
|
||||
const title = el.getAttribute('data-tip') || '';
|
||||
const sep = '\n\n' + TIP_RULE + '\n';
|
||||
const i = title.indexOf(sep);
|
||||
const desc = i >= 0 ? title.slice(0, i) : '';
|
||||
/* Der Sprung-Hinweis nennt im Tooltip Alt+Klick; hier gibt es kein Alt. */
|
||||
const facts = (i >= 0 ? title.slice(i + sep.length) : title)
|
||||
.replace(t('jumpHint'), t('jumpHintTouch'));
|
||||
const href = el.tagName === 'A' ? el.getAttribute('href') : null;
|
||||
const roh = i >= 0 ? title.slice(i + sep.length) : title;
|
||||
const facts = touch ? roh.replace(t('jumpHint'), t('jumpHintTouch')) : roh;
|
||||
const href = touch && el.tagName === 'A' ? el.getAttribute('href') : null;
|
||||
/* Absätze: Leerzeilen trennen (SPEC §1), einzelne Zeilenumbrüche sind bloß
|
||||
der Umbruch der Quelldatei und werden zu Leerzeichen. Der `title` kann das
|
||||
nicht — er zeigt die harten Umbrüche und sah im schmalen Fenster
|
||||
@@ -1265,7 +1271,7 @@ function toggleNodeTip(el){
|
||||
nodeTipBody.innerHTML =
|
||||
(paras.length ? `<div class="nodetip-desc">${paras.map(p => `<p>${esc(p)}</p>`).join('')}</div>` : '') +
|
||||
(facts ? `<div class="nodetip-facts">${esc(facts)}</div>` : '') +
|
||||
(href ? `<a class="nodetip-link" href="${esc(href)}" target="_blank" rel="noopener">↗ ${esc(t('tipOpenLink'))}</a>` : '');
|
||||
(href ? `<a class="nodetip-link" tabindex="-1" href="${esc(href)}" target="_blank" rel="noopener">↗ ${esc(t('tipOpenLink'))}</a>` : '');
|
||||
tipNode = el;
|
||||
el.classList.add('tipped');
|
||||
nodeTip.hidden = false;
|
||||
@@ -1293,6 +1299,53 @@ function placeNodeTip(el){
|
||||
Math.max(12, Math.min(anchorX - left, w - 12)).toFixed(1) + 'px');
|
||||
}
|
||||
|
||||
/* ---------- Dasselbe Fenster am Zeiger und an der Tastatur (D57) ----------
|
||||
Es löst den nativen `title` ab: Der konnte weder Absätze noch eine Linie
|
||||
(der Trennstrich war aus 24 `─` gemalt, D40-Nachtrag) und erschien nie beim
|
||||
Tastaturfokus. Beides kann das Fenster ohnehin schon — es wurde nur für
|
||||
Touch gebaut (D52).
|
||||
|
||||
Die Verzögerung ist Absicht-Erkennung: Über einen dichten Baum fährt man
|
||||
hinweg, ohne etwas wissen zu wollen. Beim Wechsel von Knoten zu Knoten
|
||||
zeigt es sofort weiter — wer schon liest, wartet nicht noch einmal. */
|
||||
const TIP_DELAY = 350;
|
||||
let tipTimer = null, tipLeave = null;
|
||||
const finePointer = () => window.matchMedia('(hover:hover) and (pointer:fine)').matches;
|
||||
function cancelTipTimers(){
|
||||
clearTimeout(tipTimer); clearTimeout(tipLeave);
|
||||
tipTimer = tipLeave = null;
|
||||
}
|
||||
out.addEventListener('pointerover', e => {
|
||||
if(e.pointerType === 'touch' || !finePointer()) return;
|
||||
const el = e.target.closest && e.target.closest('.node');
|
||||
if(!el || el === tipNode) return;
|
||||
cancelTipTimers();
|
||||
/* Steht schon eines offen, ist die Absicht erwiesen — dann ohne Warten. */
|
||||
tipTimer = setTimeout(() => showNodeTip(el, false), tipNode ? 0 : TIP_DELAY);
|
||||
});
|
||||
out.addEventListener('pointerout', e => {
|
||||
if(e.pointerType === 'touch' || !finePointer()) return;
|
||||
clearTimeout(tipTimer);
|
||||
/* Nicht sofort zumachen: Der Weg ins Fenster führt über den Zwischenraum,
|
||||
und dort ist der Zeiger kurz über keinem von beiden. */
|
||||
clearTimeout(tipLeave);
|
||||
tipLeave = setTimeout(() => {
|
||||
if(!nodeTip.matches(':hover')) closeNodeTip();
|
||||
}, 120);
|
||||
});
|
||||
nodeTip.addEventListener('pointerenter', cancelTipTimers);
|
||||
nodeTip.addEventListener('pointerleave', () => { closeNodeTip(); });
|
||||
/* Tastaturfokus: ohne Verzögerung — er ist bereits die ausdrückliche Absicht,
|
||||
und ein `title` hat hier noch nie etwas gezeigt. */
|
||||
out.addEventListener('focusin', e => {
|
||||
const el = e.target.closest && e.target.closest('.node');
|
||||
if(el && el !== tipNode) showNodeTip(el, false);
|
||||
});
|
||||
out.addEventListener('focusout', e => {
|
||||
const el = e.target.closest && e.target.closest('.node');
|
||||
if(el && el === tipNode) closeNodeTip();
|
||||
});
|
||||
|
||||
document.getElementById('nodeTipClose').addEventListener('click', closeNodeTip);
|
||||
/* Tipp/Klick daneben schließt — der Link-Knopf im Fenster aber nicht. */
|
||||
document.addEventListener('pointerdown', e => {
|
||||
|
||||
+16
-5
@@ -131,25 +131,36 @@ function nodeHtml(n, extra, opts, fold){
|
||||
effKey
|
||||
? t('heldTooltip', {eff: t('st_' + effKey), own: t('st_' + n.status.key)})
|
||||
: (n.status ? t('st_' + n.status.key) : ''),
|
||||
n.optional ? t('a11yOptional') : '', t('jumpHint')]
|
||||
n.optional ? t('a11yOptional') : '',
|
||||
/* Die Kostenannahme „Größe fehlt, gilt als M" (D18) hatte einen
|
||||
eigenen `title` am Badge — der erschiene neben dem Fenster
|
||||
ein zweites Mal. Sie gehört ohnehin zu den Kurz-Fakten. */
|
||||
(!n.size && cheapPath && !isDone(n)) ? t('implicitSizeTooltip') : '',
|
||||
t('jumpHint')]
|
||||
.filter(Boolean).join(' · ');
|
||||
const tip = n.desc && facts ? n.desc + '\n\n' + TIP_RULE + '\n' + facts
|
||||
: (n.desc || facts);
|
||||
const title = ` title="${attr(tip)}"`;
|
||||
/* `data-tip` statt `title`: Der Inhalt wird vom eigenen Knoten-Fenster
|
||||
dargestellt (D57), und ein `title` daneben zeigte der Browser zusätzlich
|
||||
als zweiten, dürftigeren Tooltip. Kein zweites Attribut mit demselben Text
|
||||
— es ist dasselbe, nur unter anderem Namen. */
|
||||
const title = ` data-tip="${attr(tip)}"`;
|
||||
const tagsHtml = n.tags && n.tags.length
|
||||
? `<span class="tags" aria-hidden="true">${n.tags.map(tag => `<span class="tag">${esc(tag)}</span>`).join('')}</span>`
|
||||
: '';
|
||||
/* Das implizite M-Badge macht eine KOSTENANNAHME sichtbar (D18). An einem
|
||||
erledigten Knoten wird keine getroffen — er kostet nichts mehr (D46) —,
|
||||
dort bleibt es deshalb weg. */
|
||||
const implicitTip = attr(t('implicitSizeTooltip'));
|
||||
const implicitSize = !n.size && cheapPath && !isDone(n);
|
||||
const sizeBadge = n.size
|
||||
? `<span class="size" aria-hidden="true">${n.size}</span>`
|
||||
: (cheapPath && !isDone(n) ? `<span class="size implicit" aria-hidden="true" title="${implicitTip}">M</span>` : '');
|
||||
: (implicitSize ? `<span class="size implicit" aria-hidden="true">M</span>` : '');
|
||||
/* High-Risk: Warndreieck (⚠, Textpräsentation via VS15) an der oberen linken
|
||||
Ecke. aria-hidden — die Information steckt bereits im Status des aria-label. */
|
||||
/* Kein eigener `title` mehr: Er zeigte sonst zusätzlich zum Knoten-Fenster
|
||||
(D57), und seine Aussage — „High Risk" — steht dort ohnehin als Status. */
|
||||
const riskMark = n.status && n.status.key === 'highrisk'
|
||||
? `<span class="risk" aria-hidden="true" title="${attr(t('riskTooltip'))}">⚠︎</span>`
|
||||
? `<span class="risk" aria-hidden="true">⚠︎</span>`
|
||||
: '';
|
||||
/* Falt-Zeichen (SPEC §9/D38): ▾ offen, „▸ n" eingeklappt — das Klickziel
|
||||
fürs Umklappen (der einfache Klick auf den Knoten bleibt der Link, §6).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`renderTreeHtml — kanonisches Beispiel > Grundzustand (Pfad aus, verworfene aus): Struktur-Snapshot 1`] = `"<li class="has-and"><a class="node root-node st-arbeit" href="https://wiki.example.de/relaunch" target="_blank" rel="noopener" data-line="2" aria-label="Website-Relaunch, a11yStatus, a11ySize, a11yLink" aria-expanded="true" title="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Website-Relaunch<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">XL</span></a><ul class="and"><li class="has-and"><div class="node done st-fertig" tabindex="0" data-line="3" aria-label="Konzeption, a11yStatus, a11ySize" aria-expanded="true" title="st_fertig · jumpHint"><span class="fold" aria-hidden="true">▾</span>Konzeption<span class="size" aria-hidden="true">M</span></div><ul class="and"><li><div class="node done st-fertig" tabindex="0" data-line="4" aria-label="Zielgruppenanalyse, a11yStatus, a11ySize" title="st_fertig · jumpHint">Zielgruppenanalyse<span class="size" aria-hidden="true">S</span></div></li><li><div class="node done st-fertig" tabindex="0" data-line="5" aria-label="Sitemap, a11yStatus, a11ySize" title="st_fertig · jumpHint">Sitemap<span class="size" aria-hidden="true">XS</span></div></li></ul></li><li class="has-and"><div class="node st-arbeit" tabindex="0" data-line="6" aria-label="Umsetzung, a11yStatus, a11ySize" aria-expanded="true" title="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Umsetzung<span class="size" aria-hidden="true">XL</span></div><ul class="and"><li><a class="node st-durchstich" href="https://git.example.de/frontend" target="_blank" rel="noopener" data-line="7" aria-label="Frontend, a11yStatus, a11ySize, a11yTags, a11yLink" title="st_durchstich · jumpHint">Frontend<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">S</span><span class="tags" aria-hidden="true"><span class="tag">anna</span></span></a></li><li><div class="node st-geplant" tabindex="0" data-line="8" aria-label="Backend, a11yStatus, a11ySize, a11yTags" title="st_geplant · jumpHint">Backend<span class="size" aria-hidden="true">L</span><span class="tags" aria-hidden="true"><span class="tag">ben</span><span class="tag">carla</span></span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li><li class="opt"><div class="node opt st-idee" tabindex="0" data-line="9" aria-label="Dark Mode, a11yStatus, a11ySize, a11yOptional" title="st_idee · a11yOptional · jumpHint">Dark Mode<span class="size" aria-hidden="true">S</span></div></li><li class="has-or"><div class="node st-geplant" tabindex="0" data-line="10" aria-label="CMS-Anbindung, a11yStatus, a11ySize" aria-expanded="true" title="st_geplant · jumpHint"><span class="fold" aria-hidden="true">▾</span>CMS-Anbindung<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node st-geplant" tabindex="0" data-line="11" aria-label="WordPress, a11yStatus" title="st_geplant · jumpHint">WordPress</div></li><li><div class="node st-idee" tabindex="0" data-line="12" aria-label="Headless CMS, a11yStatus" title="st_idee · jumpHint">Headless CMS</div></li></ul></li></ul></li><li class="has-or"><div class="node st-idee" tabindex="0" data-line="14" aria-label="Hosting, a11yStatus, a11ySize" aria-expanded="true" title="st_idee · jumpHint"><span class="fold" aria-hidden="true">▾</span>Hosting<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node" tabindex="0" data-line="15" aria-label="Cloud" title="jumpHint">Cloud</div></li><li><div class="node" tabindex="0" data-line="16" aria-label="On-Premise" title="jumpHint">On-Premise</div></li></ul></li></ul></li>"`;
|
||||
exports[`renderTreeHtml — kanonisches Beispiel > Grundzustand (Pfad aus, verworfene aus): Struktur-Snapshot 1`] = `"<li class="has-and"><a class="node root-node st-arbeit" href="https://wiki.example.de/relaunch" target="_blank" rel="noopener" data-line="2" aria-label="Website-Relaunch, a11yStatus, a11ySize, a11yLink" aria-expanded="true" data-tip="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Website-Relaunch<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">XL</span></a><ul class="and"><li class="has-and"><div class="node done st-fertig" tabindex="0" data-line="3" aria-label="Konzeption, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_fertig · jumpHint"><span class="fold" aria-hidden="true">▾</span>Konzeption<span class="size" aria-hidden="true">M</span></div><ul class="and"><li><div class="node done st-fertig" tabindex="0" data-line="4" aria-label="Zielgruppenanalyse, a11yStatus, a11ySize" data-tip="st_fertig · jumpHint">Zielgruppenanalyse<span class="size" aria-hidden="true">S</span></div></li><li><div class="node done st-fertig" tabindex="0" data-line="5" aria-label="Sitemap, a11yStatus, a11ySize" data-tip="st_fertig · jumpHint">Sitemap<span class="size" aria-hidden="true">XS</span></div></li></ul></li><li class="has-and"><div class="node st-arbeit" tabindex="0" data-line="6" aria-label="Umsetzung, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Umsetzung<span class="size" aria-hidden="true">XL</span></div><ul class="and"><li><a class="node st-durchstich" href="https://git.example.de/frontend" target="_blank" rel="noopener" data-line="7" aria-label="Frontend, a11yStatus, a11ySize, a11yTags, a11yLink" data-tip="st_durchstich · jumpHint">Frontend<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">S</span><span class="tags" aria-hidden="true"><span class="tag">anna</span></span></a></li><li><div class="node st-geplant" tabindex="0" data-line="8" aria-label="Backend, a11yStatus, a11ySize, a11yTags" data-tip="st_geplant · jumpHint">Backend<span class="size" aria-hidden="true">L</span><span class="tags" aria-hidden="true"><span class="tag">ben</span><span class="tag">carla</span></span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li><li class="opt"><div class="node opt st-idee" tabindex="0" data-line="9" aria-label="Dark Mode, a11yStatus, a11ySize, a11yOptional" data-tip="st_idee · a11yOptional · jumpHint">Dark Mode<span class="size" aria-hidden="true">S</span></div></li><li class="has-or"><div class="node st-geplant" tabindex="0" data-line="10" aria-label="CMS-Anbindung, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_geplant · jumpHint"><span class="fold" aria-hidden="true">▾</span>CMS-Anbindung<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node st-geplant" tabindex="0" data-line="11" aria-label="WordPress, a11yStatus" data-tip="st_geplant · jumpHint">WordPress</div></li><li><div class="node st-idee" tabindex="0" data-line="12" aria-label="Headless CMS, a11yStatus" data-tip="st_idee · jumpHint">Headless CMS</div></li></ul></li></ul></li><li class="has-or"><div class="node st-idee" tabindex="0" data-line="14" aria-label="Hosting, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_idee · jumpHint"><span class="fold" aria-hidden="true">▾</span>Hosting<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node" tabindex="0" data-line="15" aria-label="Cloud" data-tip="jumpHint">Cloud</div></li><li><div class="node" tabindex="0" data-line="16" aria-label="On-Premise" data-tip="jumpHint">On-Premise</div></li></ul></li></ul></li>"`;
|
||||
|
||||
exports[`renderTreeHtml — kanonisches Beispiel > günstigster Pfad an: cheap/cheap-leaf + implizite M-Badges 1`] = `"<li class="has-and"><a class="node root-node cheap st-arbeit" href="https://wiki.example.de/relaunch" target="_blank" rel="noopener" data-line="2" aria-label="Website-Relaunch, a11yStatus, a11ySize, a11yLink" aria-expanded="true" title="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Website-Relaunch<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">XL</span></a><ul class="and"><li class="has-and"><div class="node cheap done st-fertig" tabindex="0" data-line="3" aria-label="Konzeption, a11yStatus, a11ySize" aria-expanded="true" title="st_fertig · jumpHint"><span class="fold" aria-hidden="true">▾</span>Konzeption<span class="size" aria-hidden="true">M</span></div><ul class="and"><li><div class="node cheap done st-fertig" tabindex="0" data-line="4" aria-label="Zielgruppenanalyse, a11yStatus, a11ySize" title="st_fertig · jumpHint">Zielgruppenanalyse<span class="size" aria-hidden="true">S</span></div></li><li><div class="node cheap done st-fertig" tabindex="0" data-line="5" aria-label="Sitemap, a11yStatus, a11ySize" title="st_fertig · jumpHint">Sitemap<span class="size" aria-hidden="true">XS</span></div></li></ul></li><li class="has-and"><div class="node cheap st-arbeit" tabindex="0" data-line="6" aria-label="Umsetzung, a11yStatus, a11ySize" aria-expanded="true" title="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Umsetzung<span class="size" aria-hidden="true">XL</span></div><ul class="and"><li><a class="node cheap cheap-leaf st-durchstich" href="https://git.example.de/frontend" target="_blank" rel="noopener" data-line="7" aria-label="Frontend, a11yStatus, a11ySize, a11yTags, a11yLink" title="st_durchstich · jumpHint">Frontend<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">S</span><span class="tags" aria-hidden="true"><span class="tag">anna</span></span></a></li><li><div class="node cheap cheap-leaf st-geplant" tabindex="0" data-line="8" aria-label="Backend, a11yStatus, a11ySize, a11yTags" title="st_geplant · jumpHint">Backend<span class="size" aria-hidden="true">L</span><span class="tags" aria-hidden="true"><span class="tag">ben</span><span class="tag">carla</span></span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li><li class="opt"><div class="node opt st-idee" tabindex="0" data-line="9" aria-label="Dark Mode, a11yStatus, a11ySize, a11yOptional" title="st_idee · a11yOptional · jumpHint">Dark Mode<span class="size" aria-hidden="true">S</span></div></li><li class="has-or"><div class="node cheap st-geplant" tabindex="0" data-line="10" aria-label="CMS-Anbindung, a11yStatus, a11ySize" aria-expanded="true" title="st_geplant · jumpHint"><span class="fold" aria-hidden="true">▾</span>CMS-Anbindung<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node cheap cheap-leaf st-geplant" tabindex="0" data-line="11" aria-label="WordPress, a11yStatus, a11ySizeImplicit" title="st_geplant · jumpHint">WordPress<span class="size implicit" aria-hidden="true" title="implicitSizeTooltip">M</span></div></li><li><div class="node st-idee" tabindex="0" data-line="12" aria-label="Headless CMS, a11yStatus, a11ySizeImplicit" title="st_idee · jumpHint">Headless CMS<span class="size implicit" aria-hidden="true" title="implicitSizeTooltip">M</span></div></li></ul></li></ul></li><li class="has-or"><div class="node cheap st-idee" tabindex="0" data-line="14" aria-label="Hosting, a11yStatus, a11ySize" aria-expanded="true" title="st_idee · jumpHint"><span class="fold" aria-hidden="true">▾</span>Hosting<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node cheap cheap-leaf" tabindex="0" data-line="15" aria-label="Cloud, a11ySizeImplicit" title="jumpHint">Cloud<span class="size implicit" aria-hidden="true" title="implicitSizeTooltip">M</span></div></li><li><div class="node" tabindex="0" data-line="16" aria-label="On-Premise, a11ySizeImplicit" title="jumpHint">On-Premise<span class="size implicit" aria-hidden="true" title="implicitSizeTooltip">M</span></div></li></ul></li></ul></li>"`;
|
||||
exports[`renderTreeHtml — kanonisches Beispiel > günstigster Pfad an: cheap/cheap-leaf + implizite M-Badges 1`] = `"<li class="has-and"><a class="node root-node cheap st-arbeit" href="https://wiki.example.de/relaunch" target="_blank" rel="noopener" data-line="2" aria-label="Website-Relaunch, a11yStatus, a11ySize, a11yLink" aria-expanded="true" data-tip="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Website-Relaunch<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">XL</span></a><ul class="and"><li class="has-and"><div class="node cheap done st-fertig" tabindex="0" data-line="3" aria-label="Konzeption, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_fertig · jumpHint"><span class="fold" aria-hidden="true">▾</span>Konzeption<span class="size" aria-hidden="true">M</span></div><ul class="and"><li><div class="node cheap done st-fertig" tabindex="0" data-line="4" aria-label="Zielgruppenanalyse, a11yStatus, a11ySize" data-tip="st_fertig · jumpHint">Zielgruppenanalyse<span class="size" aria-hidden="true">S</span></div></li><li><div class="node cheap done st-fertig" tabindex="0" data-line="5" aria-label="Sitemap, a11yStatus, a11ySize" data-tip="st_fertig · jumpHint">Sitemap<span class="size" aria-hidden="true">XS</span></div></li></ul></li><li class="has-and"><div class="node cheap st-arbeit" tabindex="0" data-line="6" aria-label="Umsetzung, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Umsetzung<span class="size" aria-hidden="true">XL</span></div><ul class="and"><li><a class="node cheap cheap-leaf st-durchstich" href="https://git.example.de/frontend" target="_blank" rel="noopener" data-line="7" aria-label="Frontend, a11yStatus, a11ySize, a11yTags, a11yLink" data-tip="st_durchstich · jumpHint">Frontend<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">S</span><span class="tags" aria-hidden="true"><span class="tag">anna</span></span></a></li><li><div class="node cheap cheap-leaf st-geplant" tabindex="0" data-line="8" aria-label="Backend, a11yStatus, a11ySize, a11yTags" data-tip="st_geplant · jumpHint">Backend<span class="size" aria-hidden="true">L</span><span class="tags" aria-hidden="true"><span class="tag">ben</span><span class="tag">carla</span></span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li><li class="opt"><div class="node opt st-idee" tabindex="0" data-line="9" aria-label="Dark Mode, a11yStatus, a11ySize, a11yOptional" data-tip="st_idee · a11yOptional · jumpHint">Dark Mode<span class="size" aria-hidden="true">S</span></div></li><li class="has-or"><div class="node cheap st-geplant" tabindex="0" data-line="10" aria-label="CMS-Anbindung, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_geplant · jumpHint"><span class="fold" aria-hidden="true">▾</span>CMS-Anbindung<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node cheap cheap-leaf st-geplant" tabindex="0" data-line="11" aria-label="WordPress, a11yStatus, a11ySizeImplicit" data-tip="st_geplant · implicitSizeTooltip · jumpHint">WordPress<span class="size implicit" aria-hidden="true">M</span></div></li><li><div class="node st-idee" tabindex="0" data-line="12" aria-label="Headless CMS, a11yStatus, a11ySizeImplicit" data-tip="st_idee · implicitSizeTooltip · jumpHint">Headless CMS<span class="size implicit" aria-hidden="true">M</span></div></li></ul></li></ul></li><li class="has-or"><div class="node cheap st-idee" tabindex="0" data-line="14" aria-label="Hosting, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_idee · jumpHint"><span class="fold" aria-hidden="true">▾</span>Hosting<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node cheap cheap-leaf" tabindex="0" data-line="15" aria-label="Cloud, a11ySizeImplicit" data-tip="implicitSizeTooltip · jumpHint">Cloud<span class="size implicit" aria-hidden="true">M</span></div></li><li><div class="node" tabindex="0" data-line="16" aria-label="On-Premise, a11ySizeImplicit" data-tip="implicitSizeTooltip · jumpHint">On-Premise<span class="size implicit" aria-hidden="true">M</span></div></li></ul></li></ul></li>"`;
|
||||
|
||||
exports[`renderTreeHtml — kanonisches Beispiel > verworfene einblenden: Eigenentwicklung erscheint (durchgestrichen) 1`] = `"<li class="has-and"><a class="node root-node st-arbeit" href="https://wiki.example.de/relaunch" target="_blank" rel="noopener" data-line="2" aria-label="Website-Relaunch, a11yStatus, a11ySize, a11yLink" aria-expanded="true" title="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Website-Relaunch<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">XL</span></a><ul class="and"><li class="has-and"><div class="node done st-fertig" tabindex="0" data-line="3" aria-label="Konzeption, a11yStatus, a11ySize" aria-expanded="true" title="st_fertig · jumpHint"><span class="fold" aria-hidden="true">▾</span>Konzeption<span class="size" aria-hidden="true">M</span></div><ul class="and"><li><div class="node done st-fertig" tabindex="0" data-line="4" aria-label="Zielgruppenanalyse, a11yStatus, a11ySize" title="st_fertig · jumpHint">Zielgruppenanalyse<span class="size" aria-hidden="true">S</span></div></li><li><div class="node done st-fertig" tabindex="0" data-line="5" aria-label="Sitemap, a11yStatus, a11ySize" title="st_fertig · jumpHint">Sitemap<span class="size" aria-hidden="true">XS</span></div></li></ul></li><li class="has-and"><div class="node st-arbeit" tabindex="0" data-line="6" aria-label="Umsetzung, a11yStatus, a11ySize" aria-expanded="true" title="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Umsetzung<span class="size" aria-hidden="true">XL</span></div><ul class="and"><li><a class="node st-durchstich" href="https://git.example.de/frontend" target="_blank" rel="noopener" data-line="7" aria-label="Frontend, a11yStatus, a11ySize, a11yTags, a11yLink" title="st_durchstich · jumpHint">Frontend<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">S</span><span class="tags" aria-hidden="true"><span class="tag">anna</span></span></a></li><li><div class="node st-geplant" tabindex="0" data-line="8" aria-label="Backend, a11yStatus, a11ySize, a11yTags" title="st_geplant · jumpHint">Backend<span class="size" aria-hidden="true">L</span><span class="tags" aria-hidden="true"><span class="tag">ben</span><span class="tag">carla</span></span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li><li class="opt"><div class="node opt st-idee" tabindex="0" data-line="9" aria-label="Dark Mode, a11yStatus, a11ySize, a11yOptional" title="st_idee · a11yOptional · jumpHint">Dark Mode<span class="size" aria-hidden="true">S</span></div></li><li class="has-or"><div class="node st-geplant" tabindex="0" data-line="10" aria-label="CMS-Anbindung, a11yStatus, a11ySize" aria-expanded="true" title="st_geplant · jumpHint"><span class="fold" aria-hidden="true">▾</span>CMS-Anbindung<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node st-geplant" tabindex="0" data-line="11" aria-label="WordPress, a11yStatus" title="st_geplant · jumpHint">WordPress</div></li><li><div class="node st-idee" tabindex="0" data-line="12" aria-label="Headless CMS, a11yStatus" title="st_idee · jumpHint">Headless CMS</div></li><li><div class="node st-verworfen" tabindex="0" data-line="13" aria-label="Eigenentwicklung, a11yStatus" title="st_verworfen · jumpHint">Eigenentwicklung</div></li></ul></li></ul></li><li class="has-or"><div class="node st-idee" tabindex="0" data-line="14" aria-label="Hosting, a11yStatus, a11ySize" aria-expanded="true" title="st_idee · jumpHint"><span class="fold" aria-hidden="true">▾</span>Hosting<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node" tabindex="0" data-line="15" aria-label="Cloud" title="jumpHint">Cloud</div></li><li><div class="node" tabindex="0" data-line="16" aria-label="On-Premise" title="jumpHint">On-Premise</div></li></ul></li></ul></li>"`;
|
||||
exports[`renderTreeHtml — kanonisches Beispiel > verworfene einblenden: Eigenentwicklung erscheint (durchgestrichen) 1`] = `"<li class="has-and"><a class="node root-node st-arbeit" href="https://wiki.example.de/relaunch" target="_blank" rel="noopener" data-line="2" aria-label="Website-Relaunch, a11yStatus, a11ySize, a11yLink" aria-expanded="true" data-tip="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Website-Relaunch<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">XL</span></a><ul class="and"><li class="has-and"><div class="node done st-fertig" tabindex="0" data-line="3" aria-label="Konzeption, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_fertig · jumpHint"><span class="fold" aria-hidden="true">▾</span>Konzeption<span class="size" aria-hidden="true">M</span></div><ul class="and"><li><div class="node done st-fertig" tabindex="0" data-line="4" aria-label="Zielgruppenanalyse, a11yStatus, a11ySize" data-tip="st_fertig · jumpHint">Zielgruppenanalyse<span class="size" aria-hidden="true">S</span></div></li><li><div class="node done st-fertig" tabindex="0" data-line="5" aria-label="Sitemap, a11yStatus, a11ySize" data-tip="st_fertig · jumpHint">Sitemap<span class="size" aria-hidden="true">XS</span></div></li></ul></li><li class="has-and"><div class="node st-arbeit" tabindex="0" data-line="6" aria-label="Umsetzung, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_arbeit · jumpHint"><span class="fold" aria-hidden="true">▾</span>Umsetzung<span class="size" aria-hidden="true">XL</span></div><ul class="and"><li><a class="node st-durchstich" href="https://git.example.de/frontend" target="_blank" rel="noopener" data-line="7" aria-label="Frontend, a11yStatus, a11ySize, a11yTags, a11yLink" data-tip="st_durchstich · jumpHint">Frontend<span class="ext" aria-hidden="true">↗</span><span class="size" aria-hidden="true">S</span><span class="tags" aria-hidden="true"><span class="tag">anna</span></span></a></li><li><div class="node st-geplant" tabindex="0" data-line="8" aria-label="Backend, a11yStatus, a11ySize, a11yTags" data-tip="st_geplant · jumpHint">Backend<span class="size" aria-hidden="true">L</span><span class="tags" aria-hidden="true"><span class="tag">ben</span><span class="tag">carla</span></span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li><li class="opt"><div class="node opt st-idee" tabindex="0" data-line="9" aria-label="Dark Mode, a11yStatus, a11ySize, a11yOptional" data-tip="st_idee · a11yOptional · jumpHint">Dark Mode<span class="size" aria-hidden="true">S</span></div></li><li class="has-or"><div class="node st-geplant" tabindex="0" data-line="10" aria-label="CMS-Anbindung, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_geplant · jumpHint"><span class="fold" aria-hidden="true">▾</span>CMS-Anbindung<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node st-geplant" tabindex="0" data-line="11" aria-label="WordPress, a11yStatus" data-tip="st_geplant · jumpHint">WordPress</div></li><li><div class="node st-idee" tabindex="0" data-line="12" aria-label="Headless CMS, a11yStatus" data-tip="st_idee · jumpHint">Headless CMS</div></li><li><div class="node st-verworfen" tabindex="0" data-line="13" aria-label="Eigenentwicklung, a11yStatus" data-tip="st_verworfen · jumpHint">Eigenentwicklung</div></li></ul></li></ul></li><li class="has-or"><div class="node st-idee" tabindex="0" data-line="14" aria-label="Hosting, a11yStatus, a11ySize" aria-expanded="true" data-tip="st_idee · jumpHint"><span class="fold" aria-hidden="true">▾</span>Hosting<span class="size" aria-hidden="true">M</span></div><ul class="or"><li><div class="node" tabindex="0" data-line="15" aria-label="Cloud" data-tip="jumpHint">Cloud</div></li><li><div class="node" tabindex="0" data-line="16" aria-label="On-Premise" data-tip="jumpHint">On-Premise</div></li></ul></li></ul></li>"`;
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('Unbekannte IDs warnen — Zyklen nie', () => {
|
||||
describe('Darstellung — Tooltip und aria, kein eigenes Zeichen', () => {
|
||||
it('zeigt die Abhängigkeiten als → im Tooltip und als a11yDeps', () => {
|
||||
const {html} = render(`[ ] A #a\n[ ] B #b\n[ ] C :#a,#b`);
|
||||
expect(html).toContain('title="→ #a, #b · st_geplant · jumpHint"');
|
||||
expect(html).toContain('data-tip="→ #a, #b · st_geplant · jumpHint"');
|
||||
expect(html).toContain('aria-label="C, a11yStatus, a11yDeps"');
|
||||
});
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Langform — ID-Blöcke hinter `---`', () => {
|
||||
describe('Darstellung — Tooltip, ”-Marke, aria', () => {
|
||||
it('stellt die Beschreibung an den Anfang des Tooltips und setzt die Marke', () => {
|
||||
const {html} = render(`[ ] Auth #auth\n " Kapselt Login.`);
|
||||
expect(html).toContain('title="Kapselt Login.\n\n' + '─'.repeat(24)
|
||||
expect(html).toContain('data-tip="Kapselt Login.\n\n' + '─'.repeat(24)
|
||||
+ '\n#auth · st_geplant · jumpHint"');
|
||||
expect(html).toContain('<span class="desc-mark" aria-hidden="true">”</span>');
|
||||
expect(html).toContain('aria-label="Auth, a11yStatus, a11yId, Kapselt Login."');
|
||||
@@ -112,7 +112,7 @@ describe('Darstellung — Tooltip, ”-Marke, aria', () => {
|
||||
der Übergang unter. Getrennt wird nur, wenn es etwas zu trennen gibt. */
|
||||
it('trennt Beschreibung und Kurz-Fakten durch Leerzeile und Strich', () => {
|
||||
const {html} = render(`[ ] Auth #auth\n " Erste Zeile.\n " Zweite Zeile.`);
|
||||
const tip = html.match(/title="([^"]*)"/)[1];
|
||||
const tip = html.match(/data-tip="([^"]*)"/)[1];
|
||||
const [text, rest] = tip.split('\n\n');
|
||||
expect(text).toBe('Erste Zeile.\nZweite Zeile.'); /* Fließtext bleibt zusammen */
|
||||
expect(rest.split('\n')[0]).toMatch(/^─+$/); /* Trennstrich als eigene Zeile */
|
||||
@@ -121,7 +121,7 @@ describe('Darstellung — Tooltip, ”-Marke, aria', () => {
|
||||
|
||||
it('setzt keinen Trennstrich, wenn es keine Beschreibung gibt', () => {
|
||||
const {html} = render(`[ ] Ohne #auth`);
|
||||
expect(html.match(/title="([^"]*)"/)[1]).not.toContain('─');
|
||||
expect(html.match(/data-tip="([^"]*)"/)[1]).not.toContain('─');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('Darstellung — Farbe effektiv, Marke intrinsisch', () => {
|
||||
|
||||
it('benennt beide Status in Tooltip und aria-label', () => {
|
||||
const {html} = render(TXT);
|
||||
expect(html).toContain('title="→ #api · heldTooltip · jumpHint"');
|
||||
expect(html).toContain('data-tip="→ #api · heldTooltip · jumpHint"');
|
||||
expect(html).toContain('aria-label="Deploy, a11yStatus, a11yEffective, a11yDeps"');
|
||||
});
|
||||
|
||||
|
||||
@@ -115,13 +115,13 @@ describe('Doppelte IDs — Warnung an der späteren Zeile', () => {
|
||||
describe('Darstellung — Tooltip und aria, kein Badge', () => {
|
||||
it('zeigt die ID im Tooltip und im aria-label', () => {
|
||||
const {html} = render(`[ ] Auth #auth`);
|
||||
expect(html).toContain('title="#auth · st_geplant · jumpHint"');
|
||||
expect(html).toContain('data-tip="#auth · st_geplant · jumpHint"');
|
||||
expect(html).toContain('aria-label="Auth, a11yStatus, a11yId"');
|
||||
});
|
||||
|
||||
it('ändert Knoten ohne ID nicht', () => {
|
||||
const {html} = render(`[ ] Ohne`);
|
||||
expect(html).not.toContain('a11yId');
|
||||
expect(html).toContain('title="st_geplant · jumpHint"');
|
||||
expect(html).toContain('data-tip="st_geplant · jumpHint"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('renderTreeHtml — „Untergliederung fehlt" (Geister-Knoten, SPEC §5
|
||||
const {html} = renderTreeHtml(roots, {t, showDiscarded: false, cheapPath: false, cheapSet: new Set()});
|
||||
expect(count(html, 'ghost-node')).toBe(1);
|
||||
expect(html).toContain('title="ghostTooltip"');
|
||||
expect(html).toMatchInlineSnapshot(`"<li><div class="node root-node st-geplant" tabindex="0" data-line="1" aria-label="Großes Paket, a11yStatus, a11ySize" title="st_geplant · jumpHint">Großes Paket<span class="size" aria-hidden="true">L</span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li>"`);
|
||||
expect(html).toMatchInlineSnapshot(`"<li><div class="node root-node st-geplant" tabindex="0" data-line="1" aria-label="Großes Paket, a11yStatus, a11ySize" data-tip="st_geplant · jumpHint">Großes Paket<span class="size" aria-hidden="true">L</span></div><div class="ghost-node" aria-label="ghostTooltip" title="ghostTooltip">ghost</div></li>"`);
|
||||
});
|
||||
|
||||
it('verworfenes M+ löst die Regel nicht aus', () => {
|
||||
|
||||
Reference in New Issue
Block a user