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/04/25 22:21:19 UTC

(superset) branch master updated: fix: delete Chart under "All" in home page doesn't refresh after dele… (#39471)

This is an automated email from the ASF dual-hosted git repository.

EnxDev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 30bd490b848 fix: delete Chart under "All" in home page doesn't refresh after dele… (#39471)
30bd490b848 is described below

commit 30bd490b8484c67d50b8076f4ec216c49167144f
Author: Haoqian Zhang <ha...@gmail.com>
AuthorDate: Sat Apr 25 18:21:12 2026 -0400

    fix: delete Chart under "All" in home page doesn't refresh after dele… (#39471)
---
 .../src/features/charts/ChartCard.tsx              |  7 ++-
 .../src/features/home/ChartTable.test.tsx          | 72 ++++++++++++++++++++--
 superset-frontend/src/features/home/ChartTable.tsx | 26 ++++----
 superset-frontend/src/views/CRUD/utils.tsx         |  2 +
 4 files changed, 90 insertions(+), 17 deletions(-)

diff --git a/superset-frontend/src/features/charts/ChartCard.tsx b/superset-frontend/src/features/charts/ChartCard.tsx
index 3d48207aa2d..47562c84450 100644
--- a/superset-frontend/src/features/charts/ChartCard.tsx
+++ b/superset-frontend/src/features/charts/ChartCard.tsx
@@ -34,6 +34,8 @@ import Chart from 'src/types/Chart';
 import { FacePile } from 'src/components';
 import { handleChartDelete, CardStyles } from 'src/views/CRUD/utils';
 import { assetUrl } from 'src/utils/assetUrl';
+import type { ListViewFetchDataConfig as FetchDataConfig } from 'src/components';
+import { TableTab } from 'src/views/CRUD/types';
 
 interface ChartCardProps {
   chart: Chart;
@@ -42,7 +44,7 @@ interface ChartCardProps {
   bulkSelectEnabled: boolean;
   addDangerToast: (msg: string) => void;
   addSuccessToast: (msg: string) => void;
-  refreshData: () => void;
+  refreshData: (config?: FetchDataConfig | null) => void;
   loading?: boolean;
   saveFavoriteStatus: (id: number, isStarred: boolean) => void;
   favoriteStatus: boolean;
@@ -50,6 +52,7 @@ interface ChartCardProps {
   userId?: string | number;
   showThumbnails?: boolean;
   handleBulkChartExport: (chartsToExport: Chart[]) => void;
+  getData?: (tab: TableTab) => void;
 }
 
 export default function ChartCard({
@@ -67,6 +70,7 @@ export default function ChartCard({
   chartFilter,
   userId,
   handleBulkChartExport,
+  getData,
 }: ChartCardProps) {
   const history = useHistory();
   const canEdit = hasPerm('can_write');
@@ -136,6 +140,7 @@ export default function ChartCard({
               refreshData,
               chartFilter,
               userId,
+              getData,
             )
           }
         >
diff --git a/superset-frontend/src/features/home/ChartTable.test.tsx b/superset-frontend/src/features/home/ChartTable.test.tsx
index 4425fe696a7..22e4cf9517b 100644
--- a/superset-frontend/src/features/home/ChartTable.test.tsx
+++ b/superset-frontend/src/features/home/ChartTable.test.tsx
@@ -26,6 +26,7 @@ import { VizType } from '@superset-ui/core';
 import fetchMock from 'fetch-mock';
 import { act } from 'react-dom/test-utils';
 import handleResourceExport from 'src/utils/export';
+import { LocalStorageKeys } from 'src/utils/localStorageHelpers';
 import ChartTable from './ChartTable';
 
 // Mock the export module
@@ -53,12 +54,16 @@ const mockCharts = Array.from({ length: 3 }).map((_, i) => ({
   thumbnail_url: '',
 }));
 
-fetchMock.get(chartsEndpoint, {
-  result: mockCharts,
-});
+fetchMock.get(
+  chartsEndpoint,
+  {
+    result: mockCharts,
+  },
+  { name: chartsEndpoint },
+);
 
 fetchMock.get(chartsInfoEndpoint, {
-  permissions: ['can_add', 'can_edit', 'can_delete', 'can_export'],
+  permissions: ['can_add', 'can_write', 'can_delete', 'can_export'],
 });
 
 fetchMock.get(chartFavoriteStatusEndpoint, {
@@ -99,6 +104,10 @@ const renderChartTable = (props: any) =>
     render(<ChartTable {...props} />, renderOptions);
   });
 
+beforeEach(() => {
+  window.localStorage.removeItem(LocalStorageKeys.HomepageChartFilter);
+});
+
 test('renders with EmptyState if no data present', async () => {
   await renderChartTable(mockedProps);
   expect(screen.getAllByRole('tab')).toHaveLength(3);
@@ -178,3 +187,58 @@ test('handles chart export with correct ID and shows spinner', async () => {
     { timeout: 3000 },
   );
 });
+
+test('refreshes other tab data after deleting a chart', async () => {
+  fetchMock.removeRoute(chartsEndpoint);
+  fetchMock.get(
+    chartsEndpoint,
+    {
+      result: mockCharts.slice(1),
+      count: mockCharts.length - 1,
+    },
+    { name: chartsEndpoint },
+  );
+  fetchMock.delete('glob:*/api/v1/chart/0', {
+    message: 'Chart deleted',
+  });
+
+  await renderChartTable({
+    ...otherTabProps,
+    otherTabTitle: 'All',
+  });
+
+  expect(screen.getByText('cool chart 0')).toBeInTheDocument();
+
+  const refreshCallsBeforeDelete =
+    fetchMock.callHistory.calls(chartsEndpoint).length;
+
+  const moreButtons = screen.getAllByRole('img', { name: /more/i });
+  await userEvent.click(moreButtons[0]);
+
+  await userEvent.click(await screen.findByText('Delete'));
+
+  const deleteInput = screen.getByTestId('delete-modal-input');
+  await userEvent.type(deleteInput, 'DELETE');
+  await userEvent.click(screen.getByTestId('modal-confirm-button'));
+
+  await waitFor(() => {
+    expect(
+      fetchMock.callHistory.calls(/api\/v1\/chart\/0/, {
+        method: 'DELETE',
+      }),
+    ).toHaveLength(1);
+  });
+
+  await waitFor(() => {
+    expect(fetchMock.callHistory.calls(chartsEndpoint).length).toBe(
+      refreshCallsBeforeDelete + 1,
+    );
+  });
+
+  await waitFor(() => {
+    expect(screen.queryByText('cool chart 0')).not.toBeInTheDocument();
+  });
+
+  expect(screen.getByText('cool chart 1')).toBeInTheDocument();
+  expect(screen.getByText('cool chart 2')).toBeInTheDocument();
+});
diff --git a/superset-frontend/src/features/home/ChartTable.tsx b/superset-frontend/src/features/home/ChartTable.tsx
index 013128f71c8..37d594c7c50 100644
--- a/superset-frontend/src/features/home/ChartTable.tsx
+++ b/superset-frontend/src/features/home/ChartTable.tsx
@@ -112,18 +112,19 @@ function ChartTable({
   const [preparingExport, setPreparingExport] = useState<boolean>(false);
   const [loaded, setLoaded] = useState<boolean>(false);
 
-  const getData = (tab: TableTab) =>
-    fetchData({
-      pageIndex: 0,
-      pageSize: PAGE_SIZE,
-      sortBy: [
-        {
-          id: 'changed_on_delta_humanized',
-          desc: true,
-        },
-      ],
-      filters: getFilterValues(tab, WelcomeTable.Charts, user, otherTabFilters),
-    });
+  const getChartFetchDataConfig = (tab: TableTab) => ({
+    pageIndex: 0,
+    pageSize: PAGE_SIZE,
+    sortBy: [
+      {
+        id: 'changed_on_delta_humanized',
+        desc: true,
+      },
+    ],
+    filters: getFilterValues(tab, WelcomeTable.Charts, user, otherTabFilters),
+  });
+
+  const getData = (tab: TableTab) => fetchData(getChartFetchDataConfig(tab));
 
   useEffect(() => {
     if (loaded || activeTab === TableTab.Favorite) {
@@ -234,6 +235,7 @@ function ChartTable({
               refreshData={refreshData}
               addDangerToast={addDangerToast}
               addSuccessToast={addSuccessToast}
+              getData={getData}
               favoriteStatus={favoriteStatus[e.id]}
               saveFavoriteStatus={saveFavoriteStatus}
               handleBulkChartExport={handleBulkChartExport}
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index a7042146348..a7e7d894c57 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -327,6 +327,7 @@ export function handleChartDelete(
   refreshData: (arg0?: FetchDataConfig | null) => void,
   chartFilter?: string,
   userId?: string | number,
+  getData?: (tab: TableTab) => void,
 ) {
   const filters = {
     pageIndex: 0,
@@ -350,6 +351,7 @@ export function handleChartDelete(
   }).then(
     () => {
       if (chartFilter === 'Mine') refreshData(filters);
+      else if (chartFilter && getData) getData(chartFilter as TableTab);
       else refreshData();
       addSuccessToast(t('Deleted: %s', sliceName));
     },