fix: group mobile drawer sections, drop the bottom-pinned void
In the mobile drawer the nav's flex-1 filled the full-height panel, pushing the WorkspaceSwitcher to the very bottom and leaving a large empty void between the menu and the switcher. Add a 'grow' prop to SidebarNav (default true = desktop bottom-anchor) and pass grow=false in the mobile drawer so the nav takes only its natural height — logo, menu and switcher group together at the top. Desktop sidebar unchanged. +2 Vitest assertions for the contract. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -165,7 +165,10 @@ const mobileVisible = computed<boolean>({
|
||||
}"
|
||||
>
|
||||
<SidebarHeader />
|
||||
<SidebarNav :collapsed="false" />
|
||||
<SidebarNav
|
||||
:collapsed="false"
|
||||
:grow="false"
|
||||
/>
|
||||
<WorkspaceSwitcher :collapsed="false" />
|
||||
</Drawer>
|
||||
</template>
|
||||
|
||||
@@ -22,9 +22,21 @@ import type { RouteLocationRaw } from 'unplugin-vue-router'
|
||||
import Icon from '@/components/Icon.vue'
|
||||
import { APP_NAVIGATION, type NavItem } from '@/config/navigation'
|
||||
|
||||
defineProps<{
|
||||
withDefaults(defineProps<{
|
||||
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()
|
||||
|
||||
@@ -52,7 +64,10 @@ function itemTo(item: NavItem): RouteLocationRaw {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="flex-1 min-h-0 overflow-y-auto py-3.5 px-2.5 [scrollbar-width:thin]">
|
||||
<nav
|
||||
class="min-h-0 overflow-y-auto py-3.5 px-2.5 [scrollbar-width:thin]"
|
||||
:class="grow ? 'flex-1' : 'flex-initial'"
|
||||
>
|
||||
<template
|
||||
v-for="topItem in APP_NAVIGATION"
|
||||
:key="topItem.key"
|
||||
|
||||
@@ -66,8 +66,8 @@ const globalStubs = {
|
||||
SidebarHeader: { name: 'SidebarHeader', template: '<div class="sidebar-header-stub" />' },
|
||||
SidebarNav: {
|
||||
name: 'SidebarNav',
|
||||
props: ['collapsed'],
|
||||
template: '<div class="sidebar-nav-stub" :data-collapsed="collapsed" />',
|
||||
props: ['collapsed', 'grow'],
|
||||
template: '<div class="sidebar-nav-stub" :data-collapsed="collapsed" :data-grow="grow" />',
|
||||
},
|
||||
WorkspaceSwitcher: {
|
||||
name: 'WorkspaceSwitcher',
|
||||
@@ -203,6 +203,35 @@ describe('AppSidebar', () => {
|
||||
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 () => {
|
||||
mockIsMobileRef.value = true
|
||||
|
||||
const wrapper = mountSidebar()
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
// grow=false → nav takes natural height, no flex-1 fill → no void above a
|
||||
// bottom-pinned WorkspaceSwitcher (they group together at the top instead).
|
||||
const navInDrawer = wrapper.find('.drawer-stub').find('.sidebar-nav-stub')
|
||||
|
||||
expect(navInDrawer.attributes('data-grow')).toBe('false')
|
||||
})
|
||||
|
||||
it('desktop: SidebarNav is allowed to grow (WorkspaceSwitcher stays bottom-anchored)', async () => {
|
||||
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')
|
||||
})
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Desktop <aside> width class based on sidebarCollapsed
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user