diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx index 6731ade3b21..9a48cbe1384 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx @@ -316,7 +316,6 @@ class DatasourceControl extends PureComponent { datasourceKey: `${datasource.id}__${datasource.type}`, sql: datasource.sql, }; - const defaultDatasourceMenuItems = []; if (this.props.isEditable && !isMissingDatasource) { defaultDatasourceMenuItems.push({ diff --git a/superset-frontend/src/hooks/useUnsavedChangesPrompt/index.ts b/superset-frontend/src/hooks/useUnsavedChangesPrompt/index.ts index b79523b72fc..3d912a0af07 100644 --- a/superset-frontend/src/hooks/useUnsavedChangesPrompt/index.ts +++ b/superset-frontend/src/hooks/useUnsavedChangesPrompt/index.ts @@ -20,6 +20,7 @@ import { getClientErrorObject, t } from '@superset-ui/core'; import { useEffect, useRef, useCallback, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { useBeforeUnload } from 'src/hooks/useBeforeUnload'; +import type { Location } from 'history'; type UseUnsavedChangesPromptProps = { hasUnsavedChanges: boolean; @@ -69,7 +70,13 @@ export const useUnsavedChangesPrompt = ({ }, [onSave]); const blockCallback = useCallback( - ({ pathname }: { pathname: string }) => { + ({ + pathname, + state, + }: { + pathname: Location['pathname']; + state: Location['state']; + }) => { if (manualSaveRef.current) { manualSaveRef.current = false; return undefined; @@ -77,7 +84,7 @@ export const useUnsavedChangesPrompt = ({ confirmNavigationRef.current = () => { unblockRef.current?.(); - history.push(pathname); + history.push(pathname, state); }; setShowModal(true); diff --git a/superset-frontend/src/hooks/useUnsavedChangesPrompt/useUnsavedChangesPrompt.test.tsx b/superset-frontend/src/hooks/useUnsavedChangesPrompt/useUnsavedChangesPrompt.test.tsx index 444b53197de..ccd259f22f5 100644 --- a/superset-frontend/src/hooks/useUnsavedChangesPrompt/useUnsavedChangesPrompt.test.tsx +++ b/superset-frontend/src/hooks/useUnsavedChangesPrompt/useUnsavedChangesPrompt.test.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, act } from '@testing-library/react-hooks'; import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; import { useUnsavedChangesPrompt } from '.'; @@ -124,3 +124,42 @@ test('should close modal when handleConfirmNavigation is called', () => { expect(result.current.showModal).toBe(false); }); + +test('should preserve pathname and state when confirming navigation', () => { + const onSave = jest.fn(); + const history = createMemoryHistory(); + const wrapper = ({ children }: any) => ( + {children} + ); + + const locationState = { fromDashboard: true, dashboardId: 123 }; + const pathname = '/another-page'; + + const { result } = renderHook( + () => useUnsavedChangesPrompt({ hasUnsavedChanges: true, onSave }), + { wrapper }, + ); + + const pushSpy = jest.spyOn(history, 'push'); + + // Simulate a blocked navigation (the hook sets up history.block internally) + act(() => { + history.push(pathname, locationState); + }); + + // Modal should now be visible + expect(result.current.showModal).toBe(true); + + // Confirm navigation + act(() => { + result.current.handleConfirmNavigation(); + }); + + // Modal should close + expect(result.current.showModal).toBe(false); + + // Verify correct call with pathname and state preserved + expect(pushSpy).toHaveBeenCalledWith(pathname, locationState); + + pushSpy.mockRestore(); +});