diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.test.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.test.tsx
new file mode 100644
index 00000000000..79d443b8f87
--- /dev/null
+++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.test.tsx
@@ -0,0 +1,103 @@
+import { QueryClient } from '@tanstack/react-query'
+import { screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import { LOCAL_STORAGE_KEYS } from 'common'
+import mockRouter from 'next-router-mock'
+import type { ReactNode } from 'react'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { ExplorerLayout } from './ExplorerLayout'
+import { createTabsState, TabsStateContext } from '@/state/tabs'
+import { customRender } from '@/tests/lib/custom-render'
+
+vi.mock('next/navigation', () => ({
+ usePathname: () => mockRouter.asPath,
+}))
+
+vi.mock('../ProjectLayout', () => ({
+ ProjectLayoutWithAuth: ({ children }: { children: ReactNode }) => <>{children}>,
+}))
+
+vi.mock('@/components/interfaces/Account/Preferences/useExplorerPreferences', () => ({
+ useExplorerPreferences: () => ({ home: 'home', hasCompletedOnboarding: true, isReady: true }),
+}))
+
+vi.mock('@/components/interfaces/Explorer/ExplorerQueryTabCoordinator', () => ({
+ ExplorerQueryTabCoordinator: () => null,
+}))
+
+vi.mock('@/components/interfaces/Explorer/ExplorerNotebookTabCoordinator', () => ({
+ ExplorerNotebookTabCoordinator: () => null,
+}))
+
+vi.mock('@/components/interfaces/Explorer/hooks', () => ({
+ useCreateChat: () => ({ createChat: vi.fn() }),
+ useCreateNotebook: () => ({ createNotebook: vi.fn() }),
+ useCreateQuery: () => ({ createQuery: vi.fn() }),
+}))
+
+describe('Explorer Home tab navigation', () => {
+ beforeEach(() => {
+ vi.restoreAllMocks()
+ localStorage.clear()
+ mockRouter.setCurrentUrl({
+ pathname: '/project/[ref]/explorer/query/[id]',
+ query: { ref: 'default', id: 'query-1' },
+ })
+ })
+
+ it.each(['mouse', 'keyboard'] as const)(
+ 'clears the saved Explorer destination before navigating Home with the %s',
+ async (input) => {
+ const user = userEvent.setup()
+ const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false, staleTime: Infinity } },
+ })
+ const historyKey = ['localStorage', LOCAL_STORAGE_KEYS.DASHBOARD_HISTORY('default')]
+ const otherHistory = { editor: 'table-1', sql: 'snippet-1' }
+ queryClient.setQueryData(historyKey, {
+ ...otherHistory,
+ explorer: { type: 'query', id: 'query-1' },
+ })
+
+ const tabs = createTabsState('default')
+ tabs.addTab({
+ id: 'query-query-1',
+ type: 'query',
+ label: 'My query',
+ metadata: { queryId: 'query-1' },
+ isPreview: false,
+ })
+ let historyAtNavigation: unknown
+ const push = vi.spyOn(mockRouter, 'push')
+ push.mockImplementationOnce(async () => {
+ historyAtNavigation = queryClient.getQueryData(historyKey)
+ return true
+ })
+
+ customRender(
+
+ Explorer content
+ ,
+ { queryClient }
+ )
+
+ if (input === 'mouse') {
+ await user.click(screen.getByRole('tab', { name: 'Open Explorer home' }))
+ } else {
+ await user.tab()
+ expect(screen.getByRole('tab', { name: 'My query' })).toHaveFocus()
+ await user.keyboard('{ArrowLeft}')
+ }
+
+ expect(push).toHaveBeenCalledWith('/project/default/explorer')
+ expect(historyAtNavigation).toEqual(otherHistory)
+ expect(queryClient.getQueryData(historyKey)).toEqual(otherHistory)
+ expect(screen.getByRole('tab', { name: 'Open Explorer home' })).toHaveAttribute(
+ 'aria-selected',
+ 'true'
+ )
+ expect(tabs.tabsMap['query-query-1']).toBeDefined()
+ }
+ )
+})
diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx
index da87c153c7f..f73170c15a1 100644
--- a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx
+++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.tsx
@@ -28,6 +28,7 @@ import {
useCreateNotebook,
useCreateQuery,
} from '@/components/interfaces/Explorer/hooks'
+import { useDashboardHistory } from '@/hooks/misc/useDashboardHistory'
import { useIsTemporarySqlEditorVisit } from '@/hooks/misc/useIsTemporarySqlEditorVisit'
import { useTrack } from '@/lib/telemetry/track'
import {
@@ -46,6 +47,7 @@ export interface ExplorerLayoutProps extends ComponentProps {
const { ref } = useParams()
const tabs = useTabsStateSnapshot()
+ const { setLastVisitedExplorerTab } = useDashboardHistory()
const { home, hasCompletedOnboarding, isReady } = useExplorerPreferences()
const shouldShowHomeTab = isReady && (!hasCompletedOnboarding || home === 'home')
@@ -68,6 +70,10 @@ export const ExplorerLayout = ({ browserTitle, children, title }: ExplorerLayout
entity: browserTitle?.entity ?? activeTabLabel,
}
+ const handleTabChange = (id: string) => {
+ if (id === EXPLORER_HOME_TAB_ID) setLastVisitedExplorerTab(undefined)
+ }
+
return (
: undefined}
newTabButton={}
+ onTabChange={handleTabChange}
/>
{children}
diff --git a/apps/studio/components/layouts/Tabs/Tabs.tsx b/apps/studio/components/layouts/Tabs/Tabs.tsx
index 05022bc102c..36839849c4c 100644
--- a/apps/studio/components/layouts/Tabs/Tabs.tsx
+++ b/apps/studio/components/layouts/Tabs/Tabs.tsx
@@ -41,6 +41,7 @@ interface EditorTabsProps {
customTabs?: ReactNode
newTabButton?: ReactNode
isCollapseButtonHidden?: boolean
+ onTabChange?: (id: string) => void
}
// [Joshen] Will be adjusting this component to support Explorer
@@ -49,6 +50,7 @@ export const EditorTabs = ({
customTabs,
newTabButton,
isCollapseButtonHidden,
+ onTabChange,
}: EditorTabsProps) => {
const { ref } = useParams()
const router = useRouter()
@@ -168,6 +170,7 @@ export const EditorTabs = ({
}
const handleTabChange = (id: string) => {
+ onTabChange?.(id)
tabs.handleTabNavigation(id, router)
}