import React, { FormEvent, PureComponent } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { DataSourceInstanceSettings, getDataSourceRef, LoadingState, SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { getTemplateSrv } from '@grafana/runtime'; import { Field, Text } from '@grafana/ui'; import { Box } from '@grafana/ui/src/unstable'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { StoreState } from '../../../types'; import { getTimeSrv } from '../../dashboard/services/TimeSrv'; import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor'; import { VariableLegend } from '../editor/VariableLegend'; import { VariableTextAreaField } from '../editor/VariableTextAreaField'; import { initialVariableEditorState } from '../editor/reducer'; import { getQueryVariableEditorState } from '../editor/selectors'; import { OnPropChangeArguments, VariableEditorProps } from '../editor/types'; import { isLegacyQueryEditor, isQueryEditor } from '../guard'; import { changeVariableMultiValue } from '../state/actions'; import { getVariablesState } from '../state/selectors'; import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../types'; import { toKeyedVariableIdentifier } from '../utils'; import { QueryVariableRefreshSelect } from './QueryVariableRefreshSelect'; import { QueryVariableSortSelect } from './QueryVariableSortSelect'; import { changeQueryVariableDataSource, changeQueryVariableQuery, initQueryVariableEditor } from './actions'; const mapStateToProps = (state: StoreState, ownProps: OwnProps) => { const { rootStateKey } = ownProps.variable; if (!rootStateKey) { console.error('QueryVariableEditor: variable has no rootStateKey'); return { extended: getQueryVariableEditorState(initialVariableEditorState), }; } const { editor } = getVariablesState(rootStateKey, state); return { extended: getQueryVariableEditorState(editor), }; }; const mapDispatchToProps = { initQueryVariableEditor, changeQueryVariableDataSource, changeQueryVariableQuery, changeVariableMultiValue, }; const connector = connect(mapStateToProps, mapDispatchToProps); export interface OwnProps extends VariableEditorProps {} export type Props = OwnProps & ConnectedProps; export interface State { regex: string | null; tagsQuery: string | null; tagValuesQuery: string | null; } export class QueryVariableEditorUnConnected extends PureComponent { state: State = { regex: null, tagsQuery: null, tagValuesQuery: null, }; async componentDidMount() { await this.props.initQueryVariableEditor(toKeyedVariableIdentifier(this.props.variable)); } componentDidUpdate(prevProps: Readonly): void { if (prevProps.variable.datasource !== this.props.variable.datasource) { this.props.changeQueryVariableDataSource( toKeyedVariableIdentifier(this.props.variable), this.props.variable.datasource ); } } onDataSourceChange = (dsSettings: DataSourceInstanceSettings) => { this.props.onPropChange({ propName: 'datasource', propValue: dsSettings.isDefault ? null : getDataSourceRef(dsSettings), }); }; onLegacyQueryChange = async (query: any, definition: string) => { if (this.props.variable.query !== query) { this.props.changeQueryVariableQuery(toKeyedVariableIdentifier(this.props.variable), query, definition); } }; onQueryChange = async (query: any) => { if (this.props.variable.query !== query) { let definition = ''; if (query && query.hasOwnProperty('query') && typeof query.query === 'string') { definition = query.query; } this.props.changeQueryVariableQuery(toKeyedVariableIdentifier(this.props.variable), query, definition); } }; onRegExChange = (event: FormEvent) => { this.setState({ regex: event.currentTarget.value }); }; onRegExBlur = async (event: FormEvent) => { const regex = event.currentTarget.value; if (this.props.variable.regex !== regex) { this.props.onPropChange({ propName: 'regex', propValue: regex, updateOptions: true }); } }; onRefreshChange = (option: VariableRefresh) => { this.props.onPropChange({ propName: 'refresh', propValue: option }); }; onSortChange = async (option: SelectableValue) => { this.props.onPropChange({ propName: 'sort', propValue: option.value, updateOptions: true }); }; onSelectionOptionsChange = async ({ propValue, propName }: OnPropChangeArguments) => { this.props.onPropChange({ propName, propValue, updateOptions: true }); }; renderQueryEditor = () => { const { extended, variable } = this.props; if (!extended || !extended.dataSource || !extended.VariableQueryEditor) { return null; } const datasource = extended.dataSource; const VariableQueryEditor = extended.VariableQueryEditor; let query = variable.query; if (typeof query === 'string') { query = query || (datasource.variables?.getDefaultQuery?.() ?? ''); } else { query = { ...datasource.variables?.getDefaultQuery?.(), ...variable.query, }; } if (isLegacyQueryEditor(VariableQueryEditor, datasource)) { return ( Query ); } const range = getTimeSrv().timeRange(); if (isQueryEditor(VariableQueryEditor, datasource)) { return ( Query {}} data={{ series: [], state: LoadingState.Done, timeRange: range }} range={range} onBlur={() => {}} history={[]} /> ); } return null; }; render() { return ( <> Query options {this.renderQueryEditor()} Optional, if you want to extract part of a series name or metric node segment.
Named capture groups can be used to separate the display text and value ( see examples ). } placeholder="/.*-(?.*)-(?.*)-.*/" onChange={this.onRegExChange} onBlur={this.onRegExBlur} testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2} width={52} /> Selection options ); } } export const QueryVariableEditor = connector(QueryVariableEditorUnConnected);