Kundenansicht neu: Leiste im Chat am iPhone wieder ganz unten
Die Messung am iPhone (iOS 26.2, App vom Home-Bildschirm) zeigt die
Ursache: Auf der Startseite ist das Fenster 1002 px hoch. Im Chat
schrumpft die Spalte auf den Kopf (alles andere steht fest), die Seite
wird kürzer als der Bildschirm, und iOS hält unten rund 60 pt für eine
Safari-Leiste frei, die es in der App nicht gibt: 932 px, Leiste und
Eingabe stehen zu hoch, darunter ein weißer Streifen. Mit der Tastatur
hat das nichts zu tun.
Am Handy hat die Spalte im Chat jetzt wie in den anderen Bereichen
mindestens volle Bildschirmhöhe (100vh, dann 100lvh). Ohne
Browserleisten scrollt die Seite dadurch nicht; in Chromium sind alle
Chat-Bilder am Handy und Desktop pixelgleich zu main.
Der Höhen-Wächter aus 0fa8fdb (Hülle aus- und einblenden) hat nicht
geholfen und ist wieder raus. Die Messung bleibt für die Kontrolle am
iPhone und wird danach entfernt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cca29a4aef
commit
17bce304eb
6 files changed
+28
-224
No files matched your search
@@ -0,0 +1,20 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { lies, handyTeil } from './support/knHuelle.js'
|
||||
|
||||
// iPhone-App vom Home-Bildschirm (Messung 2026-10-29): ist die Seite im Chat kürzer als der Bildschirm, macht iOS 26 das Fenster rund 60 pt kleiner
|
||||
// und die Leiste steht zu hoch. Am Handy muss die Spalte im Chat daher mindestens volle Bildschirmhöhe haben, wie in den anderen Bereichen.
|
||||
test('huelle.css: am Handy hat die Spalte im Chat mindestens volle Höhe (100vh, dann 100lvh), am Desktop bleibt min-height 0', () => {
|
||||
const css = lies('huelle.css')
|
||||
const handy = handyTeil(css)
|
||||
assert.match(handy, /\.kn-spalte:has\(#bereich-chat:not\(\[hidden\]\)\) \{ display: block; height: auto; min-height: 100vh; min-height: 100lvh; \}/)
|
||||
const desktop = css.slice(0, css.indexOf('.kn-spalte:has(#bereich-chat:not([hidden])) { display: block'))
|
||||
assert.match(desktop, /\.kn-spalte:has\(#bereich-chat:not\(\[hidden\]\)\) \{ display: flex;[^}]*min-height: 0; \}/)
|
||||
assert.match(css, /\.kn-spalte \{ position: relative; min-height: 100vh;/)
|
||||
})
|
||||
|
||||
test('huelle.js: der alte Höhen-Wächter (Hülle aus- und einblenden) ist weg, die Messung läuft nur am iPhone und nicht in der Mitarbeiter-Ansicht', () => {
|
||||
const js = lies('huelle.js')
|
||||
assert.doesNotMatch(js, /beobachteHoehe|'\.\/hoehe\.js'/)
|
||||
assert.match(js, /if \(istIos\(\) && istHandy\(\) && !viewAs\) starteDiagHoehe\(\{ win, doc \}\)/)
|
||||
})
|
||||
@@ -1,122 +0,0 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { LUFT, MAX_VERSUCHE, PRUEF_MS, beobachteHoehe, haengt, hoeheZustand } from '../public/neu/hoehe.js'
|
||||
import { lies } from './support/knHuelle.js'
|
||||
|
||||
test('hoeheZustand: größte Höhe je Breite, Drehen fängt neu an', () => {
|
||||
let z = hoeheZustand(null, { hoehe: 932, breite: 430 })
|
||||
z = hoeheZustand(z, { hoehe: 873, breite: 430 }); assert.deepEqual(z, { voll: 932, breite: 430 })
|
||||
z = hoeheZustand(z, { hoehe: 430, breite: 932 }); assert.deepEqual(z, { voll: 430, breite: 932 })
|
||||
})
|
||||
|
||||
test('haengt: nur bei gleicher Breite, ohne Textfeld und mehr als LUFT kleiner (iOS-Fall 932 → 873)', () => {
|
||||
const z = { voll: 932, breite: 430 }
|
||||
assert.equal(haengt(z, { hoehe: 873, breite: 430, textfeldAktiv: false }), true)
|
||||
assert.equal(haengt(z, { hoehe: 873, breite: 430, textfeldAktiv: true }), false) // Tastatur offen
|
||||
assert.equal(haengt(z, { hoehe: 932 - LUFT, breite: 430, textfeldAktiv: false }), false)
|
||||
assert.equal(haengt(z, { hoehe: 400, breite: 932, textfeldAktiv: false }), false) // gedreht
|
||||
assert.equal(haengt(null, { hoehe: 1, breite: 1 }), false)
|
||||
})
|
||||
|
||||
/** Kleine Attrappe: Fenster, Dokument und eine Hülle, die mitschreibt, wann sie aus- und eingeblendet wird. */
|
||||
function aufbau({ aktiv = () => true } = {}) {
|
||||
const sender = (felder = {}) => {
|
||||
const hoerer = {}
|
||||
return Object.assign({
|
||||
addEventListener(typ, fn) { (hoerer[typ] ??= []).push(fn) },
|
||||
removeEventListener(typ, fn) { hoerer[typ] = (hoerer[typ] ?? []).filter((x) => x !== fn) },
|
||||
senden(typ) { for (const fn of hoerer[typ] ?? []) fn({ type: typ }) },
|
||||
anzahl: (typ) => (hoerer[typ] ?? []).length,
|
||||
}, felder)
|
||||
}
|
||||
const spaeter = []
|
||||
const protokoll = []
|
||||
const win = sender({
|
||||
innerHeight: 932, innerWidth: 430, scrollX: 0, scrollY: 0,
|
||||
setTimeout: (fn, ms) => { spaeter.push({ fn, ms }); return spaeter.length },
|
||||
clearTimeout: (id) => { if (spaeter[id - 1]) spaeter[id - 1].fn = () => {} },
|
||||
scrollTo: (x, y) => { win.scrollX = x; win.scrollY = y; protokoll.push(['scrollTo', x, y]) },
|
||||
})
|
||||
const verlauf = { scrollTop: 1200, scrollLeft: 0 }
|
||||
const haken = {}
|
||||
const style = {}
|
||||
let display = ''
|
||||
Object.defineProperty(style, 'display', { get: () => display, set: (v) => { display = v; protokoll.push(['display', v]); if (v === 'none') { verlauf.scrollTop = 0; win.scrollY = 0; haken.beimAusblenden?.() } } })
|
||||
const knopf = { focus: () => protokoll.push(['focus']) }
|
||||
const element = {
|
||||
style,
|
||||
get offsetHeight() { protokoll.push(['umbruch']); return 100 },
|
||||
querySelectorAll: () => [verlauf, { scrollTop: 0, scrollLeft: 0 }],
|
||||
contains: (el) => el === knopf || el === verlauf,
|
||||
}
|
||||
const doc = sender({ activeElement: null, body: {}, visibilityState: 'visible' })
|
||||
const istFeld = (el) => el?.feld === true
|
||||
const h = beobachteHoehe({ win, doc, element, aktiv, istTextfeld: istFeld })
|
||||
const laufe = () => { while (spaeter.length) spaeter.shift().fn() }
|
||||
return { win, doc, element, verlauf, knopf, protokoll, spaeter, h, laufe, haken }
|
||||
}
|
||||
|
||||
test('beobachteHoehe: Tastatur zu und Höhe hängt → Hülle einmal aus und ein, Scrollstände zurück; danach Ruhe', () => {
|
||||
const t = aufbau()
|
||||
t.win.scrollY = 40
|
||||
t.doc.activeElement = { feld: true }
|
||||
t.win.innerHeight = 873; t.win.senden('resize') // iOS: die Höhe fällt mit der Tastatur und bleibt unten
|
||||
assert.equal(t.spaeter.length, 0) // solange das Feld den Fokus hat, wird nicht geprüft
|
||||
t.doc.activeElement = t.doc.body; t.doc.senden('focusout')
|
||||
assert.deepEqual(t.spaeter.map((s) => s.ms), PRUEF_MS)
|
||||
t.spaeter.shift().fn()
|
||||
assert.deepEqual(t.protokoll, [['display', 'none'], ['umbruch'], ['display', ''], ['umbruch'], ['scrollTo', 0, 40]])
|
||||
assert.equal(t.verlauf.scrollTop, 1200)
|
||||
t.win.innerHeight = 932 // WebKit hat neu gemessen
|
||||
t.protokoll.length = 0; t.laufe()
|
||||
assert.deepEqual(t.protokoll, [])
|
||||
assert.deepEqual(t.h.stand(), { voll: 932, breite: 430, versuche: 1 })
|
||||
})
|
||||
|
||||
test('beobachteHoehe: onHeile meldet vorher, nachher, voll und Versuch; ein Fehler im Beobachter stört nicht', () => {
|
||||
const t = aufbau()
|
||||
const meldungen = []
|
||||
t.h.beende()
|
||||
const h = beobachteHoehe({ win: t.win, doc: t.doc, element: t.element, istTextfeld: () => false, onHeile: (w) => { meldungen.push(w); throw new Error('kaputt') } })
|
||||
t.win.innerHeight = 873
|
||||
assert.equal(h.pruefe(), true)
|
||||
assert.deepEqual(meldungen, [{ vorher: 873, nachher: 873, voll: 932, versuche: 1 }])
|
||||
})
|
||||
|
||||
test('beobachteHoehe: hilft es nicht, höchstens MAX_VERSUCHE je Anlass; App wieder vorne ist ein neuer Anlass', () => {
|
||||
const t = aufbau()
|
||||
t.win.innerHeight = 873
|
||||
t.doc.senden('focusout'); t.laufe()
|
||||
t.win.senden('resize'); t.laufe()
|
||||
assert.equal(t.protokoll.filter((p) => p[0] === 'display' && p[1] === 'none').length, MAX_VERSUCHE)
|
||||
t.doc.visibilityState = 'hidden'; t.doc.senden('visibilitychange'); assert.equal(t.spaeter.length, 0)
|
||||
t.doc.visibilityState = 'visible'; t.doc.senden('visibilitychange'); t.laufe()
|
||||
assert.equal(t.protokoll.filter((p) => p[0] === 'display' && p[1] === 'none').length, 2 * MAX_VERSUCHE)
|
||||
})
|
||||
|
||||
test('beobachteHoehe: Fokus auf einem Knopf in der Hülle bleibt, ohne Scrollen', () => {
|
||||
const t = aufbau()
|
||||
t.win.innerHeight = 873
|
||||
t.doc.activeElement = t.knopf
|
||||
t.haken.beimAusblenden = () => { t.doc.activeElement = t.doc.body } // beim Ausblenden verliert der Knopf den Fokus (wie im Browser)
|
||||
assert.equal(t.h.pruefe(), true)
|
||||
assert.deepEqual(t.protokoll.at(-1), ['focus'])
|
||||
})
|
||||
|
||||
test('beobachteHoehe: nicht aktiv (kein iOS-App-Modus), keine Hülle oder Höhe passt → nichts passiert; beende räumt auf', () => {
|
||||
const aus = aufbau({ aktiv: () => false })
|
||||
aus.win.innerHeight = 873; assert.equal(aus.h.pruefe(), false); assert.deepEqual(aus.protokoll, [])
|
||||
const ok = aufbau()
|
||||
assert.equal(ok.h.pruefe(), false)
|
||||
ok.win.innerHeight = 932 - LUFT; assert.equal(ok.h.pruefe(), false); assert.deepEqual(ok.protokoll, [])
|
||||
ok.doc.senden('focusout'); ok.h.beende(); ok.win.innerHeight = 873; ok.laufe()
|
||||
assert.deepEqual(ok.protokoll, [])
|
||||
assert.deepEqual([ok.win.anzahl('resize'), ok.doc.anzahl('focusout'), ok.doc.anzahl('visibilitychange')], [0, 0, 0])
|
||||
})
|
||||
|
||||
test('huelle.js: Höhen-Wächter nur in der iOS-App vom Home-Bildschirm am Handy, auf der Hülle', () => {
|
||||
const js = lies('huelle.js')
|
||||
assert.match(js, /import \{ beobachteHoehe \} from '\.\/hoehe\.js'/)
|
||||
assert.match(js, /beobachteHoehe\(\{ win, doc, element: doc\.querySelector\('\.kn-huelle'\), aktiv: \(\) => istIos\(\) && standalone\(\) && istHandy\(\), istTextfeld, onHeile:/)
|
||||
assert.match(js, /if \(istIos\(\) && istHandy\(\) && !viewAs\) diag = starteDiagHoehe\(/)
|
||||
})
|
||||
Reference in new issue
Block a user