QueryVariableEditor.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import { css } from '@emotion/css';
  2. import React, { FormEvent, PureComponent } from 'react';
  3. import { connect, ConnectedProps } from 'react-redux';
  4. import { DataSourceInstanceSettings, getDataSourceRef, LoadingState, SelectableValue } from '@grafana/data';
  5. import { selectors } from '@grafana/e2e-selectors';
  6. import { DataSourcePicker, getTemplateSrv } from '@grafana/runtime';
  7. import { InlineField, InlineFieldRow, VerticalGroup } from '@grafana/ui';
  8. import { StoreState } from '../../../types';
  9. import { getTimeSrv } from '../../dashboard/services/TimeSrv';
  10. import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
  11. import { VariableSectionHeader } from '../editor/VariableSectionHeader';
  12. import { VariableTextField } from '../editor/VariableTextField';
  13. import { initialVariableEditorState } from '../editor/reducer';
  14. import { getQueryVariableEditorState } from '../editor/selectors';
  15. import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
  16. import { isLegacyQueryEditor, isQueryEditor } from '../guard';
  17. import { changeVariableMultiValue } from '../state/actions';
  18. import { getVariablesState } from '../state/selectors';
  19. import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../types';
  20. import { toKeyedVariableIdentifier } from '../utils';
  21. import { QueryVariableRefreshSelect } from './QueryVariableRefreshSelect';
  22. import { QueryVariableSortSelect } from './QueryVariableSortSelect';
  23. import { changeQueryVariableDataSource, changeQueryVariableQuery, initQueryVariableEditor } from './actions';
  24. const mapStateToProps = (state: StoreState, ownProps: OwnProps) => {
  25. const { rootStateKey } = ownProps.variable;
  26. if (!rootStateKey) {
  27. console.error('QueryVariableEditor: variable has no rootStateKey');
  28. return {
  29. extended: getQueryVariableEditorState(initialVariableEditorState),
  30. };
  31. }
  32. const { editor } = getVariablesState(rootStateKey, state);
  33. return {
  34. extended: getQueryVariableEditorState(editor),
  35. };
  36. };
  37. const mapDispatchToProps = {
  38. initQueryVariableEditor,
  39. changeQueryVariableDataSource,
  40. changeQueryVariableQuery,
  41. changeVariableMultiValue,
  42. };
  43. const connector = connect(mapStateToProps, mapDispatchToProps);
  44. export interface OwnProps extends VariableEditorProps<QueryVariableModel> {}
  45. export type Props = OwnProps & ConnectedProps<typeof connector>;
  46. export interface State {
  47. regex: string | null;
  48. tagsQuery: string | null;
  49. tagValuesQuery: string | null;
  50. }
  51. export class QueryVariableEditorUnConnected extends PureComponent<Props, State> {
  52. state: State = {
  53. regex: null,
  54. tagsQuery: null,
  55. tagValuesQuery: null,
  56. };
  57. async componentDidMount() {
  58. await this.props.initQueryVariableEditor(toKeyedVariableIdentifier(this.props.variable));
  59. }
  60. componentDidUpdate(prevProps: Readonly<Props>): void {
  61. if (prevProps.variable.datasource !== this.props.variable.datasource) {
  62. this.props.changeQueryVariableDataSource(
  63. toKeyedVariableIdentifier(this.props.variable),
  64. this.props.variable.datasource
  65. );
  66. }
  67. }
  68. onDataSourceChange = (dsSettings: DataSourceInstanceSettings) => {
  69. this.props.onPropChange({
  70. propName: 'datasource',
  71. propValue: dsSettings.isDefault ? null : getDataSourceRef(dsSettings),
  72. });
  73. };
  74. onLegacyQueryChange = async (query: any, definition: string) => {
  75. if (this.props.variable.query !== query) {
  76. this.props.changeQueryVariableQuery(toKeyedVariableIdentifier(this.props.variable), query, definition);
  77. }
  78. };
  79. onQueryChange = async (query: any) => {
  80. if (this.props.variable.query !== query) {
  81. let definition = '';
  82. if (query && query.hasOwnProperty('query') && typeof query.query === 'string') {
  83. definition = query.query;
  84. }
  85. this.props.changeQueryVariableQuery(toKeyedVariableIdentifier(this.props.variable), query, definition);
  86. }
  87. };
  88. onRegExChange = (event: FormEvent<HTMLInputElement>) => {
  89. this.setState({ regex: event.currentTarget.value });
  90. };
  91. onRegExBlur = async (event: FormEvent<HTMLInputElement>) => {
  92. const regex = event.currentTarget.value;
  93. if (this.props.variable.regex !== regex) {
  94. this.props.onPropChange({ propName: 'regex', propValue: regex, updateOptions: true });
  95. }
  96. };
  97. onRefreshChange = (option: SelectableValue<VariableRefresh>) => {
  98. this.props.onPropChange({ propName: 'refresh', propValue: option.value });
  99. };
  100. onSortChange = async (option: SelectableValue<VariableSort>) => {
  101. this.props.onPropChange({ propName: 'sort', propValue: option.value, updateOptions: true });
  102. };
  103. onSelectionOptionsChange = async ({ propValue, propName }: OnPropChangeArguments<VariableWithMultiSupport>) => {
  104. this.props.onPropChange({ propName, propValue, updateOptions: true });
  105. };
  106. renderQueryEditor = () => {
  107. const { extended, variable } = this.props;
  108. if (!extended || !extended.dataSource || !extended.VariableQueryEditor) {
  109. return null;
  110. }
  111. const query = variable.query;
  112. const datasource = extended.dataSource;
  113. const VariableQueryEditor = extended.VariableQueryEditor;
  114. if (isLegacyQueryEditor(VariableQueryEditor, datasource)) {
  115. return (
  116. <VariableQueryEditor
  117. datasource={datasource}
  118. query={query}
  119. templateSrv={getTemplateSrv()}
  120. onChange={this.onLegacyQueryChange}
  121. />
  122. );
  123. }
  124. const range = getTimeSrv().timeRange();
  125. if (isQueryEditor(VariableQueryEditor, datasource)) {
  126. return (
  127. <VariableQueryEditor
  128. datasource={datasource}
  129. query={query}
  130. onChange={this.onQueryChange}
  131. onRunQuery={() => {}}
  132. data={{ series: [], state: LoadingState.Done, timeRange: range }}
  133. range={range}
  134. onBlur={() => {}}
  135. history={[]}
  136. />
  137. );
  138. }
  139. return null;
  140. };
  141. render() {
  142. return (
  143. <VerticalGroup spacing="xs">
  144. <VariableSectionHeader name="Query Options" />
  145. <VerticalGroup spacing="lg">
  146. <VerticalGroup spacing="none">
  147. <InlineFieldRow>
  148. <InlineField label="Data source" labelWidth={20} htmlFor="data-source-picker">
  149. <DataSourcePicker
  150. current={this.props.variable.datasource}
  151. onChange={this.onDataSourceChange}
  152. variables={true}
  153. />
  154. </InlineField>
  155. <QueryVariableRefreshSelect onChange={this.onRefreshChange} refresh={this.props.variable.refresh} />
  156. </InlineFieldRow>
  157. <div
  158. className={css`
  159. flex-direction: column;
  160. width: 100%;
  161. `}
  162. >
  163. {this.renderQueryEditor()}
  164. </div>
  165. <VariableTextField
  166. value={this.state.regex ?? this.props.variable.regex}
  167. name="Regex"
  168. placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
  169. onChange={this.onRegExChange}
  170. onBlur={this.onRegExBlur}
  171. labelWidth={20}
  172. interactive={true}
  173. tooltip={
  174. <div>
  175. Optional, if you want to extract part of a series name or metric node segment. Named capture groups
  176. can be used to separate the display text and value (
  177. <a
  178. className="external-link"
  179. href="https://grafana.com/docs/grafana/latest/variables/filter-variables-with-regex#filter-and-modify-using-named-text-and-value-capture-groups"
  180. target="__blank"
  181. >
  182. see examples
  183. </a>
  184. ).
  185. </div>
  186. }
  187. testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}
  188. grow
  189. />
  190. <QueryVariableSortSelect onChange={this.onSortChange} sort={this.props.variable.sort} />
  191. </VerticalGroup>
  192. <SelectionOptionsEditor
  193. variable={this.props.variable}
  194. onPropChange={this.onSelectionOptionsChange}
  195. onMultiChanged={this.props.changeVariableMultiValue}
  196. />
  197. </VerticalGroup>
  198. </VerticalGroup>
  199. );
  200. }
  201. }
  202. export const QueryVariableEditor = connector(QueryVariableEditorUnConnected);