From 4680bd786de4f357a0d698139471f8879ad4b81e Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 28 Aug 2026 16:08:35 -0700 Subject: [PATCH] fix(ui): prevent competing sidebar tooltips --- .../sidebar-footer/sidebar-footer.test.tsx | 24 +++++++++++++++++-- .../sidebar-footer/sidebar-footer.tsx | 2 +- .../workspace-header.test.tsx | 5 ++++ .../workspace-header/workspace-header.tsx | 2 -- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.test.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.test.tsx index 662c67920dc..58bc39d2a15 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.test.tsx @@ -51,7 +51,10 @@ import { SidebarFooter } from '@/app/workspace/[workspaceId]/w/components/sideba let container: HTMLDivElement let root: Root -async function renderFooter(initialState: Record) { +async function renderFooter( + initialState: Record, + overrides: Partial[0]> = {} +) { desktopMocks.getState.mockResolvedValue(initialState) await act(async () => { root.render( @@ -64,6 +67,7 @@ async function renderFooter(initialState: Record) { onOpenDocs={() => {}} onJoinSlack={() => {}} onContactSupport={() => {}} + {...overrides} /> ) }) @@ -123,7 +127,23 @@ afterEach(() => { container.remove() }) -describe('SidebarFooter desktop update affordance', () => { +describe('SidebarFooter', () => { + it('keeps the overflow tooltip disabled while the collapsed tooltip still owns the trigger', async () => { + await renderFooter({ status: 'idle' }, { isCollapsed: false, showCollapsedTooltips: true }) + const label = profileTrigger().querySelector('[data-overflow-text]') + if (!label) throw new Error('Profile label was not rendered') + Object.defineProperties(label, { + clientWidth: { configurable: true, value: 40 }, + scrollWidth: { configurable: true, value: 80 }, + }) + + act(() => { + label.dispatchEvent(new MouseEvent('pointerover', { bubbles: true })) + }) + + expect(document.querySelector('[data-native-surface-overlay]')).toBeNull() + }) + it('renders profile settings destinations with native link semantics', async () => { await renderFooter({ status: 'idle' }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx index e12d80206aa..fc4c8902929 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx @@ -255,7 +255,7 @@ export function SidebarFooter({ ) : ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.test.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.test.tsx index c420e8910ab..801d13ca3e9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.test.tsx @@ -167,6 +167,7 @@ describe('WorkspaceHeader workspace switcher highlight', () => { const switcher = container.querySelector('button[aria-label="Switch workspace"]') expect(switcher).toBeDisabled() + expect(switcher).not.toHaveAttribute('title') expect(switcher).toHaveTextContent('Brightwave') expect(switcher).toHaveTextContent('B') expect(switcher?.querySelector('.animate-pulse')).toBeNull() @@ -175,6 +176,10 @@ describe('WorkspaceHeader workspace switcher highlight', () => { it('leaves Enter unarmed until a cursor is on screen', () => { render() + expect(container.querySelector('button[aria-label="Switch workspace"]')).not.toHaveAttribute( + 'title' + ) + const search = document.querySelector('input[placeholder="Search workspaces..."]') act(() => { search?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx index f56d901ab8b..5513a68476c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx @@ -519,7 +519,6 @@ function WorkspaceHeaderImpl({ type='button' aria-label='Switch workspace' className={cn(chipVariants(), 'min-w-0 max-w-full')} - title={activeWorkspace?.name} onContextMenu={(e) => { if (activeWorkspaceFull) { handleContextMenu(e, activeWorkspaceFull) @@ -871,7 +870,6 @@ function WorkspaceHeaderImpl({ shifts when the workspace resolves. Chips carry no margin, so neither does this. */ className={cn(chipGeometryClass, isCollapsed ? 'flex' : 'inline-flex min-w-0 max-w-full')} - title={activeWorkspace?.name} disabled > {activeWorkspaceFull?.logoUrl ? (