fix: strip Aura .p-drawer-content padding in mobile drawer (switcher stays bottom-anchored) #29
@@ -151,6 +151,15 @@ const mobileVisible = computed<boolean>({
|
|||||||
the *visual* viewport — if the switcher is reported clipped on a device,
|
the *visual* viewport — if the switcher is reported clipped on a device,
|
||||||
that dynamic-viewport (`dvh`) interaction is the remaining suspect and
|
that dynamic-viewport (`dvh`) interaction is the remaining suspect and
|
||||||
needs a real-device check (PrimeVue sets the mask height inline).
|
needs a real-device check (PrimeVue sets the mask height inline).
|
||||||
|
|
||||||
|
Wrapper padding: Aura themes `.p-drawer-content` with
|
||||||
|
`padding: 0 {modal.padding} {modal.padding} {modal.padding}` (~20px on the
|
||||||
|
sides + bottom), which insets the whole logo/menu/switcher block from the
|
||||||
|
drawer edges. The desktop <aside> has no such padding, so we strip it for
|
||||||
|
edge-to-edge parity with `!p-0` — the important variant is required because
|
||||||
|
plain `p-0` lost to Aura's `.p-drawer-content` rule in stylesheet order
|
||||||
|
(same failure mode as the header `!hidden` above). The children own their
|
||||||
|
own internal spacing exactly as on desktop.
|
||||||
-->
|
-->
|
||||||
<!-- mobile -->
|
<!-- mobile -->
|
||||||
<Drawer
|
<Drawer
|
||||||
@@ -160,15 +169,12 @@ const mobileVisible = computed<boolean>({
|
|||||||
class="!w-64"
|
class="!w-64"
|
||||||
:show-close-icon="false"
|
:show-close-icon="false"
|
||||||
:pt="{
|
:pt="{
|
||||||
content: { class: 'flex flex-col p-0 overflow-hidden h-full min-h-0' },
|
content: { class: 'flex flex-col !p-0 overflow-hidden h-full min-h-0' },
|
||||||
header: { class: '!hidden' },
|
header: { class: '!hidden' },
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<SidebarHeader />
|
<SidebarHeader />
|
||||||
<SidebarNav
|
<SidebarNav :collapsed="false" />
|
||||||
:collapsed="false"
|
|
||||||
:grow="false"
|
|
||||||
/>
|
|
||||||
<WorkspaceSwitcher :collapsed="false" />
|
<WorkspaceSwitcher :collapsed="false" />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,21 +22,9 @@ import type { RouteLocationRaw } from 'unplugin-vue-router'
|
|||||||
import Icon from '@/components/Icon.vue'
|
import Icon from '@/components/Icon.vue'
|
||||||
import { APP_NAVIGATION, type NavItem } from '@/config/navigation'
|
import { APP_NAVIGATION, type NavItem } from '@/config/navigation'
|
||||||
|
|
||||||
withDefaults(defineProps<{
|
defineProps<{
|
||||||
collapsed: boolean
|
collapsed: boolean
|
||||||
/**
|
}>()
|
||||||
* When true (default), the nav fills available vertical space (`flex-1`)
|
|
||||||
* so a bottom-anchored sibling (WorkspaceSwitcher) pins to the bottom — the
|
|
||||||
* desktop sidebar behaviour. Set false in the mobile drawer (`flex-initial`)
|
|
||||||
* so the nav takes its natural height — logo + menu + switcher group together
|
|
||||||
* at the top instead of leaving a void above a bottom-pinned switcher
|
|
||||||
* (MOBILE-SHELL-PARITY follow-up). `flex-initial` (not `flex-none`) keeps
|
|
||||||
* `flex-shrink: 1`, so if the menu ever grows past the panel height the nav
|
|
||||||
* shrinks and scrolls internally (`min-h-0 overflow-y-auto`) rather than
|
|
||||||
* pushing the switcher out of the `overflow-hidden` drawer content.
|
|
||||||
*/
|
|
||||||
grow?: boolean
|
|
||||||
}>(), { grow: true })
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -64,10 +52,7 @@ function itemTo(item: NavItem): RouteLocationRaw {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav
|
<nav class="flex-1 min-h-0 overflow-y-auto py-3.5 px-2.5 [scrollbar-width:thin]">
|
||||||
class="min-h-0 overflow-y-auto py-3.5 px-2.5 [scrollbar-width:thin]"
|
|
||||||
:class="grow ? 'flex-1' : 'flex-initial'"
|
|
||||||
>
|
|
||||||
<template
|
<template
|
||||||
v-for="topItem in APP_NAVIGATION"
|
v-for="topItem in APP_NAVIGATION"
|
||||||
:key="topItem.key"
|
:key="topItem.key"
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ const globalStubs = {
|
|||||||
SidebarHeader: { name: 'SidebarHeader', template: '<div class="sidebar-header-stub" />' },
|
SidebarHeader: { name: 'SidebarHeader', template: '<div class="sidebar-header-stub" />' },
|
||||||
SidebarNav: {
|
SidebarNav: {
|
||||||
name: 'SidebarNav',
|
name: 'SidebarNav',
|
||||||
props: ['collapsed', 'grow'],
|
props: ['collapsed'],
|
||||||
template: '<div class="sidebar-nav-stub" :data-collapsed="collapsed" :data-grow="grow" />',
|
template: '<div class="sidebar-nav-stub" :data-collapsed="collapsed" />',
|
||||||
},
|
},
|
||||||
WorkspaceSwitcher: {
|
WorkspaceSwitcher: {
|
||||||
name: 'WorkspaceSwitcher',
|
name: 'WorkspaceSwitcher',
|
||||||
@@ -203,33 +203,21 @@ describe('AppSidebar', () => {
|
|||||||
expect(drawer.find('.workspace-switcher-stub').exists()).toBe(true)
|
expect(drawer.find('.workspace-switcher-stub').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('mobile: SidebarNav is told NOT to grow (grow=false) so logo/menu/switcher group at the top', async () => {
|
it('mobile: drawer content strips the Aura wrapper padding (!p-0) for edge-to-edge parity', async () => {
|
||||||
mockIsMobileRef.value = true
|
mockIsMobileRef.value = true
|
||||||
|
|
||||||
const wrapper = mountSidebar()
|
const wrapper = mountSidebar()
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
// grow=false → nav takes natural height, no flex-1 fill → no void above a
|
const pt = wrapper.findComponent(DrawerStub).props('pt') as {
|
||||||
// bottom-pinned WorkspaceSwitcher (they group together at the top instead).
|
content: { class: string }
|
||||||
const navInDrawer = wrapper.find('.drawer-stub').find('.sidebar-nav-stub')
|
}
|
||||||
|
|
||||||
expect(navInDrawer.attributes('data-grow')).toBe('false')
|
// Aura sets .p-drawer-content padding; plain p-0 loses to it in stylesheet
|
||||||
})
|
// order, so the important variant is required to remove the inset around
|
||||||
|
// the logo/menu/switcher block.
|
||||||
it('desktop: SidebarNav is allowed to grow (WorkspaceSwitcher stays bottom-anchored)', async () => {
|
expect(pt.content.class).toContain('!p-0')
|
||||||
mockIsMobileRef.value = false
|
|
||||||
|
|
||||||
const wrapper = mountSidebar()
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
|
||||||
|
|
||||||
// The desktop <aside> passes no grow prop → defaults to true (flex-1),
|
|
||||||
// keeping the switcher pinned at the bottom. The stub does not apply the
|
|
||||||
// component default, so we assert grow is NOT explicitly false here.
|
|
||||||
const navInAside = wrapper.find('aside').find('.sidebar-nav-stub')
|
|
||||||
|
|
||||||
expect(navInAside.attributes('data-grow')).not.toBe('false')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user