From 6ddc9ebcab901915757269006569ffcb87c4913e Mon Sep 17 00:00:00 2001 From: Ludwig Lehnert Date: Sat, 3 Oct 2026 18:49:07 +0000 Subject: [PATCH] minor ui improvements --- app/web/app.js | 2 +- app/web/styles.css | 7 ++++++- tests/document_ui_smoke.mjs | 39 ++++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/app/web/app.js b/app/web/app.js index 7cf535f..448f383 100644 --- a/app/web/app.js +++ b/app/web/app.js @@ -11,7 +11,7 @@ const esc = value => String(value ?? "").replace(/[&<>'"]/g, char => ({"&":"& function actionIcon(action) { const paths = { download: '', - restore: '', + restore: '', }; return ``; } diff --git a/app/web/styles.css b/app/web/styles.css index f531d41..4bf5eea 100644 --- a/app/web/styles.css +++ b/app/web/styles.css @@ -216,7 +216,12 @@ mark { background: #fff0a0; color: #111; } .document-dialog-head p { font-size: .85rem; overflow-wrap: anywhere; margin-bottom: 0; } .document-detail { height: calc(100% - 95px); min-height: 0; } .document-preview { padding: 1rem; overflow: auto; background: #f5f5f5; } -.document-preview img { display: block; width: 100%; height: auto; } +.document-dialog:has(.document-preview img) { overflow: hidden; } +.document-dialog:has(.document-preview img)[open] { display: flex; flex-direction: column; } +.document-dialog:has(.document-preview img) .document-dialog-head { flex-shrink: 0; } +.document-dialog:has(.document-preview img) .document-detail { flex: 1; height: auto; overflow: hidden; } +.document-dialog:has(.document-preview img) .document-preview { display: flex; align-items: center; justify-content: center; height: 100%; overflow: hidden; } +.document-preview img { display: block; width: auto; height: auto; max-width: 100%; max-height: 100%; object-fit: contain; } .document-dialog.pdf-dialog { width: 100vw; height: 100dvh; max-width: 100vw; max-height: 100dvh; margin: 0; border: 0; overflow: hidden; } .document-dialog.pdf-dialog[open] { display: flex; flex-direction: column; } .pdf-dialog .document-dialog-head { flex-shrink: 0; padding: .6rem 1rem; } diff --git a/tests/document_ui_smoke.mjs b/tests/document_ui_smoke.mjs index 43b194d..9cc908a 100644 --- a/tests/document_ui_smoke.mjs +++ b/tests/document_ui_smoke.mjs @@ -57,7 +57,12 @@ try { const parts=url.pathname.split('/'),row=fixtures.find(row=>row.id===parts[3]); if(!row||row.id===deniedId){status=404;body={error:'Datei nicht verfügbar'};} else if(parts[4]==='preview'){ + if(row.previewWidth){ + const width=row.previewWidth,height=row.previewHeight; + type='image/svg+xml';body=Buffer.from(`Oben · ${width} × ${height}Unten · vollständiges Bild`); + }else{ type='image/svg+xml';body=Buffer.from(`RECHNUNGOktober 2026 · FINANZ742Büroausstattung3 × Bildschirm1.500,00 EURGesamtbetrag: 1.500,00 EUR`); + } }else if(parts[4]==='content'){type='application/pdf';body=pdf;}else body=row; }else if(url.pathname.startsWith('/assets/vendor/pdfjs/6.3.289-app1/')){ const relative=url.pathname.slice('/assets/vendor/pdfjs/6.3.289-app1/'.length); @@ -122,6 +127,38 @@ try { } fixtures[1].extension='docx'; await page.locator('#document-close').click(); + // Large images must fit in both dimensions, including after resizing an open dialog. + const originalOffice={...fixtures[1]}; + for(const [orientation,width,height] of [['portrait',4000,7000],['landscape',7000,4000],['small',180,240]]){ + Object.assign(fixtures[1],{extension:'png',name:'Bildvorschau.png',path:'Dokumente/Ein sehr langer Ordnername für die Prüfung mehrzeiliger Metadaten/Bildvorschau.png',hasPreview:true,previewWidth:width,previewHeight:height,version:width.toString(16).padStart(16,'0')}); + await page.setViewportSize({width:1500,height:1050}); + await page.locator(`[data-document="${fixtures[1].id}"]`).click(); + await page.locator('#document-preview img').waitFor(); + await page.waitForFunction(()=>{const img=document.querySelector('#document-preview img');return img?.complete&&img.naturalWidth>0;}); + for(const [device,viewport] of [['desktop',{width:1500,height:1050}],['mobile',{width:390,height:844}],['mobile-landscape',{width:844,height:390}]]){ + await page.setViewportSize(viewport); + const metrics=await page.evaluate(()=>{ + const dialog=document.querySelector('#document-dialog'),preview=document.querySelector('#document-preview'),img=preview.querySelector('img'); + const bounds=element=>{const {x,y,width,height}=element.getBoundingClientRect();return {x,y,width,height};}; + return {dialog:bounds(dialog),preview:bounds(preview),image:bounds(img),naturalWidth:img.naturalWidth,naturalHeight:img.naturalHeight,scrolls:[dialog,preview].map(element=>({x:element.scrollWidth-element.clientWidth,y:element.scrollHeight-element.clientHeight}))}; + }); + assert.deepEqual([metrics.naturalWidth,metrics.naturalHeight],[width,height]); + for(const scroll of metrics.scrolls){assert.ok(scroll.x<=1,'Image preview must not scroll horizontally');assert.ok(scroll.y<=1,'Image preview must not scroll vertically');} + assert.ok(Math.abs(metrics.image.width/metrics.image.height-width/height)<0.01,'Image aspect ratio must be preserved'); + assert.ok(metrics.image.width<=width&&metrics.image.height<=height,'Small images must not be enlarged'); + assert.ok(metrics.image.width>0&&metrics.image.height>0,'Image must remain visible'); + assert.ok(metrics.image.x>=metrics.preview.x&&metrics.image.y>=metrics.preview.y); + assert.ok(metrics.image.x+metrics.image.width<=metrics.preview.x+metrics.preview.width+1); + assert.ok(metrics.image.y+metrics.image.height<=metrics.preview.y+metrics.preview.height+1); + assert.ok(metrics.dialog.y>=0&&metrics.dialog.y+metrics.dialog.height<=viewport.height+1,'Dialog must fit the viewport'); + assert.equal(await page.evaluate(()=>getComputedStyle(document.documentElement).overflowY),'hidden'); + await shot(`06-image-${orientation}-${device}`); + } + await page.locator('#document-close').click(); + } + delete fixtures[1].previewWidth;delete fixtures[1].previewHeight; + Object.assign(fixtures[1],originalOffice); + await page.setViewportSize({width:1500,height:1050}); await page.locator('#source-filter').selectOption('private:alice'); await page.waitForFunction(()=>document.querySelectorAll('.document-card').length===1); assert.match(await page.locator('.document-card').innerText(),/Meine Notizen/); @@ -184,6 +221,6 @@ try { assert.equal(await page.locator('#admin-link').isVisible(),true); assert.ok(pendingRequests.includes('FINANZ742')&&pendingRequests.includes('SCANLIVE742')); assert.deepEqual(errors,[]); - console.log('PASS: user login, admin separation, live content search, own-private filter, preview/download links, OCR refresh, revocation, grid/list, desktop/mobile.'); + console.log('PASS: user login, admin separation, live content search, own-private filter, preview/download links, OCR refresh, revocation, grid/list, desktop/mobile, portrait/landscape image fitting.'); console.log('Screenshots: '+out); } finally {await browser.close();}