You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by en...@apache.org on 2026/08/28 13:00:41 UTC
(superset) 01/05: fix(dashboard): reconcile stale results tab in Chart Data modal
This is an automated email from the ASF dual-hosted git repository.
EnxDev pushed a commit to branch enxdev/fix/explore-stale-results-tab
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 33e252cd8ee9968e2c33bff2b3480f2c32d6d444
Author: Enzo Martellucci <en...@gmail.com>
AuthorDate: Tue Aug 25 12:11:02 2026 +0200
fix(dashboard): reconcile stale results tab in Chart Data modal
Address PR #43454 review feedback: reuse DataTablesPane's stale-tab
fallback so ResultsPaneOnDashboard doesn't render blank when a mixed
chart's active results tab disappears, and drop CSS rules now
redundant with the fullHeight prop.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../components/DataTablesPane/DataTablesPane.tsx | 17 +------
.../components/ResultsPaneOnDashboard.tsx | 22 +++++----
.../test/ResultsPaneOnDashboard.test.tsx | 56 ++++++++++++++++++++++
.../test/getStaleResultsTabFallback.test.ts | 2 +-
.../src/explore/components/DataTablesPane/utils.ts | 35 ++++++++++++++
5 files changed, 106 insertions(+), 26 deletions(-)
diff --git a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx
index cb8878e3bf9..7a6e3d649cd 100644
--- a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx
+++ b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.tsx
@@ -29,22 +29,7 @@ import {
} from 'src/utils/localStorageHelpers';
import { SamplesPane, useResultsPane } from './components';
import { DataTablesPaneProps, ResultTypes } from './types';
-
-/**
- * A mixed chart can be reconfigured to return fewer result panes than before
- * (e.g. dropping a query), which removes the corresponding results tab. If the
- * selected tab was one of those, the active key goes stale and the data panel
- * renders blank until the user reselects a valid tab. Returns the first
- * results tab to fall back to in that case, otherwise undefined.
- */
-export const getStaleResultsTabFallback = (
- activeTabKey: string,
- resultsTabKeys: string[],
-): string | undefined =>
- activeTabKey.startsWith(ResultTypes.Results) &&
- !resultsTabKeys.includes(activeTabKey)
- ? ResultTypes.Results
- : undefined;
+import { getStaleResultsTabFallback } from './utils';
const StyledDiv = styled.div`
${() => `
diff --git a/superset-frontend/src/explore/components/DataTablesPane/components/ResultsPaneOnDashboard.tsx b/superset-frontend/src/explore/components/DataTablesPane/components/ResultsPaneOnDashboard.tsx
index 4485ca79208..2cada3a2429 100644
--- a/superset-frontend/src/explore/components/DataTablesPane/components/ResultsPaneOnDashboard.tsx
+++ b/superset-frontend/src/explore/components/DataTablesPane/components/ResultsPaneOnDashboard.tsx
@@ -20,22 +20,15 @@ import { t } from '@apache-superset/core/translation';
import { styled } from '@apache-superset/core/theme';
import Tabs from '@superset-ui/core/components/Tabs';
import { ResultTypes, ResultsPaneProps } from '../types';
+import { getStaleResultsTabFallback } from '../utils';
import { useResultsPane } from './useResultsPane';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
const Wrapper = styled.div`
display: flex;
flex-direction: column;
height: 100%;
- .ant-tabs {
- height: 100%;
- }
-
- .ant-tabs-body {
- height: 100%;
- }
-
.ant-tabs-content {
display: flex;
flex-direction: column;
@@ -86,6 +79,17 @@ export const ResultsPaneOnDashboard = ({
};
});
+ const resultsTabFallback = getStaleResultsTabFallback(
+ activeTabKey,
+ items.map(({ key }) => key),
+ );
+
+ useEffect(() => {
+ if (resultsTabFallback) {
+ setActiveTabKey(resultsTabFallback);
+ }
+ }, [resultsTabFallback]);
+
return (
<Wrapper>
<Tabs activeKey={activeTabKey} onChange={setActiveTabKey} items={items} />
diff --git a/superset-frontend/src/explore/components/DataTablesPane/test/ResultsPaneOnDashboard.test.tsx b/superset-frontend/src/explore/components/DataTablesPane/test/ResultsPaneOnDashboard.test.tsx
index 145ab3e7896..16a7c2ff0b7 100644
--- a/superset-frontend/src/explore/components/DataTablesPane/test/ResultsPaneOnDashboard.test.tsx
+++ b/superset-frontend/src/explore/components/DataTablesPane/test/ResultsPaneOnDashboard.test.tsx
@@ -20,14 +20,34 @@ import fetchMock from 'fetch-mock';
import {
screen,
render,
+ act,
waitForElementToBeRemoved,
waitFor,
} from 'spec/helpers/testing-library';
import { ChartMetadata, ChartPlugin, VizType } from '@superset-ui/core';
import { setupAGGridModules } from '@superset-ui/core/components/ThemedAgGridReact';
+import Tabs from '@superset-ui/core/components/Tabs';
import { ResultsPaneOnDashboard } from '../components';
+import { useResultsPane } from '../components/useResultsPane';
import { createResultsPaneOnDashboardProps } from './fixture';
+// `fullHeight`'s CSS isn't testable under jsdom (no `importSource` for the
+// `css` prop in jest's babel config), so spy on call args instead.
+jest.mock('@superset-ui/core/components/Tabs', () => {
+ const actual = jest.requireActual('@superset-ui/core/components/Tabs');
+ return { __esModule: true, ...actual, default: jest.fn(actual.default) };
+});
+
+// Wraps the real hook; only overridden below to avoid mounting a second
+// real AG Grid instance, which jsdom doesn't support.
+jest.mock('../components/useResultsPane', () => {
+ const actual = jest.requireActual('../components/useResultsPane');
+ return {
+ __esModule: true,
+ useResultsPane: jest.fn(actual.useResultsPane),
+ };
+});
+
beforeAll(() => {
setupAGGridModules();
});
@@ -106,6 +126,10 @@ describe('ResultsPaneOnDashboard', () => {
expect(
await findByText('No results were returned for this query'),
).toBeVisible();
+ expect(Tabs).toHaveBeenCalledWith(
+ expect.objectContaining({ fullHeight: true }),
+ expect.anything(),
+ );
});
test('render errorMessage', async () => {
@@ -219,4 +243,36 @@ describe('ResultsPaneOnDashboard', () => {
expect(tab2).toBeVisible();
expect(tab3).toBeNull();
});
+
+ test('falls back to the first results tab when the active one disappears', async () => {
+ const mockedUseResultsPane = useResultsPane as jest.Mock;
+ mockedUseResultsPane.mockReturnValue([<div key="a" />, <div key="b" />]);
+
+ const props = createResultsPaneOnDashboardProps({ sliceId: 999 });
+ const { rerender } = render(<ResultsPaneOnDashboard {...props} />, {
+ useRedux: true,
+ });
+
+ const latestTabsProps = () => {
+ const { calls } = (Tabs as unknown as jest.Mock).mock;
+ return calls[calls.length - 1][0];
+ };
+ expect(latestTabsProps().items.map((i: { key: string }) => i.key)).toEqual([
+ 'results',
+ 'results 2',
+ ]);
+
+ act(() => {
+ latestTabsProps().onChange('results 2');
+ });
+ expect(latestTabsProps().activeKey).toBe('results 2');
+
+ // A mixed chart dropped from two query results to one, removing "results 2"
+ mockedUseResultsPane.mockReturnValue([<div key="a" />]);
+ rerender(<ResultsPaneOnDashboard {...props} />);
+
+ await waitFor(() => {
+ expect(latestTabsProps().activeKey).toBe('results');
+ });
+ });
});
diff --git a/superset-frontend/src/explore/components/DataTablesPane/test/getStaleResultsTabFallback.test.ts b/superset-frontend/src/explore/components/DataTablesPane/test/getStaleResultsTabFallback.test.ts
index d387babf4ee..2b6d8df6aee 100644
--- a/superset-frontend/src/explore/components/DataTablesPane/test/getStaleResultsTabFallback.test.ts
+++ b/superset-frontend/src/explore/components/DataTablesPane/test/getStaleResultsTabFallback.test.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { getStaleResultsTabFallback } from '../DataTablesPane';
+import { getStaleResultsTabFallback } from '../utils';
import { ResultTypes } from '../types';
test('keeps the active tab when it still exists', () => {
diff --git a/superset-frontend/src/explore/components/DataTablesPane/utils.ts b/superset-frontend/src/explore/components/DataTablesPane/utils.ts
new file mode 100644
index 00000000000..b6e58649382
--- /dev/null
+++ b/superset-frontend/src/explore/components/DataTablesPane/utils.ts
@@ -0,0 +1,35 @@
+/**
+ * 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 { ResultTypes } from './types';
+
+/**
+ * A mixed chart can be reconfigured to return fewer result panes than before
+ * (e.g. dropping a query), which removes the corresponding results tab. If the
+ * selected tab was one of those, the active key goes stale and the data panel
+ * renders blank until the user reselects a valid tab. Returns the first
+ * results tab to fall back to in that case, otherwise undefined.
+ */
+export const getStaleResultsTabFallback = (
+ activeTabKey: string,
+ resultsTabKeys: string[],
+): string | undefined =>
+ activeTabKey.startsWith(ResultTypes.Results) &&
+ !resultsTabKeys.includes(activeTabKey)
+ ? ResultTypes.Results
+ : undefined;