import { useState } from 'react'; import { ConnectedProps, connect } from 'react-redux'; import { config, reportInteraction } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; import { Button, ButtonVariant, Dropdown, Menu, ToolbarButton } from '@grafana/ui'; import { t } from '@grafana/ui/src/utils/i18n'; import { useSelector } from 'app/types'; import { changeDatasource } from './state/datasource'; import { setQueries } from './state/query'; import { isSplit, selectExploreDSMaps, selectPanesEntries } from './state/selectors'; const mapDispatchToProps = { setQueries, changeDatasource, }; const connector = connect(undefined, mapDispatchToProps); interface ExploreRunQueryButtonProps { queries: DataQuery[]; rootDatasourceUid?: string; disabled?: boolean; variant?: ButtonVariant; onClick?: () => void; } export type Props = ConnectedProps & ExploreRunQueryButtonProps; /* This component does not validate datasources before running them. Root datasource validation should happen outside this component and can pass in an undefined if invalid If query level validation is done and a query datasource is invalid, pass in disabled = true */ export function ExploreRunQueryButton({ rootDatasourceUid, queries, disabled = false, variant = 'secondary', onClick, changeDatasource, setQueries, }: Props) { const [openRunQueryButton, setOpenRunQueryButton] = useState(false); const isPaneSplit = useSelector(isSplit); const exploreActiveDS = useSelector(selectExploreDSMaps); const panesEntries = useSelector(selectPanesEntries); const isDifferentDatasource = (uid: string, exploreId: string) => !exploreActiveDS.dsToExplore.find((di) => di.datasource.uid === uid)?.exploreIds.includes(exploreId); // exploreId on where the query will be ran, and the datasource ID for the item's DS const runQueryText = (exploreId: string, dsUid?: string) => { // if the datasource or exploreID is undefined, it will be disabled, but give it default query button text return dsUid !== undefined && exploreId !== undefined && isDifferentDatasource(dsUid, exploreId) ? { fallbackText: 'Switch data source and run query', translation: t('explore.run-query.switch-datasource-button', 'Switch data source and run query'), } : { fallbackText: 'Run query', translation: t('explore.run-query.run-query-button', 'Run query'), }; }; const runQuery = async (exploreId: string) => { const differentDataSource = isDifferentDatasource(rootDatasourceUid!, exploreId); if (differentDataSource) { await changeDatasource({ exploreId, datasource: rootDatasourceUid! }); } setQueries(exploreId, queries); reportInteraction('grafana_explore_query_history_run', { queryHistoryEnabled: config.queryHistoryEnabled, differentDataSource, }); }; const runButton = () => { const isInvalid = disabled || queries.length === 0 || rootDatasourceUid === undefined; if (!isPaneSplit) { const exploreId = exploreActiveDS.exploreToDS[0]?.exploreId; // may be undefined if explore is refreshed while the pane is up const buttonText = runQueryText(exploreId, rootDatasourceUid); return ( ); } else { const menu = ( {panesEntries.map((pane, i) => { const buttonText = runQueryText(pane[0], rootDatasourceUid); const paneLabel = i === 0 ? t('explore.run-query.left-pane', 'Left pane') : t('explore.run-query.right-pane', 'Right pane'); return ( { runQuery(pane[0]); onClick?.(); }} label={`${paneLabel}: ${buttonText.translation}`} disabled={isInvalid || pane[0] === undefined} /> ); })} ); return ( setOpenRunQueryButton(state)} placement="bottom-start" overlay={menu}> {t('explore.run-query.run-query-button', 'Run query')} ); } }; return <>{runButton()}; } export default connector(ExploreRunQueryButton);