From 9d082798c7dc2eb1c4b9d4fc97804b184ff40780 Mon Sep 17 00:00:00 2001 From: Evan Rusackas Date: Fri, 16 Jan 2026 07:08:55 -0800 Subject: [PATCH] fix(types): improve type safety in explore components - Fix embedded/utils.ts: use `as unknown as QueryFormData` for proper type cast - Fix SaveDatasetModal: use 'base' instead of null for mountExploreUrl - Fix ExploreChartHeader: add color_namespace to metadata type, fix FaveStar props, cast report to ReportObject, fix AlteredSliceTag formData types - Fix useExploreAdditionalActionsMenu: fix StreamingProgress import, add ExploreSlice interface, fix onClick handlers with proper React.MouseEvent types, fix getChartPermalink datasource checks, fix VizType casts, fix exportType cast - Fix FilterValue.tsx and FiltersConfigForm.tsx: use correct type for waitForAsyncData parameter in 202 response handling Co-Authored-By: Claude Opus 4.5 --- .../components/SaveDatasetModal/index.tsx | 4 +- .../src/components/Chart/Chart.tsx | 6 +- .../src/components/Chart/ChartRenderer.tsx | 8 +- .../src/components/Chart/chartActions.test.ts | 8 +- .../DatasourceEditor/DatasourceEditor.tsx | 16 ++- .../FilterBar/FilterControls/FilterValue.tsx | 2 +- .../FiltersConfigForm/FiltersConfigForm.tsx | 2 +- superset-frontend/src/embedded/utils.ts | 8 +- .../components/useResultsPane.tsx | 4 +- .../components/ExploreChartHeader/index.tsx | 24 ++-- .../components/ExploreViewContainer/index.tsx | 30 ++++- .../controls/AnnotationLayerControl/index.tsx | 4 +- .../useExploreAdditionalActionsMenu/index.tsx | 105 ++++++++++++------ superset-frontend/src/explore/types.ts | 1 + .../features/reports/ReportModal/actions.ts | 6 +- .../src/middleware/logger.test.ts | 8 +- 16 files changed, 154 insertions(+), 82 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 490f35e6725..c1a2fc871f0 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -258,7 +258,7 @@ export const SaveDatasetModal = ({ ]); setLoading(false); - const url = mountExploreUrl(null, { + const url = mountExploreUrl('base', { [URL_PARAMS.formDataKey.name]: key, }); createWindow(url); @@ -364,7 +364,7 @@ export const SaveDatasetModal = ({ }) .then((key: string) => { setLoading(false); - const url = mountExploreUrl(null, { + const url = mountExploreUrl('base', { [URL_PARAMS.formDataKey.name]: key, }); createWindow(url); diff --git a/superset-frontend/src/components/Chart/Chart.tsx b/superset-frontend/src/components/Chart/Chart.tsx index 1d4e4474b6b..136774eb1d5 100644 --- a/superset-frontend/src/components/Chart/Chart.tsx +++ b/superset-frontend/src/components/Chart/Chart.tsx @@ -302,7 +302,11 @@ class Chart extends PureComponent { isCurrentUserBot() ? ( ) : ( diff --git a/superset-frontend/src/components/Chart/ChartRenderer.tsx b/superset-frontend/src/components/Chart/ChartRenderer.tsx index 2560abf089a..d68667ebb64 100644 --- a/superset-frontend/src/components/Chart/ChartRenderer.tsx +++ b/superset-frontend/src/components/Chart/ChartRenderer.tsx @@ -55,9 +55,11 @@ interface LegendState { } // Webpack globals declaration -declare const __webpack_require__: { - h?: () => string; -} | undefined; +declare const __webpack_require__: + | { + h?: () => string; + } + | undefined; // Types for chart actions interface ChartActions { diff --git a/superset-frontend/src/components/Chart/chartActions.test.ts b/superset-frontend/src/components/Chart/chartActions.test.ts index 21666b509ef..720ab1a74d3 100644 --- a/superset-frontend/src/components/Chart/chartActions.test.ts +++ b/superset-frontend/src/components/Chart/chartActions.test.ts @@ -132,7 +132,9 @@ describe('chart actions', () => { some_param: 'fake query!', result_type: 'full', result_format: 'json', - } as unknown as Awaited>); + } as unknown as Awaited< + ReturnType + >); fakeMetadata = { useLegacyApi: true }; mockedGetChartMetadataRegistry.mockImplementation( () => @@ -195,7 +197,9 @@ describe('chart actions', () => { >); const getQuerySettingsStub = sinon .stub(exploreUtils, 'getQuerySettings') - .returns([false, () => {}] as unknown as ReturnType); + .returns([false, () => {}] as unknown as ReturnType< + typeof exploreUtils.getQuerySettings + >); try { const thunkAction = actions.exploreJSON( diff --git a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx index d2b36166352..5b98e044706 100644 --- a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx +++ b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx @@ -1265,10 +1265,7 @@ class DatasourceEditor extends PureComponent< } } - findDuplicates( - arr: T[], - accessor: (obj: T) => string, - ): string[] { + findDuplicates(arr: T[], accessor: (obj: T) => string): string[] { const seen: Record = {}; const dups: string[] = []; arr.forEach((obj: T) => { @@ -1294,7 +1291,10 @@ class DatasourceEditor extends PureComponent< ); // Looking for duplicate metric_name - dups = this.findDuplicates(datasource.metrics ?? [], obj => obj.metric_name); + dups = this.findDuplicates( + datasource.metrics ?? [], + obj => obj.metric_name, + ); errors = errors.concat( dups.map(name => t('Metric name [%s] is duplicated', name)), ); @@ -1587,7 +1587,11 @@ class DatasourceEditor extends PureComponent< allowDeletes itemRenderers={{ name: (d, onChange) => ( - + ), config: (v, onChange) => ( = ({ setState([result as ChartDataResponseResult]); handleFilterLoadFinish(); } else if (response.status === 202) { - waitForAsyncData(result as ChartDataResponseResult) + waitForAsyncData(result as Parameters[0]) .then((asyncResult: ChartDataResponseResult[]) => { setState(asyncResult); handleFilterLoadFinish(); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx index fb2bddcf87f..3c79efc590b 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx @@ -502,7 +502,7 @@ const FiltersConfigForm = ( defaultValueQueriesData: [result as ChartDataResponseResult], }); } else if (response.status === 202) { - waitForAsyncData(result as ChartDataResponseResult) + waitForAsyncData(result as Parameters[0]) .then((asyncResult: ChartDataResponseResult[]) => { setNativeFilterFieldValuesWrapper({ defaultValueQueriesData: asyncResult, diff --git a/superset-frontend/src/embedded/utils.ts b/superset-frontend/src/embedded/utils.ts index ef9e0f64352..404c493962c 100644 --- a/superset-frontend/src/embedded/utils.ts +++ b/superset-frontend/src/embedded/utils.ts @@ -17,7 +17,11 @@ * under the License. */ -import { DataMaskStateWithId, JsonObject, QueryFormData } from '@superset-ui/core'; +import { + DataMaskStateWithId, + JsonObject, + QueryFormData, +} from '@superset-ui/core'; import { logging } from '@apache-superset/core'; import { isEmpty, isEqual } from 'lodash'; import { NATIVE_FILTER_PREFIX } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/utils'; @@ -138,7 +142,7 @@ export const getChartDataPayloads = async ( }; const payload = await buildV1ChartDataPayload({ - formData: formData as QueryFormData, + formData: formData as unknown as QueryFormData, resultFormat: 'json', resultType: 'results', ownState, diff --git a/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx b/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx index dab52b0d59d..a25a7974101 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx @@ -73,7 +73,9 @@ export const useResultsPane = ({ // it's an invalid formData when gets a errorMessage if (errorMessage) return; if (isRequest && cache.has(queryFormData)) { - setResultResp(ensureIsArray(cache.get(queryFormData)) as QueryResultInterface[]); + setResultResp( + ensureIsArray(cache.get(queryFormData)) as QueryResultInterface[], + ); setResponseError(''); if (queryForce) { setForceQuery?.(false); diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.tsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.tsx index df60ca43bad..463e402a5c4 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/index.tsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.tsx @@ -45,14 +45,15 @@ import { Tag } from 'src/components/Tag'; import { ChartState, ExplorePageInitialData } from 'src/explore/types'; import { Slice } from 'src/types/Chart'; import { AlertObject } from 'src/features/alerts/types'; +import { ReportObject } from 'src/features/reports/types'; import { User } from 'src/types/bootstrapTypes'; import { useExploreAdditionalActionsMenu } from '../useExploreAdditionalActionsMenu'; import { useExploreMetadataBar } from './useExploreMetadataBar'; interface ExploreActions { updateChartTitle: (title: string) => void; - fetchFaveStar: (sliceId: string) => void; - saveFaveStar: (sliceId: string, isStarred: boolean) => void; + fetchFaveStar: (sliceId: number) => void; + saveFaveStar: (sliceId: number, isStarred: boolean) => void; redirectSQLLab: ( formData: QueryFormData, history?: ReturnType | false, @@ -178,7 +179,7 @@ export const ExploreChartHeader: FC = ({ ); const handleReportDelete = async (report: AlertObject) => { - await dispatch(deleteActiveReport(report)); + await dispatch(deleteActiveReport(report as unknown as ReportObject)); setCurrentReportDeleting(null); }; @@ -205,7 +206,7 @@ export const ExploreChartHeader: FC = ({ setCurrentReportDeleting, ); - const metadataBar = useExploreMetadataBar(metadata, slice); + const metadataBar = useExploreMetadataBar(metadata, slice ?? null); const oldSliceName = slice?.slice_name; const originalFormData = useMemo(() => { @@ -234,7 +235,9 @@ export const ExploreChartHeader: FC = ({ triggerManualSave, } = useUnsavedChangesPrompt({ hasUnsavedChanges: Object.keys(formDiffs).length > 0, - onSave: () => dispatch(setSaveChartModalVisibility(true)), + onSave: () => { + dispatch(setSaveChartModalVisibility(true)); + }, isSaveModalVisible, manualSaveOnUnsavedChanges: true, }); @@ -261,7 +264,8 @@ export const ExploreChartHeader: FC = ({ canEdit: !slice || canOverwrite || - (slice?.owners || []).includes(user?.userId), + (user?.userId !== undefined && + (slice?.owners || []).includes(user.userId)), onSave: actions.updateChartTitle, placeholder: t('Add the name of the chart'), label: t('Chart title'), @@ -271,9 +275,9 @@ export const ExploreChartHeader: FC = ({ certifiedBy: slice?.certified_by, details: slice?.certification_details, }} - showFaveStar={!!user?.userId} + showFaveStar={!!user?.userId && slice?.slice_id !== undefined} faveStarProps={{ - itemId: slice?.slice_id, + itemId: slice?.slice_id ?? 0, fetchFaveStar: actions.fetchFaveStar, saveFaveStar: actions.saveFaveStar, isStarred, @@ -285,8 +289,8 @@ export const ExploreChartHeader: FC = ({ ) : null} {formData && isMatrixifyEnabled(formData) && ( diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx index 7c725912a4f..7e31d50fa51 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.tsx @@ -17,7 +17,14 @@ * under the License. */ /* eslint camelcase: 0 */ -import { memo, useCallback, useEffect, useMemo, useState, KeyboardEvent } from 'react'; +import { + memo, + useCallback, + useEffect, + useMemo, + useState, + KeyboardEvent, +} from 'react'; import { bindActionCreators, Dispatch } from 'redux'; import { connect } from 'react-redux'; import { @@ -64,7 +71,12 @@ import * as exploreActions from 'src/explore/actions/exploreActions'; import * as saveModalActions from 'src/explore/actions/saveModalActions'; import { useTabId } from 'src/hooks/useTabId'; import withToasts from 'src/components/MessageToasts/withToasts'; -import { ChartState, Datasource, ExplorePageInitialData, SaveActionType } from 'src/explore/types'; +import { + ChartState, + Datasource, + ExplorePageInitialData, + SaveActionType, +} from 'src/explore/types'; import { Slice } from 'src/types/Chart'; import { User } from 'src/types/bootstrapTypes'; import ExploreChartPanel from '../ExploreChartPanel'; @@ -222,7 +234,10 @@ const defaultSidebarsWidth: Record = { }; function getSidebarWidths(key: LocalStorageKeys): number { - const defaultKey = key === LocalStorageKeys.ControlsWidth ? 'controls_width' : 'datasource_width'; + const defaultKey = + key === LocalStorageKeys.ControlsWidth + ? 'controls_width' + : 'datasource_width'; return getItem(key, defaultSidebarsWidth[defaultKey]); } @@ -967,13 +982,18 @@ function ExploreViewContainer(props: ExploreViewContainerProps) { ); } -const retainQueryModeRequirements = (hiddenFormData: Partial | undefined): string[] => +const retainQueryModeRequirements = ( + hiddenFormData: Partial | undefined, +): string[] => Object.keys(hiddenFormData ?? {}).filter( key => !QUERY_MODE_REQUISITES.has(key), ); interface SliceWithSubheader extends Slice { - form_data?: QueryFormData & { subheader?: string; subheader_font_size?: number }; + form_data?: QueryFormData & { + subheader?: string; + subheader_font_size?: number; + }; } function patchBigNumberTotalFormData( diff --git a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx index 96f99694e1b..0cacfa204c3 100644 --- a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx +++ b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/index.tsx @@ -306,7 +306,9 @@ function mapDispatchToProps( ) { return { refreshAnnotationData: (payload: Payload) => - dispatch(runAnnotationQuery(payload as Parameters[0])), + dispatch( + runAnnotationQuery(payload as Parameters[0]), + ), }; } diff --git a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx index 9c980eb3e7f..47278d6fecf 100644 --- a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx +++ b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { +import React, { ReactElement, useCallback, useMemo, @@ -32,6 +32,8 @@ import { VizType, JsonObject, LatestQueryFormData, + QueryFormData, + Behavior, } from '@superset-ui/core'; import { css, styled, useTheme, t } from '@apache-superset/core/ui'; import { @@ -60,7 +62,7 @@ import { import exportPivotExcel from 'src/utils/downloadAsPivotExcel'; import { useStreamingExport, - StreamingExportProgress, + StreamingProgress, } from 'src/components/StreamingExportModal'; import { Slice } from 'src/types/Chart'; import { ChartState, ExplorePageInitialData } from 'src/explore/types'; @@ -156,15 +158,20 @@ interface OwnStateWithClientView extends JsonObject { export interface StreamingExportState { isVisible: boolean; - progress: StreamingExportProgress; + progress: StreamingProgress; onCancel: () => void; onRetry: () => void; onDownload: () => void; } +interface ExploreSlice { + slice?: Slice | null; + form_data?: Partial; +} + interface ExploreState { charts?: Record; - explore?: JsonObject; + explore?: ExploreSlice; common?: { conf?: { CSV_STREAMING_ROW_THRESHOLD?: number; @@ -189,7 +196,9 @@ export const useExploreAdditionalActionsMenu = ( ) => void, onOpenPropertiesModal: () => void, ownState: OwnStateWithClientView | undefined, - dashboards: ExplorePageInitialData['metadata']['dashboards'] | undefined, + dashboards: + | NonNullable['dashboards'] + | undefined, showReportModal: () => void, setCurrentReportDeleting: Dispatch>, ...rest: MenuProps[] @@ -203,8 +212,8 @@ export const useExploreAdditionalActionsMenu = ( dashboardSearchTerm, 300, ); - const chart = useSelector( - state => state.charts?.[getChartKey(state.explore)], + const chart = useSelector(state => + state.explore ? state.charts?.[getChartKey(state.explore)] : undefined, ); const streamingThreshold = useSelector( state => @@ -216,9 +225,9 @@ export const useExploreAdditionalActionsMenu = ( const [isStreamingModalVisible, setIsStreamingModalVisible] = useState(false); const { progress, - isExporting, + isExporting: _isExporting, startExport, - cancelExport, + cancelExport: _cancelExport, resetExport, retryExport, } = useStreamingExport({ @@ -255,19 +264,24 @@ export const useExploreAdditionalActionsMenu = ( searchTerm: debouncedDashboardSearchTerm, }); - const showDashboardSearch = dashboards?.length > SEARCH_THRESHOLD; + const showDashboardSearch = (dashboards?.length ?? 0) > SEARCH_THRESHOLD; const vizType = latestQueryFormData?.viz_type; const meta = vizType ? getChartMetadataRegistry().get(vizType) : undefined; // Detect if the chart plugin exposes the export-current-view behavior const hasExportCurrentView = !!meta?.behaviors?.includes( - 'EXPORT_CURRENT_VIEW', + 'EXPORT_CURRENT_VIEW' as Behavior, ); const shareByEmail = useCallback(async () => { try { const subject = t('Superset Chart'); - const result = await getChartPermalink(latestQueryFormData); + if (!latestQueryFormData?.datasource) { + throw new Error('No datasource available'); + } + const result = await getChartPermalink( + latestQueryFormData as Pick, + ); if (!result?.url) { throw new Error('Failed to generate permalink'); } @@ -290,11 +304,12 @@ export const useExploreAdditionalActionsMenu = ( if ( isTableViz && - queriesResponse?.length > 1 && + queriesResponse && + queriesResponse.length > 1 && queriesResponse[1]?.data?.[0]?.rowcount ) { actualRowCount = queriesResponse[1].data[0].rowcount; - } else if (queriesResponse?.[0]?.sql_rowcount != null) { + } else if (queriesResponse && queriesResponse[0]?.sql_rowcount != null) { actualRowCount = queriesResponse[0].sql_rowcount; } else { actualRowCount = latestQueryFormData?.row_limit; @@ -304,7 +319,7 @@ export const useExploreAdditionalActionsMenu = ( const shouldUseStreaming = actualRowCount && actualRowCount >= streamingThreshold; - let filename; + let filename: string | undefined; if (shouldUseStreaming) { const now = new Date(); const date = now.toISOString().slice(0, 10); @@ -317,18 +332,22 @@ export const useExploreAdditionalActionsMenu = ( } return exportChart({ - formData: latestQueryFormData, + formData: latestQueryFormData as QueryFormData, ownState, resultType: 'full', resultFormat: 'csv', onStartStreamingExport: shouldUseStreaming ? exportParams => { - setIsStreamingModalVisible(true); - startExport({ - ...exportParams, - filename, - expectedRows: actualRowCount, - }); + if (exportParams.url) { + setIsStreamingModalVisible(true); + startExport({ + ...exportParams, + url: exportParams.url, + filename, + expectedRows: actualRowCount, + exportType: exportParams.exportType as 'csv' | 'xlsx', + }); + } } : null, }); @@ -346,7 +365,7 @@ export const useExploreAdditionalActionsMenu = ( () => canDownloadCSV ? exportChart({ - formData: latestQueryFormData, + formData: latestQueryFormData as QueryFormData, ownState, resultType: 'post_processed', resultFormat: 'csv', @@ -359,7 +378,7 @@ export const useExploreAdditionalActionsMenu = ( () => canDownloadCSV ? exportChart({ - formData: latestQueryFormData, + formData: latestQueryFormData as QueryFormData, ownState, resultType: 'results', resultFormat: 'json', @@ -372,7 +391,7 @@ export const useExploreAdditionalActionsMenu = ( () => canDownloadCSV ? exportChart({ - formData: latestQueryFormData, + formData: latestQueryFormData as QueryFormData, ownState, resultType: 'results', resultFormat: 'xlsx', @@ -383,11 +402,13 @@ export const useExploreAdditionalActionsMenu = ( const copyLink = useCallback(async () => { try { - if (!latestQueryFormData) { - throw new Error(); + if (!latestQueryFormData?.datasource) { + throw new Error('No datasource available'); } await copyTextToClipboard(async () => { - const result = await getChartPermalink(latestQueryFormData); + const result = await getChartPermalink( + latestQueryFormData as Pick, + ); if (!result?.url) { throw new Error('Failed to generate permalink'); } @@ -591,7 +612,10 @@ export const useExploreAdditionalActionsMenu = ( // Download submenu const allDataChildren = []; - if (VIZ_TYPES_PIVOTABLE.includes(latestQueryFormData.viz_type)) { + if ( + latestQueryFormData.viz_type && + VIZ_TYPES_PIVOTABLE.includes(latestQueryFormData.viz_type as VizType) + ) { allDataChildren.push( { key: MENU_KEYS.EXPORT_TO_CSV, @@ -686,7 +710,7 @@ export const useExploreAdditionalActionsMenu = ( key: MENU_KEYS.EXPORT_ALL_SCREENSHOT, label: t('Export screenshot (jpeg)'), icon: , - onClick: e => { + onClick: (e: { domEvent: React.MouseEvent | React.KeyboardEvent }) => { downloadAsImage( '.panel-body .chart-container', slice?.slice_name ?? t('New chart'), @@ -742,7 +766,7 @@ export const useExploreAdditionalActionsMenu = ( ); } else { exportChart({ - formData: latestQueryFormData, + formData: latestQueryFormData as QueryFormData, ownState, resultType: 'results', resultFormat: 'csv', @@ -790,7 +814,7 @@ export const useExploreAdditionalActionsMenu = ( key: MENU_KEYS.EXPORT_CURRENT_SCREENSHOT, label: t('Export screenshot (jpeg)'), icon: , - onClick: e => { + onClick: (e: { domEvent: React.MouseEvent | React.KeyboardEvent }) => { downloadAsImage( '.panel-body .chart-container', slice?.slice_name ?? t('New chart'), @@ -864,7 +888,11 @@ export const useExploreAdditionalActionsMenu = ( }); // Share submenu - const shareChildren = [ + const shareChildren: Array<{ + key: string; + label: React.ReactNode; + onClick: () => void; + }> = [ { key: MENU_KEYS.COPY_PERMALINK, label: t('Copy permalink to clipboard'), @@ -932,7 +960,9 @@ export const useExploreAdditionalActionsMenu = ( } modalTitle={t('View query')} modalBody={ - + } draggable resizable @@ -947,8 +977,11 @@ export const useExploreAdditionalActionsMenu = ( menuItems.push({ key: MENU_KEYS.RUN_IN_SQL_LAB, label: t('Run in SQL Lab'), - onClick: e => { - onOpenInEditor(latestQueryFormData, e.domEvent?.metaKey); + onClick: (e: { domEvent?: React.MouseEvent | React.KeyboardEvent }) => { + onOpenInEditor( + latestQueryFormData, + !!(e.domEvent as React.MouseEvent | undefined)?.metaKey, + ); setIsDropdownVisible(false); }, }); diff --git a/superset-frontend/src/explore/types.ts b/superset-frontend/src/explore/types.ts index 573fcb9c8f6..6bc6b1bb303 100644 --- a/superset-frontend/src/explore/types.ts +++ b/superset-frontend/src/explore/types.ts @@ -88,6 +88,7 @@ export interface ExplorePageInitialData { owners: string[]; created_by?: string; changed_by?: string; + color_namespace?: string; dashboards?: { id: number; dashboard_title: string; diff --git a/superset-frontend/src/features/reports/ReportModal/actions.ts b/superset-frontend/src/features/reports/ReportModal/actions.ts index 66a96e77888..03a66815704 100644 --- a/superset-frontend/src/features/reports/ReportModal/actions.ts +++ b/superset-frontend/src/features/reports/ReportModal/actions.ts @@ -119,11 +119,7 @@ export function fetchUISpecificReport({ ); }) .catch(() => - dispatch( - addDangerToast( - t('There was an issue fetching reports.'), - ), - ), + dispatch(addDangerToast(t('There was an issue fetching reports.'))), ); }; } diff --git a/superset-frontend/src/middleware/logger.test.ts b/superset-frontend/src/middleware/logger.test.ts index 106f5272478..b136a70e363 100644 --- a/superset-frontend/src/middleware/logger.test.ts +++ b/superset-frontend/src/middleware/logger.test.ts @@ -97,9 +97,7 @@ describe('logger middleware', () => { timeSandbox.clock.tick(2000); expect(postStub.callCount).toBe(1); - expect(postStub.getCall(0).args[0].endpoint).toMatch( - '/superset/log/', - ); + expect(postStub.getCall(0).args[0].endpoint).toMatch('/superset/log/'); }); test('should include ts, start_offset, event_name, impression_id, source, and source_id in every event', () => { @@ -139,9 +137,7 @@ describe('logger middleware', () => { timeSandbox.clock.tick(2000); expect(postStub.callCount).toBe(1); - expect( - postStub.getCall(0).args[0].postPayload.events, - ).toHaveLength(3); + expect(postStub.getCall(0).args[0].postPayload.events).toHaveLength(3); }); test('should use navigator.sendBeacon if it exists', () => {