diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx b/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx index a8010f1c6a8..a1969c99612 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/ExploreChartHeader.test.tsx @@ -383,6 +383,34 @@ describe('ExploreChartHeader', () => { expect(setShowModal).toHaveBeenCalledWith(false); }); + + test('renders Matrixify tag when matrixify is enabled', async () => { + const props = createProps({ + formData: { + ...createProps().chart.latestQueryFormData, + matrixify_enable_vertical_layout: true, + matrixify_mode_rows: 'metrics', + matrixify_rows: [{ label: 'COUNT(*)', expressionType: 'SIMPLE' }], + }, + }); + render(, { useRedux: true }); + + const matrixifyTag = await screen.findByText('Matrixify'); + expect(matrixifyTag).toBeInTheDocument(); + }); + + test('does not render Matrixify tag when matrixify is disabled', async () => { + const props = createProps({ + formData: { + ...createProps().chart.latestQueryFormData, + }, + }); + render(, { useRedux: true }); + + await waitFor(() => { + expect(screen.queryByText('Matrixify')).not.toBeInTheDocument(); + }); + }); }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx index 8be2bea51bc..857e5032f26 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx @@ -27,7 +27,7 @@ import { UnsavedChangesModal, } from '@superset-ui/core/components'; import { AlteredSliceTag } from 'src/components'; -import { SupersetClient } from '@superset-ui/core'; +import { SupersetClient, isMatrixifyEnabled } from '@superset-ui/core'; import { logging } from '@apache-superset/core'; import { css, t } from '@apache-superset/core/ui'; import { chartPropShape } from 'src/dashboard/util/propShapes'; @@ -42,6 +42,7 @@ import { deleteActiveReport } from 'src/features/reports/ReportModal/actions'; import { useUnsavedChangesPrompt } from 'src/hooks/useUnsavedChangesPrompt'; import { getChartFormDiffs } from 'src/utils/getChartFormDiffs'; import { StreamingExportModal } from 'src/components/StreamingExportModal'; +import { Tag } from 'src/components/Tag'; import { useExploreAdditionalActionsMenu } from '../useExploreAdditionalActionsMenu'; import { useExploreMetadataBar } from './useExploreMetadataBar'; @@ -272,6 +273,9 @@ export const ExploreChartHeader = ({ currentFormData={currentFormData} /> ) : null} + {formData && isMatrixifyEnabled(formData) && ( + + )} {metadataBar} } diff --git a/superset-frontend/src/pages/ChartList/ChartList.test.tsx b/superset-frontend/src/pages/ChartList/ChartList.test.tsx index 214f887c7aa..107a7e2d022 100644 --- a/superset-frontend/src/pages/ChartList/ChartList.test.tsx +++ b/superset-frontend/src/pages/ChartList/ChartList.test.tsx @@ -17,7 +17,12 @@ * under the License. */ import fetchMock from 'fetch-mock'; -import { screen, waitFor, fireEvent } from 'spec/helpers/testing-library'; +import { + screen, + waitFor, + fireEvent, + within, +} from 'spec/helpers/testing-library'; import { isFeatureEnabled } from '@superset-ui/core'; import { API_ENDPOINTS, @@ -245,6 +250,34 @@ describe('ChartList', () => { ); }); + test('displays Matrixify tag for charts with matrixify enabled', async () => { + renderChartList(mockUser); + + // Wait for the chart list to load + await waitFor(() => { + expect(screen.getByText('Test Chart 0')).toBeInTheDocument(); + }); + + // Find the row containing Test Chart 0 (which has matrixify enabled) + const chart0Row = screen.getByText('Test Chart 0').closest('tr'); + expect(chart0Row).toBeInTheDocument(); + + // Check that the Matrixify tag is present in this row + const matrixifyTag = within(chart0Row as HTMLElement).getByText( + 'Matrixify', + ); + expect(matrixifyTag).toBeInTheDocument(); + + // Find the row containing Test Chart 1 (which doesn't have matrixify) + const chart1Row = screen.getByText('Test Chart 1').closest('tr'); + expect(chart1Row).toBeInTheDocument(); + + // Check that the Matrixify tag is NOT present in this row + expect( + within(chart1Row as HTMLElement).queryByText('Matrixify'), + ).not.toBeInTheDocument(); + }); + test('handles API errors gracefully', async () => { // Mock API failure fetchMock.get( diff --git a/superset-frontend/src/pages/ChartList/ChartList.testHelpers.tsx b/superset-frontend/src/pages/ChartList/ChartList.testHelpers.tsx index 1e600cf41b7..a6578935466 100644 --- a/superset-frontend/src/pages/ChartList/ChartList.testHelpers.tsx +++ b/superset-frontend/src/pages/ChartList/ChartList.testHelpers.tsx @@ -60,6 +60,14 @@ export const mockCharts = [ thumbnail_url: '/api/v1/chart/0/thumbnail/', certified_by: null, certification_details: null, + + // Add form_data with matrixify enabled + form_data: { + viz_type: 'table', + matrixify_enable_vertical_layout: true, + matrixify_mode_rows: 'metrics', + matrixify_rows: [{ label: 'COUNT(*)', expressionType: 'SIMPLE' }], + }, }, { id: 1, @@ -102,6 +110,11 @@ export const mockCharts = [ thumbnail_url: '/api/v1/chart/1/thumbnail/', certified_by: 'Data Team', certification_details: 'Approved for production use', + + // Add form_data without matrixify + form_data: { + viz_type: 'bar', + }, }, { id: 2, diff --git a/superset-frontend/src/pages/ChartList/index.tsx b/superset-frontend/src/pages/ChartList/index.tsx index b0abfe0c8ec..f0ff7fb5475 100644 --- a/superset-frontend/src/pages/ChartList/index.tsx +++ b/superset-frontend/src/pages/ChartList/index.tsx @@ -16,15 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -import { t } from '@apache-superset/core'; +import { t, SupersetTheme } from '@apache-superset/core'; import { isFeatureEnabled, FeatureFlag, getChartMetadataRegistry, JsonResponse, SupersetClient, + isMatrixifyEnabled, } from '@superset-ui/core'; -import { styled } from '@apache-superset/core/ui'; +import { css, styled } from '@apache-superset/core/ui'; import { useState, useMemo, useCallback } from 'react'; import rison from 'rison'; import { uniqBy } from 'lodash'; @@ -78,6 +79,7 @@ import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { findPermission } from 'src/utils/findPermission'; import { QueryObjectColumns } from 'src/views/CRUD/types'; import { WIDER_DROPDOWN_WIDTH } from 'src/components/ListView/utils'; +import { Tag } from 'src/components/Tag'; const FlexRowContainer = styled.div` align-items: center; @@ -375,9 +377,22 @@ function ChartList(props: ChartListProps) { { Cell: ({ row: { - original: { viz_type: vizType }, + original: { viz_type: vizType, form_data: formData }, }, - }: any) => registry.get(vizType)?.name || vizType, + }: any) => ( + <> + {registry.get(vizType)?.name || vizType} + {formData && isMatrixifyEnabled(formData) && ( + css` + margin-left: ${theme.marginXS}px; + `} + > + + + )} + + ), Header: t('Type'), accessor: 'viz_type', id: 'viz_type',