fix(tabs): scroll file preview tabs into view when selected

The TabBar scroll-into-view effect only triggered for AI tabs. Added
activeFileTabId to the useEffect dependencies so file preview tabs
are also scrolled into view when opened.
This commit is contained in:
Pedram Amini
2026-02-02 19:21:47 -06:00
parent fc3872ea73
commit 616c041626
2 changed files with 69 additions and 3 deletions

View File

@@ -1527,6 +1527,70 @@ describe('TabBar', () => {
rafSpy.mockRestore();
});
it('scrolls to center file tab when activeFileTabId changes', async () => {
// Mock requestAnimationFrame
const rafSpy = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
cb(0);
return 0;
});
const scrollToSpy = vi.fn();
const tabs = [createTab({ id: 'tab-1', name: 'Tab 1' })];
const fileTab: FilePreviewTab = {
id: 'file-1',
path: '/path/to/file.ts',
name: 'file',
extension: '.ts',
};
const unifiedTabs = [
{ id: 'tab-1', type: 'ai' as const, data: tabs[0] },
{ id: 'file-1', type: 'file' as const, data: fileTab },
];
const { rerender, container } = render(
<TabBar
tabs={tabs}
activeTabId="tab-1"
theme={mockTheme}
onTabSelect={mockOnTabSelect}
onTabClose={mockOnTabClose}
onNewTab={mockOnNewTab}
unifiedTabs={unifiedTabs}
activeFileTabId={null}
onFileTabSelect={vi.fn()}
onFileTabClose={vi.fn()}
/>
);
// Mock scrollTo on the container
const tabBarContainer = container.firstChild as HTMLElement;
tabBarContainer.scrollTo = scrollToSpy;
// Clear initial calls
scrollToSpy.mockClear();
// Select the file tab - this should trigger scroll to file tab
rerender(
<TabBar
tabs={tabs}
activeTabId="tab-1"
theme={mockTheme}
onTabSelect={mockOnTabSelect}
onTabClose={mockOnTabClose}
onNewTab={mockOnNewTab}
unifiedTabs={unifiedTabs}
activeFileTabId="file-1"
onFileTabSelect={vi.fn()}
onFileTabClose={vi.fn()}
/>
);
// scrollTo should have been called when file tab was selected
expect(scrollToSpy).toHaveBeenCalled();
rafSpy.mockRestore();
});
});
describe('styling', () => {

View File

@@ -1598,12 +1598,14 @@ function TabBarInner({
const tabRefs = useRef<Map<string, HTMLDivElement>>(new Map());
const [isOverflowing, setIsOverflowing] = useState(false);
// Center the active tab in the scrollable area when activeTabId changes or filter is toggled
// Center the active tab in the scrollable area when activeTabId or activeFileTabId changes, or filter is toggled
useEffect(() => {
requestAnimationFrame(() => {
const container = tabBarRef.current;
// When a file tab is active, scroll to it; otherwise scroll to the active AI tab
const targetTabId = activeFileTabId || activeTabId;
const tabElement = container?.querySelector(
`[data-tab-id="${activeTabId}"]`
`[data-tab-id="${targetTabId}"]`
) as HTMLElement | null;
if (container && tabElement) {
// Calculate scroll position to center the tab
@@ -1612,7 +1614,7 @@ function TabBarInner({
container.scrollTo({ left: scrollLeft, behavior: 'smooth' });
}
});
}, [activeTabId, showUnreadOnly]);
}, [activeTabId, activeFileTabId, showUnreadOnly]);
// Can always close tabs - closing the last one creates a fresh new tab
const canClose = true;