From 0a3bd87b420c784656007632b6355771a0ffb87a Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Tue, 20 Jan 2026 09:54:27 -0300 Subject: [PATCH 1/7] fix(EchartsTimeseries): restore tooltip visibility after drill menu closes --- .../src/Timeseries/EchartsTimeseries.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx index b14b7fb6ba61..e36658b4f0cd 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx @@ -259,6 +259,18 @@ export default function EchartsTimeseries({ ? getCrossFilterDataMask(seriesName) : undefined, }); + + // Restore tooltip visibility after the drill menu closes + const restoreTooltip = () => { + const instance = echartRef.current?.getEchartInstance?.(); + if (instance) { + instance.setOption({ tooltip: { show: true } }, false); + } + }; + window.addEventListener('mousedown', restoreTooltip, { + once: true, + capture: true, + }); } }, }; From eb10d60f1b2cdee8a9c26b360449c0b14aabf2f2 Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Wed, 21 Jan 2026 19:51:18 -0300 Subject: [PATCH 2/7] fix(EchartsTimeseries): remove redundant tooltip restoration logic after drill menu closes --- .../src/Timeseries/EchartsTimeseries.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx index e36658b4f0cd..62a53a95d801 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx @@ -66,6 +66,7 @@ export default function EchartsTimeseries({ const clickTimer = useRef>(); const extraControlRef = useRef(null); const [extraControlHeight, setExtraControlHeight] = useState(0); + useEffect(() => { const element = extraControlRef.current; if (!element) { @@ -259,18 +260,6 @@ export default function EchartsTimeseries({ ? getCrossFilterDataMask(seriesName) : undefined, }); - - // Restore tooltip visibility after the drill menu closes - const restoreTooltip = () => { - const instance = echartRef.current?.getEchartInstance?.(); - if (instance) { - instance.setOption({ tooltip: { show: true } }, false); - } - }; - window.addEventListener('mousedown', restoreTooltip, { - once: true, - capture: true, - }); } }, }; From b828cc390611e3d7afdc8432635a6123c0d6c271 Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Wed, 21 Jan 2026 19:51:44 -0300 Subject: [PATCH 3/7] fix(ChartContextMenu): call onClose when context menu is closed --- .../src/components/Chart/ChartContextMenu/ChartContextMenu.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx index 945657278e67..56b295b0cf78 100644 --- a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx +++ b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx @@ -423,6 +423,9 @@ const ChartContextMenu = ( trigger={['click']} onOpenChange={value => { setVisible(value); + if (!value) { + onClose(); + } }} open={visible} > From 2479846f8ebb750cd2ba37c0a83e695865c24ea4 Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Wed, 21 Jan 2026 19:57:40 -0300 Subject: [PATCH 4/7] fix(EchartsTimeseries): remove unnecessary blank line --- .../plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx index 62a53a95d801..b14b7fb6ba61 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx @@ -66,7 +66,6 @@ export default function EchartsTimeseries({ const clickTimer = useRef>(); const extraControlRef = useRef(null); const [extraControlHeight, setExtraControlHeight] = useState(0); - useEffect(() => { const element = extraControlRef.current; if (!element) { From 86103ba0d93523687923a54dbc216899c9804060 Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Fri, 23 Jan 2026 17:28:31 -0300 Subject: [PATCH 5/7] test(ChartContextMenu): add tests for context menu close behavior --- .../ChartContextMenu.test.tsx | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx diff --git a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx new file mode 100644 index 000000000000..b6f045091edf --- /dev/null +++ b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx @@ -0,0 +1,142 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { useRef } from 'react'; +import { FeatureFlag, VizType } from '@superset-ui/core'; +import { render, screen, waitFor } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import mockState from 'spec/fixtures/mockState'; +import { sliceId } from 'spec/fixtures/mockChartQueries'; +import { cachedSupersetGet } from 'src/utils/cachedSupersetGet'; +import ChartContextMenu, { + ChartContextMenuRef, + ContextMenuItem, +} from './ChartContextMenu'; + +jest.mock('src/utils/cachedSupersetGet'); + +const mockCachedSupersetGet = cachedSupersetGet as jest.MockedFunction< + typeof cachedSupersetGet +>; + +// @ts-ignore +global.featureFlags = { + [FeatureFlag.DrillToDetail]: true, + [FeatureFlag.DrillBy]: true, +}; + +const defaultFormData = { + datasource: '1__table', + viz_type: VizType.Pie, +}; + +const TestWrapper = ({ onClose = jest.fn() }: { onClose?: jest.Mock }) => { + const contextMenuRef = useRef(null); + + return ( + <> + + + + ); +}; + +const setup = (props: Parameters[0] = {}) => { + return render(, { + useRedux: true, + initialState: { + ...mockState, + user: { + ...mockState.user, + roles: { + Admin: [ + ['can_explore', 'Superset'], + ['can_samples', 'Datasource'], + ['can_write', 'ExploreFormDataRestApi'], + ['can_get_drill_info', 'Dataset'], + ], + }, + }, + }, + }); +}; + +beforeEach(() => { + mockCachedSupersetGet.mockClear(); + mockCachedSupersetGet.mockResolvedValue({ + response: {} as Response, + json: { + result: { + columns: [], + metrics: [], + }, + }, + }); +}); + +test('context menu calls onClose when user clicks outside to close it', async () => { + const onCloseMock = jest.fn(); + setup({ onClose: onCloseMock }); + + const openButton = screen.getByTestId('open-context-menu'); + userEvent.click(openButton); + + await waitFor(() => { + expect(screen.getByTestId('chart-context-menu')).toBeInTheDocument(); + }); + + expect(onCloseMock).not.toHaveBeenCalled(); + + userEvent.click(document.body); + + // Ensure onClose is called when dropdown closes + await waitFor(() => { + expect(onCloseMock).toHaveBeenCalled(); + }); +}); + +test('context menu calls onClose when user selects a menu item', async () => { + const onCloseMock = jest.fn(); + setup({ onClose: onCloseMock }); + + const openButton = screen.getByTestId('open-context-menu'); + userEvent.click(openButton); + + await waitFor(() => { + expect(screen.getByTestId('chart-context-menu')).toBeInTheDocument(); + }); + + const menuItem = screen.getByText('Drill to detail'); + userEvent.click(menuItem); + + await waitFor(() => { + expect(onCloseMock).toHaveBeenCalled(); + }); +}); From debe7dbe81a6afab00b3e2802bbd82d8e24b1751 Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Fri, 23 Jan 2026 17:33:49 -0300 Subject: [PATCH 6/7] fix(ChartContextMenu): restore tooltip visibility in tests after context menu interactions --- .../ChartContextMenu.test.tsx | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx index b6f045091edf..76d401964514 100644 --- a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx +++ b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { useRef } from 'react'; +import { useRef, useState } from 'react'; import { FeatureFlag, VizType } from '@superset-ui/core'; import { render, screen, waitFor } from 'spec/helpers/testing-library'; import userEvent from '@testing-library/user-event'; @@ -45,8 +45,13 @@ const defaultFormData = { viz_type: VizType.Pie, }; -const TestWrapper = ({ onClose = jest.fn() }: { onClose?: jest.Mock }) => { +const TestWrapper = () => { const contextMenuRef = useRef(null); + const [isTooltipVisible, setIsTooltipVisible] = useState(true); + + const handleClose = () => { + setIsTooltipVisible(true); + }; return ( <> @@ -56,20 +61,23 @@ const TestWrapper = ({ onClose = jest.fn() }: { onClose?: jest.Mock }) => { > Open Context Menu + {isTooltipVisible && ( +
Tooltip is visible
+ )} ); }; -const setup = (props: Parameters[0] = {}) => { - return render(, { +const setup = () => { + return render(, { useRedux: true, initialState: { ...mockState, @@ -101,9 +109,8 @@ beforeEach(() => { }); }); -test('context menu calls onClose when user clicks outside to close it', async () => { - const onCloseMock = jest.fn(); - setup({ onClose: onCloseMock }); +test('tooltip is restored when user clicks outside to close context menu', async () => { + setup(); const openButton = screen.getByTestId('open-context-menu'); userEvent.click(openButton); @@ -112,19 +119,17 @@ test('context menu calls onClose when user clicks outside to close it', async () expect(screen.getByTestId('chart-context-menu')).toBeInTheDocument(); }); - expect(onCloseMock).not.toHaveBeenCalled(); + expect(screen.getByTestId('tooltip-visible')).toBeInTheDocument(); userEvent.click(document.body); - // Ensure onClose is called when dropdown closes await waitFor(() => { - expect(onCloseMock).toHaveBeenCalled(); + expect(screen.getByTestId('tooltip-visible')).toBeInTheDocument(); }); }); -test('context menu calls onClose when user selects a menu item', async () => { - const onCloseMock = jest.fn(); - setup({ onClose: onCloseMock }); +test('tooltip is restored when user selects a menu item', async () => { + setup(); const openButton = screen.getByTestId('open-context-menu'); userEvent.click(openButton); @@ -137,6 +142,6 @@ test('context menu calls onClose when user selects a menu item', async () => { userEvent.click(menuItem); await waitFor(() => { - expect(onCloseMock).toHaveBeenCalled(); + expect(screen.getByTestId('tooltip-visible')).toBeInTheDocument(); }); }); From 5c31835697b5c9cc2c81e3f506309817c214e263 Mon Sep 17 00:00:00 2001 From: Vanessa Giannoni Date: Mon, 26 Jan 2026 16:21:06 -0300 Subject: [PATCH 7/7] fix(ChartContextMenu): cleanup feature flag setup after each tests --- .../ChartContextMenu/ChartContextMenu.test.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx index 76d401964514..797259dee271 100644 --- a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx +++ b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.test.tsx @@ -34,12 +34,6 @@ const mockCachedSupersetGet = cachedSupersetGet as jest.MockedFunction< typeof cachedSupersetGet >; -// @ts-ignore -global.featureFlags = { - [FeatureFlag.DrillToDetail]: true, - [FeatureFlag.DrillBy]: true, -}; - const defaultFormData = { datasource: '1__table', viz_type: VizType.Pie, @@ -97,6 +91,12 @@ const setup = () => { }; beforeEach(() => { + // @ts-ignore + global.featureFlags = { + [FeatureFlag.DrillToDetail]: true, + [FeatureFlag.DrillBy]: true, + }; + mockCachedSupersetGet.mockClear(); mockCachedSupersetGet.mockResolvedValue({ response: {} as Response, @@ -109,6 +109,11 @@ beforeEach(() => { }); }); +afterEach(() => { + // @ts-ignore + delete global.featureFlags; +}); + test('tooltip is restored when user clicks outside to close context menu', async () => { setup();