grafana/public/app/features/variables/pickers/OptionsPicker/OptionsPicker.tsx
Hugo Häggmark 112a755e18
Variables: Adds new Api that allows proper QueryEditors for Query variables (#28217)
* Initial

* WIP

* wip

* Refactor: fixing types

* Refactor: Fixed more typings

* Feature: Moves TestData to new API

* Feature: Moves CloudMonitoringDatasource to new API

* Feature: Moves PrometheusDatasource to new Variables API

* Refactor: Clean up comments

* Refactor: changes to QueryEditorProps instead

* Refactor: cleans up testdata, prometheus and cloud monitoring variable support

* Refactor: adds variableQueryRunner

* Refactor: adds props to VariableQueryEditor

* Refactor: reverted Loki editor

* Refactor: refactor queryrunner into smaller pieces

* Refactor: adds upgrade query thunk

* Tests: Updates old tests

* Docs: fixes build errors for exported api

* Tests: adds guard tests

* Tests: adds QueryRunner tests

* Tests: fixes broken tests

* Tests: adds variableQueryObserver tests

* Test: adds tests for operator functions

* Test: adds VariableQueryRunner tests

* Refactor: renames dataSource

* Refactor: adds definition for standard variable support

* Refactor: adds cancellation to OptionPicker

* Refactor: changes according to Dominiks suggestion

* Refactor:tt

* Refactor: adds tests for factories

* Refactor: restructuring a bit

* Refactor: renames variableQueryRunner.ts

* Refactor: adds quick exit when runRequest returns errors

* Refactor: using TextArea from grafana/ui

* Refactor: changed from interfaces to classes instead

* Tests: fixes broken test

* Docs: fixes doc issue count

* Docs: fixes doc issue count

* Refactor: Adds check for self referencing queries

* Tests: fixed unused variable

* Refactor: Changes comments
2020-11-18 15:10:32 +01:00

141 lines
4.6 KiB
TypeScript

import React, { PureComponent } from 'react';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import { ClickOutsideWrapper } from '@grafana/ui';
import { LoadingState } from '@grafana/data';
import { StoreState } from 'app/types';
import { VariableLink } from '../shared/VariableLink';
import { VariableInput } from '../shared/VariableInput';
import { commitChangesToVariable, filterOrSearchOptions, navigateOptions, toggleAndFetchTag } from './actions';
import { OptionsPickerState, showOptions, toggleAllOptions, toggleOption } from './reducer';
import { VariableOption, VariableTag, VariableWithMultiSupport, VariableWithOptions } from '../../types';
import { VariableOptions } from '../shared/VariableOptions';
import { isQuery } from '../../guard';
import { VariablePickerProps } from '../types';
import { formatVariableLabel } from '../../shared/formatVariable';
import { toVariableIdentifier } from '../../state/types';
import { getVariableQueryRunner } from '../../query/VariableQueryRunner';
interface OwnProps extends VariablePickerProps<VariableWithMultiSupport> {}
interface ConnectedProps {
picker: OptionsPickerState;
}
interface DispatchProps {
showOptions: typeof showOptions;
commitChangesToVariable: typeof commitChangesToVariable;
toggleAllOptions: typeof toggleAllOptions;
toggleOption: typeof toggleOption;
toggleAndFetchTag: typeof toggleAndFetchTag;
filterOrSearchOptions: typeof filterOrSearchOptions;
navigateOptions: typeof navigateOptions;
}
type Props = OwnProps & ConnectedProps & DispatchProps;
export class OptionsPickerUnconnected extends PureComponent<Props> {
onShowOptions = () => this.props.showOptions(this.props.variable);
onHideOptions = () => this.props.commitChangesToVariable();
onToggleOption = (option: VariableOption, clearOthers: boolean) => {
const toggleFunc = this.props.variable.multi ? this.onToggleMultiValueVariable : this.onToggleSingleValueVariable;
toggleFunc(option, clearOthers);
};
onToggleSingleValueVariable = (option: VariableOption, clearOthers: boolean) => {
this.props.toggleOption({ option, clearOthers, forceSelect: false });
this.onHideOptions();
};
onToggleMultiValueVariable = (option: VariableOption, clearOthers: boolean) => {
this.props.toggleOption({ option, clearOthers, forceSelect: false });
};
render() {
const { variable, picker } = this.props;
const showOptions = picker.id === variable.id;
return (
<div className="variable-link-wrapper">
{this.renderLink(showOptions, variable)}
{this.renderOptions(showOptions, picker)}
</div>
);
}
renderLink(showOptions: boolean, variable: VariableWithMultiSupport) {
if (showOptions) {
return null;
}
const linkText = formatVariableLabel(variable);
const tags = getSelectedTags(variable);
const loading = variable.state === LoadingState.Loading;
return (
<VariableLink
text={linkText}
tags={tags}
onClick={this.onShowOptions}
loading={loading}
onCancel={this.onCancel}
/>
);
}
onCancel = () => {
getVariableQueryRunner().cancelRequest(toVariableIdentifier(this.props.variable));
};
renderOptions(showOptions: boolean, picker: OptionsPickerState) {
if (!showOptions) {
return null;
}
return (
<ClickOutsideWrapper onClick={this.onHideOptions}>
<VariableInput
value={picker.queryValue}
onChange={this.props.filterOrSearchOptions}
onNavigate={this.props.navigateOptions}
/>
<VariableOptions
values={picker.options}
onToggle={this.onToggleOption}
onToggleAll={this.props.toggleAllOptions}
onToggleTag={this.props.toggleAndFetchTag}
highlightIndex={picker.highlightIndex}
multi={picker.multi}
tags={picker.tags}
selectedValues={picker.selectedValues}
/>
</ClickOutsideWrapper>
);
}
}
const getSelectedTags = (variable: VariableWithOptions): VariableTag[] => {
if (!isQuery(variable) || !Array.isArray(variable.tags)) {
return [];
}
return variable.tags.filter(t => t.selected);
};
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
showOptions,
commitChangesToVariable,
filterOrSearchOptions,
toggleAllOptions,
toggleOption,
toggleAndFetchTag,
navigateOptions,
};
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = state => ({
picker: state.templating.optionsPicker,
});
export const OptionsPicker = connect(mapStateToProps, mapDispatchToProps)(OptionsPickerUnconnected);
OptionsPicker.displayName = 'OptionsPicker';