From f7b0072853ae00d1466598406776e9390cd2140d Mon Sep 17 00:00:00 2001 From: "Leon.Schmidt" Date: Sun, 16 Aug 2026 20:11:40 +0200 Subject: [PATCH] FIX - polish House header and sheets Respect the shared phone safe area, keep the House subtitle visible, and align the keys list with the sheet content width. Make the shared sheet grabber optionally clickable while preserving swipe-to-close behavior and add focused contract coverage. --- frontend/src/ui/overlays.css | 5 +++ frontend/src/ui/overlays/SkySheet.test.ts | 14 +++++++ frontend/src/ui/overlays/SkySheet.vue | 15 ++++++-- .../src/views/apps/HouseApp.contract.test.ts | 22 ++++++++++- frontend/src/views/apps/HouseApp.vue | 38 +++++++++++++++++-- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/frontend/src/ui/overlays.css b/frontend/src/ui/overlays.css index b3608c1..962992c 100644 --- a/frontend/src/ui/overlays.css +++ b/frontend/src/ui/overlays.css @@ -223,9 +223,14 @@ top: 0; width: 100%; height: 32px; + margin: 0; + padding: 0; display: grid; place-items: center; + border: 0; background: inherit; + color: inherit; + font: inherit; cursor: ns-resize; touch-action: none; user-select: none; diff --git a/frontend/src/ui/overlays/SkySheet.test.ts b/frontend/src/ui/overlays/SkySheet.test.ts index 45f81c5..baac1a6 100644 --- a/frontend/src/ui/overlays/SkySheet.test.ts +++ b/frontend/src/ui/overlays/SkySheet.test.ts @@ -30,6 +30,19 @@ describe('SkySheet', () => { expect(ordinary).not.toContain('sky-sheet__grabber') }) + it('can expose the drag handle as an accessible close button', async () => { + const clickable = await renderSheet({ + grabberClickable: true, + grabberLabel: 'Close property details', + opened: true, + swipeToClose: true, + }) + + expect(clickable).toContain(' { const source = readFileSync( new URL('./SkySheet.vue', import.meta.url), @@ -41,6 +54,7 @@ describe('SkySheet', () => { ) expect(source).toContain('swipeclose: [event: PointerEvent]') + expect(source).toContain('grabberclick: [event: MouseEvent]') expect(source).toContain('setPointerCapture(event.pointerId)') expect(source).toContain('dragOffset.value >= closeThreshold') expect(source).toContain("emit('swipeclose', event)") diff --git a/frontend/src/ui/overlays/SkySheet.vue b/frontend/src/ui/overlays/SkySheet.vue index fb55ac6..f3347b2 100644 --- a/frontend/src/ui/overlays/SkySheet.vue +++ b/frontend/src/ui/overlays/SkySheet.vue @@ -12,6 +12,8 @@ const props = withDefaults( ariaModal?: boolean | 'false' | 'true' backdrop?: boolean component?: string + grabberClickable?: boolean + grabberLabel?: string opened: boolean role?: 'alertdialog' | 'dialog' | 'none' | 'presentation' swipeToClose?: boolean @@ -21,12 +23,15 @@ const props = withDefaults( ariaModal: true, backdrop: true, component: 'div', + grabberClickable: false, + grabberLabel: '', tabindex: -1, }, ) const emit = defineEmits<{ backdropclick: [event: MouseEvent] escape: [event: KeyboardEvent] + grabberclick: [event: MouseEvent] swipeclose: [event: PointerEvent] }>() @@ -200,16 +205,20 @@ useOverlayFocusTrap({ :aria-describedby="effectiveRole ? ariaDescribedby : undefined" :tabindex="tabindex" > - + > diff --git a/frontend/src/views/apps/HouseApp.contract.test.ts b/frontend/src/views/apps/HouseApp.contract.test.ts index 02d4ad9..14c48c6 100644 --- a/frontend/src/views/apps/HouseApp.contract.test.ts +++ b/frontend/src/views/apps/HouseApp.contract.test.ts @@ -5,10 +5,13 @@ import { describe, expect, it } from 'vitest' const source = readFileSync(new URL('./HouseApp.vue', import.meta.url), 'utf8') describe('House app sheets', () => { - it('closes both sheets through their shared drag gesture', () => { + it('closes both sheets through their shared drag gesture or grabber button', () => { expect(source.match(/swipe-to-close/g)).toHaveLength(2) + expect(source.match(/grabber-clickable/g)).toHaveLength(2) expect(source).toContain('@swipeclose="selectedPropertyId = null"') expect(source).toContain('@swipeclose="candidatesOpened = false"') + expect(source).toContain('@grabberclick="selectedPropertyId = null"') + expect(source).toContain('@grabberclick="candidatesOpened = false"') }) it('sizes the panels instead of clipping the overlay roots', () => { @@ -23,4 +26,21 @@ describe('House app sheets', () => { ) expect(source).not.toContain('height: 620px;') }) + + it('keeps the header clear of the status bar and shows its full subtitle', () => { + expect(source).not.toContain('--sky-safe-area-top: 46px') + expect(source).toMatch( + /\.house-navbar :deep\(\.sky-navbar__heading\)\s*\{[^}]*grid-column:\s*1 \/ -1;/s, + ) + expect(source).toMatch( + /\.house-navbar :deep\(\.sky-navbar__subtitle\)\s*\{[^}]*text-overflow:\s*clip;[^}]*white-space:\s*normal;/s, + ) + }) + + it('uses the full popup content width for the keys list', () => { + expect(source).toContain('class="house-key-list"') + expect(source).toMatch( + /\.house-key-list\s*\{[^}]*--sky-list-outer-left:\s*0px;[^}]*--sky-list-outer-right:\s*0px;/s, + ) + }) }) diff --git a/frontend/src/views/apps/HouseApp.vue b/frontend/src/views/apps/HouseApp.vue index 343bbf0..e0ee700 100644 --- a/frontend/src/views/apps/HouseApp.vue +++ b/frontend/src/views/apps/HouseApp.vue @@ -363,9 +363,12 @@ onBeforeUnmount(() => {
@@ -505,7 +508,12 @@ onBeforeUnmount(() => { {{ phone.t('Apps.house.addKey') }} - + {
@@ -635,7 +646,6 @@ onBeforeUnmount(() => { linear-gradient(165deg, #26201d 0, #111216 48%, #171d25 100%); } .house-navbar { - --sky-safe-area-top: 46px; --sky-navbar-glass: transparent; position: absolute; z-index: 5; @@ -648,19 +658,29 @@ onBeforeUnmount(() => { opacity: 0; } .house-navbar :deep(.sky-navbar__heading) { + width: 100%; justify-content: center; + grid-column: 1 / -1; gap: 1px; + padding: 0 var(--sky-page-gutter); } .house-navbar :deep(.sky-navbar__title) { line-height: 24px; } .house-navbar :deep(.sky-navbar__subtitle) { + max-width: 100%; margin-top: 0; + overflow: visible; line-height: 15px; + text-overflow: clip; + white-space: normal; } .house-scroll { position: absolute; - inset: 104px 0 25px; + inset: calc( + var(--sky-safe-area-top) + var(--sky-navbar-height) + var(--sky-space-3) + ) + 0 25px; padding: 9px 13px 34px; overflow-x: hidden; overflow-y: auto; @@ -685,7 +705,10 @@ onBeforeUnmount(() => { } .house-state { position: absolute; - inset: 104px 0 25px; + inset: calc( + var(--sky-safe-area-top) + var(--sky-navbar-height) + var(--sky-space-3) + ) + 0 25px; padding: 30px; display: flex; flex-direction: column; @@ -1228,6 +1251,13 @@ onBeforeUnmount(() => { .house-keys { padding: 0 16px; } +.house-key-list { + --sky-list-outer-left: 0px; + --sky-list-outer-right: 0px; + width: 100%; + margin-top: 0; + margin-bottom: 0; +} .house-keys header { margin: 20px 4px 8px; }