From b308d3d13aeb86b57d0c9844df3a5e9d6df7a3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Lopes?= Date: Mon, 31 Aug 2026 15:45:30 -0300 Subject: [PATCH] fix(sidebar): type the trigger icon and skip an empty Vue slot Derive panelLeft as IconSource so Button accepts it. In Vue, only register the default slot when content is provided so mini density still shows the icon. --- .../src/Components/Sidebar/SidebarTrigger.tsx | 13 +++++++--- .../Sidebar/__tests__/Sidebar.test.tsx | 24 ++++++++++++++++++ .../Sidebar/hooks/useSidebarTrigger.ts | 10 ++++++++ .../src/Components/Sidebar/SidebarTrigger.vue | 7 ++++-- .../Sidebar/__tests__/Sidebar.test.ts | 25 +++++++++++++++++++ .../Sidebar/composables/useSidebarTrigger.ts | 10 ++++++++ 6 files changed, 84 insertions(+), 5 deletions(-) diff --git a/packages/react/src/Components/Sidebar/SidebarTrigger.tsx b/packages/react/src/Components/Sidebar/SidebarTrigger.tsx index d10f48ea..ea6ee9d0 100644 --- a/packages/react/src/Components/Sidebar/SidebarTrigger.tsx +++ b/packages/react/src/Components/Sidebar/SidebarTrigger.tsx @@ -4,12 +4,20 @@ import { useSidebarTrigger } from "@/Components/Sidebar/hooks/useSidebarTrigger" import type { SidebarTriggerProps } from "@/Components/Sidebar/sidebar.types"; function SidebarTrigger(props: SidebarTriggerProps) { - const { side, panelId, children, expanded, handleClick, rootInheritedAttrs } = - useSidebarTrigger(props); + const { + icon, + side, + panelId, + children, + expanded, + handleClick, + rootInheritedAttrs, + } = useSidebarTrigger(props); return ( diff --git a/packages/vue/src/Components/Sidebar/__tests__/Sidebar.test.ts b/packages/vue/src/Components/Sidebar/__tests__/Sidebar.test.ts index 634e6110..aa753061 100644 --- a/packages/vue/src/Components/Sidebar/__tests__/Sidebar.test.ts +++ b/packages/vue/src/Components/Sidebar/__tests__/Sidebar.test.ts @@ -53,6 +53,31 @@ test("it should render the sidebar aside and main inset", () => { expect(wrapper.text()).toContain("Main"); }); +test("it should render the default toggle icon on the trigger", () => { + const wrapper = mount(AppShell); + const button = wrapper.get("button[aria-label='Toggle sidebar']"); + + expect(button.find("svg").exists()).toBe(true); +}); + +test("it should replace the default toggle icon when a slot is provided", () => { + const wrapper = mount(SidebarProvider, { + slots: { + default: () => [ + h(Sidebar, null, { default: () => "Nav" }), + h(SidebarInset, null, { + default: () => h(SidebarTrigger, null, { default: () => "Menu" }), + }), + ], + }, + }); + + const button = wrapper.get("button[aria-label='Toggle sidebar']"); + + expect(button.text()).toContain("Menu"); + expect(button.find("svg").exists()).toBe(false); +}); + test("it should default to expanded desktop state", () => { const wrapper = mount(AppShell); diff --git a/packages/vue/src/Components/Sidebar/composables/useSidebarTrigger.ts b/packages/vue/src/Components/Sidebar/composables/useSidebarTrigger.ts index a4732a84..a1f353a0 100644 --- a/packages/vue/src/Components/Sidebar/composables/useSidebarTrigger.ts +++ b/packages/vue/src/Components/Sidebar/composables/useSidebarTrigger.ts @@ -3,6 +3,7 @@ import { omit } from "es-toolkit/compat"; import { computed, useAttrs, type useSlots } from "vue"; // ** Local Imports +import type { IconSource } from "@/Adapters/Icon"; import { useSidebar } from "@/Components/Sidebar/composables/useSidebar"; import { hasNamedSlot } from "@/Utils"; @@ -14,6 +15,14 @@ export function useSidebarTrigger(slots: ReturnType) { return hasNamedSlot(slots, "default"); }); + const icon = computed((): undefined | IconSource => { + if (hasDefaultSlot.value) { + return undefined; + } + + return "panelLeft"; + }); + const expanded = computed(() => { return sidebar.value.isMobile ? sidebar.value.openMobile @@ -51,6 +60,7 @@ export function useSidebarTrigger(slots: ReturnType) { }; return { + icon, expanded, iconClass, ariaLabel,