You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2026/08/25 00:38:36 UTC

(superset) branch master updated: fix(archived-list): use the semantic-layers-aware label for the dataset type (#43465)

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

aminghadersohi 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 f83fb7c0e07 fix(archived-list): use the semantic-layers-aware label for the dataset type (#43465)
f83fb7c0e07 is described below

commit f83fb7c0e07ae646f4e8bcc9bf86378b160a7f2e
Author: Mike Bridge <mi...@preset.io>
AuthorDate: Mon Aug 24 18:38:29 2026 -0600

    fix(archived-list): use the semantic-layers-aware label for the dataset type (#43465)
    
    Co-authored-by: Claude Fable 5 <no...@anthropic.com>
    Co-authored-by: Amin Ghadersohi <am...@gmail.com>
---
 docs/docs/using-superset/recently-archived.mdx     |  3 +-
 .../src/pages/ArchivedList/ArchivedList.test.tsx   | 72 ++++++++++++++++++++++
 superset-frontend/src/pages/ArchivedList/index.tsx | 19 +++---
 3 files changed, 85 insertions(+), 9 deletions(-)

diff --git a/docs/docs/using-superset/recently-archived.mdx b/docs/docs/using-superset/recently-archived.mdx
index 777e4efa56f..ebca8c6c416 100644
--- a/docs/docs/using-superset/recently-archived.mdx
+++ b/docs/docs/using-superset/recently-archived.mdx
@@ -26,7 +26,8 @@ page and its menu entry are hidden, and deletes are permanent as before.
 ## Finding archived objects
 
 Open **Recently Archived** and pick a type — **Chart**, **Dashboard**, or
-**Dataset** — from the Type selector. The view shows one type at a time; each
+**Dataset** (shown as **Datasource** when semantic layers are enabled) — from
+the Type selector. The view shows one type at a time; each
 type is read from its own list endpoint, so the same row-level access rules that
 govern the normal lists apply here.
 
diff --git a/superset-frontend/src/pages/ArchivedList/ArchivedList.test.tsx b/superset-frontend/src/pages/ArchivedList/ArchivedList.test.tsx
index 360f682898b..849e3164b9b 100644
--- a/superset-frontend/src/pages/ArchivedList/ArchivedList.test.tsx
+++ b/superset-frontend/src/pages/ArchivedList/ArchivedList.test.tsx
@@ -25,8 +25,10 @@ import {
   fireEvent,
   userEvent,
   waitFor,
+  within,
   selectOption,
 } from 'spec/helpers/testing-library';
+import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
 import { MemoryRouter } from 'react-router-dom';
 import { QueryParamProvider } from 'use-query-params';
 import { ReactRouter5Adapter } from 'use-query-params/adapters/react-router-5';
@@ -88,6 +90,15 @@ const mockCharts = [
 // list so `_info` requests resolve to it rather than the broader list glob.
 // withToasts injects the toast callbacks as props; the harness renders no
 // toast container, so the spy is the only way to pin what the user is told.
+// The type label for the dataset concept is flag-aware (SEMANTIC_LAYERS →
+// "Datasource"); mock the flag reader so tests can exercise both states. The
+// default (false for every flag) matches the real test environment, where no
+// bootstrap flags are set.
+jest.mock('@superset-ui/core', () => ({
+  ...jest.requireActual('@superset-ui/core'),
+  isFeatureEnabled: jest.fn(() => false),
+}));
+
 const mockAddDangerToast = jest.fn();
 jest.mock('src/components/MessageToasts/withToasts', () => ({
   __esModule: true,
@@ -144,6 +155,13 @@ beforeEach(() => {
   mockAddDangerToast.mockClear();
 });
 
+afterEach(() => {
+  // The flag mock is shared module state; restore the environment default so a
+  // flag-flipping test that dies mid-body (e.g. by Jest timeout) cannot leak
+  // SEMANTIC_LAYERS into whichever test runs next.
+  (isFeatureEnabled as jest.Mock).mockImplementation(() => false);
+});
+
 test('renders archived rows with Name and Type columns', async () => {
   mockRoutes();
   renderArchivedList();
@@ -573,3 +591,57 @@ test('a viewer who can read none of the types gets an empty state, not three 403
   // No list fetch was ever issued.
   expect(fetchMock.callHistory.calls(/chart\/\?q/)).toHaveLength(0);
 });
+
+test('labels the dataset type "Datasource" when semantic layers is enabled', async () => {
+  (isFeatureEnabled as jest.Mock).mockImplementation(
+    (flag: FeatureFlag) => flag === FeatureFlag.SemanticLayers,
+  );
+  mockRoutes();
+  renderArchivedList();
+  await screen.findByText('Deleted Chart One');
+
+  userEvent.click(screen.getByRole('combobox', { name: 'Type' }));
+  expect(
+    await screen.findByRole('option', { name: 'Datasource' }),
+  ).toBeInTheDocument();
+  expect(
+    screen.queryByRole('option', { name: 'Dataset' }),
+  ).not.toBeInTheDocument();
+
+  // Selecting the renamed option still drives the dataset resource —
+  // the underlying type value is flag-independent.
+  await selectOption('Datasource', 'Type');
+  await screen.findByText('deleted_table_one');
+  expect(
+    fetchMock.callHistory.calls(datasetListEndpoint).length,
+  ).toBeGreaterThan(0);
+  // Pin the Type COLUMN cell, not just the Select's own rendered value.
+  const datasetRow = screen.getByText('deleted_table_one').closest('tr');
+  expect(
+    within(datasetRow as HTMLElement).getByText('Datasource'),
+  ).toBeInTheDocument();
+});
+
+test('labels the dataset type "Dataset" when semantic layers is disabled', async () => {
+  mockRoutes();
+  renderArchivedList();
+  await screen.findByText('Deleted Chart One');
+
+  userEvent.click(screen.getByRole('combobox', { name: 'Type' }));
+  expect(
+    await screen.findByRole('option', { name: 'Dataset' }),
+  ).toBeInTheDocument();
+  expect(
+    screen.queryByRole('option', { name: 'Datasource' }),
+  ).not.toBeInTheDocument();
+
+  await selectOption('Dataset', 'Type');
+  await screen.findByText('deleted_table_one');
+  expect(
+    fetchMock.callHistory.calls(datasetListEndpoint).length,
+  ).toBeGreaterThan(0);
+  const datasetRow = screen.getByText('deleted_table_one').closest('tr');
+  expect(
+    within(datasetRow as HTMLElement).getByText('Dataset'),
+  ).toBeInTheDocument();
+});
diff --git a/superset-frontend/src/pages/ArchivedList/index.tsx b/superset-frontend/src/pages/ArchivedList/index.tsx
index fffa83b83ec..c6d5f811c5d 100644
--- a/superset-frontend/src/pages/ArchivedList/index.tsx
+++ b/superset-frontend/src/pages/ArchivedList/index.tsx
@@ -37,6 +37,7 @@ import {
   type ListViewFilters,
 } from 'src/components';
 import SubMenu from 'src/features/home/SubMenu';
+import { datasetLabel } from 'src/features/semanticLayers/label';
 import withToasts from 'src/components/MessageToasts/withToasts';
 import { recoveredToast } from 'src/utils/softDeleteCopy';
 import { findPermission } from 'src/utils/findPermission';
@@ -82,10 +83,12 @@ const EmptyStateRow = styled.div`
   `}
 `;
 
-const TYPE_LABELS: Record<ArchivedType, string> = {
-  chart: t('Chart'),
-  dashboard: t('Dashboard'),
-  dataset: t('Dataset'),
+// Getters, not strings: the dataset label follows the SEMANTIC_LAYERS flag
+// ("Dataset" / "Datasource"), read at render time via the shared naming module.
+const TYPE_LABELS: Record<ArchivedType, () => string> = {
+  chart: () => t('Chart'),
+  dashboard: () => t('Dashboard'),
+  dataset: datasetLabel,
 };
 
 interface ToastProps {
@@ -166,7 +169,7 @@ function ArchivedListBody({
     refreshData,
   } = useListViewResource<ArchivedItem>(
     config.resource,
-    TYPE_LABELS[type],
+    TYPE_LABELS[type](),
     addDangerToast,
     true,
     [],
@@ -247,7 +250,7 @@ function ArchivedListBody({
         name => {
           const { text, options } = recoveredToast(
             name,
-            TYPE_LABELS[type],
+            TYPE_LABELS[type](),
             item.url ?? item.explore_url,
           );
           addSuccessToast(text, options);
@@ -306,7 +309,7 @@ function ArchivedListBody({
         id: config.nameField,
       },
       {
-        Cell: () => TYPE_LABELS[type],
+        Cell: () => TYPE_LABELS[type](),
         Header: t('Type'),
         id: 'type',
         disableSortBy: true,
@@ -539,7 +542,7 @@ function ArchivedList({ addDangerToast, addSuccessToast }: ToastProps) {
           onChange={handleTypeChange}
           options={availableTypes.map(option => ({
             value: option,
-            label: TYPE_LABELS[option],
+            label: TYPE_LABELS[option](),
           }))}
         />
       </TypeSelectRow>