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));
},