mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Templating: Adds -- remove filter -- back to incomplete AdHoc filters (#26829)
This commit is contained in:
parent
9a694b2b5a
commit
0a40862af5
@ -1,77 +1,63 @@
|
||||
import React, { FC, ReactElement, useState } from 'react';
|
||||
import { Icon, SegmentAsync } from '@grafana/ui';
|
||||
import { OperatorSegment } from './OperatorSegment';
|
||||
import React, { FC, useCallback, useState } from 'react';
|
||||
import { AdHocVariableFilter } from 'app/features/variables/types';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { AdHocFilterKey, REMOVE_FILTER_KEY } from './AdHocFilterKey';
|
||||
import { AdHocFilterRenderer } from './AdHocFilterRenderer';
|
||||
|
||||
interface Props {
|
||||
onLoadKeys: () => Promise<Array<SelectableValue<string>>>;
|
||||
onLoadValues: (key: string) => Promise<Array<SelectableValue<string>>>;
|
||||
datasource: string;
|
||||
onCompleted: (filter: AdHocVariableFilter) => void;
|
||||
appendBefore?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const AdHocFilterBuilder: FC<Props> = ({ appendBefore, onCompleted, onLoadKeys, onLoadValues }) => {
|
||||
export const AdHocFilterBuilder: FC<Props> = ({ datasource, appendBefore, onCompleted }) => {
|
||||
const [key, setKey] = useState<string | null>(null);
|
||||
const [operator, setOperator] = useState<string>('=');
|
||||
|
||||
if (key === null) {
|
||||
return (
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-key"
|
||||
Component={filterAddButton(key)}
|
||||
value={key}
|
||||
onChange={({ value }) => setKey(value ?? '')}
|
||||
loadOptions={onLoadKeys}
|
||||
/>
|
||||
</div>
|
||||
const onKeyChanged = useCallback(
|
||||
(item: SelectableValue<string | null>) => {
|
||||
if (item.value !== REMOVE_FILTER_KEY) {
|
||||
setKey(item.value ?? '');
|
||||
return;
|
||||
}
|
||||
setKey(null);
|
||||
},
|
||||
[setKey]
|
||||
);
|
||||
|
||||
const onOperatorChanged = useCallback((item: SelectableValue<string>) => setOperator(item.value ?? ''), [
|
||||
setOperator,
|
||||
]);
|
||||
|
||||
const onValueChanged = useCallback(
|
||||
(item: SelectableValue<string>) => {
|
||||
onCompleted({
|
||||
value: item.value ?? '',
|
||||
operator: operator,
|
||||
condition: '',
|
||||
key: key!,
|
||||
});
|
||||
setKey(null);
|
||||
setOperator('=');
|
||||
},
|
||||
[onCompleted, key, setKey, setOperator]
|
||||
);
|
||||
|
||||
if (key === null) {
|
||||
return <AdHocFilterKey datasource={datasource} filterKey={key} onChange={onKeyChanged} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment key="filter-builder">
|
||||
{appendBefore}
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-key"
|
||||
value={key}
|
||||
onChange={({ value }) => setKey(value ?? '')}
|
||||
loadOptions={onLoadKeys}
|
||||
<AdHocFilterRenderer
|
||||
datasource={datasource}
|
||||
filter={{ key, value: '', operator, condition: '' }}
|
||||
placeHolder="select value"
|
||||
onKeyChange={onKeyChanged}
|
||||
onOperatorChange={onOperatorChanged}
|
||||
onValueChange={onValueChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<OperatorSegment value={operator} onChange={({ value }) => setOperator(value ?? '')} />
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-value"
|
||||
placeholder="select value"
|
||||
onChange={({ value }) => {
|
||||
onCompleted({
|
||||
value: value ?? '',
|
||||
operator: operator,
|
||||
condition: '',
|
||||
key: key,
|
||||
});
|
||||
setKey(null);
|
||||
setOperator('=');
|
||||
}}
|
||||
loadOptions={() => onLoadValues(key)}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
function filterAddButton(key: string | null): ReactElement | undefined {
|
||||
if (key !== null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<a className="gf-form-label query-part">
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
import React, { FC, ReactElement } from 'react';
|
||||
import { Icon, SegmentAsync } from '@grafana/ui';
|
||||
import { getDatasourceSrv } from '../../../plugins/datasource_srv';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
interface Props {
|
||||
datasource: string;
|
||||
filterKey: string | null;
|
||||
onChange: (item: SelectableValue<string | null>) => void;
|
||||
}
|
||||
|
||||
export const AdHocFilterKey: FC<Props> = ({ datasource, onChange, filterKey }) => {
|
||||
const loadKeys = () => fetchFilterKeys(datasource);
|
||||
const loadKeysWithRemove = () => fetchFilterKeysWithRemove(datasource);
|
||||
|
||||
if (filterKey === null) {
|
||||
return (
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-key"
|
||||
Component={plusSegment}
|
||||
value={filterKey}
|
||||
onChange={onChange}
|
||||
loadOptions={loadKeys}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-key"
|
||||
value={filterKey}
|
||||
onChange={onChange}
|
||||
loadOptions={loadKeysWithRemove}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const REMOVE_FILTER_KEY = '-- remove filter --';
|
||||
const REMOVE_VALUE = { label: REMOVE_FILTER_KEY, value: REMOVE_FILTER_KEY };
|
||||
|
||||
const plusSegment: ReactElement = (
|
||||
<a className="gf-form-label query-part">
|
||||
<Icon name="plus" />
|
||||
</a>
|
||||
);
|
||||
|
||||
const fetchFilterKeys = async (datasource: string): Promise<Array<SelectableValue<string>>> => {
|
||||
const ds = await getDatasourceSrv().get(datasource);
|
||||
|
||||
if (!ds || !ds.getTagKeys) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const metrics = await ds.getTagKeys();
|
||||
return metrics.map(m => ({ label: m.text, value: m.text }));
|
||||
};
|
||||
|
||||
const fetchFilterKeysWithRemove = async (datasource: string): Promise<Array<SelectableValue<string>>> => {
|
||||
const keys = await fetchFilterKeys(datasource);
|
||||
return [REMOVE_VALUE, ...keys];
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
import React, { FC } from 'react';
|
||||
import { OperatorSegment } from './OperatorSegment';
|
||||
import { AdHocVariableFilter } from 'app/features/variables/types';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { AdHocFilterKey } from './AdHocFilterKey';
|
||||
import { AdHocFilterValue } from './AdHocFilterValue';
|
||||
|
||||
interface Props {
|
||||
datasource: string;
|
||||
filter: AdHocVariableFilter;
|
||||
onKeyChange: (item: SelectableValue<string | null>) => void;
|
||||
onOperatorChange: (item: SelectableValue<string>) => void;
|
||||
onValueChange: (item: SelectableValue<string>) => void;
|
||||
placeHolder?: string;
|
||||
}
|
||||
|
||||
export const AdHocFilterRenderer: FC<Props> = ({
|
||||
datasource,
|
||||
filter: { key, operator, value },
|
||||
onKeyChange,
|
||||
onOperatorChange,
|
||||
onValueChange,
|
||||
placeHolder,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<AdHocFilterKey datasource={datasource} filterKey={key} onChange={onKeyChange} />
|
||||
<div className="gf-form">
|
||||
<OperatorSegment value={operator} onChange={onOperatorChange} />
|
||||
</div>
|
||||
<AdHocFilterValue
|
||||
datasource={datasource}
|
||||
filterKey={key}
|
||||
filterValue={value}
|
||||
onChange={onValueChange}
|
||||
placeHolder={placeHolder}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
import React, { FC } from 'react';
|
||||
import { SegmentAsync } from '@grafana/ui';
|
||||
import { getDatasourceSrv } from '../../../plugins/datasource_srv';
|
||||
import { MetricFindValue, SelectableValue } from '@grafana/data';
|
||||
|
||||
interface Props {
|
||||
datasource: string;
|
||||
filterKey: string;
|
||||
filterValue: string | null;
|
||||
onChange: (item: SelectableValue<string>) => void;
|
||||
placeHolder?: string;
|
||||
}
|
||||
|
||||
export const AdHocFilterValue: FC<Props> = ({ datasource, onChange, filterKey, filterValue, placeHolder }) => {
|
||||
const loadValues = () => fetchFilterValues(datasource, filterKey);
|
||||
|
||||
return (
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-value"
|
||||
placeholder={placeHolder}
|
||||
value={filterValue}
|
||||
onChange={onChange}
|
||||
loadOptions={loadValues}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const fetchFilterValues = async (datasource: string, key: string): Promise<Array<SelectableValue<string>>> => {
|
||||
const ds = await getDatasourceSrv().get(datasource);
|
||||
|
||||
if (!ds || !ds.getTagValues) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const metrics = await ds.getTagValues({ key });
|
||||
return metrics.map((m: MetricFindValue) => ({ label: m.text, value: m.text }));
|
||||
};
|
@ -2,14 +2,13 @@ import React, { PureComponent, ReactNode } from 'react';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { StoreState } from 'app/types';
|
||||
import { AdHocVariableFilter, AdHocVariableModel } from 'app/features/variables/types';
|
||||
import { SegmentAsync } from '@grafana/ui';
|
||||
import { VariablePickerProps } from '../../pickers/types';
|
||||
import { OperatorSegment } from './OperatorSegment';
|
||||
import { MetricFindValue, SelectableValue } from '@grafana/data';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { AdHocFilterBuilder } from './AdHocFilterBuilder';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { ConditionSegment } from './ConditionSegment';
|
||||
import { addFilter, changeFilter, removeFilter } from '../actions';
|
||||
import { REMOVE_FILTER_KEY } from './AdHocFilterKey';
|
||||
import { AdHocFilterRenderer } from './AdHocFilterRenderer';
|
||||
|
||||
interface OwnProps extends VariablePickerProps<AdHocVariableModel> {}
|
||||
|
||||
@ -23,8 +22,6 @@ interface DispatchProps {
|
||||
|
||||
type Props = OwnProps & ConnectedProps & DispatchProps;
|
||||
|
||||
const REMOVE_FILTER_KEY = '-- remove filter --';
|
||||
const REMOVE_VALUE = { label: REMOVE_FILTER_KEY, value: REMOVE_FILTER_KEY };
|
||||
export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
onChange = (index: number, prop: string) => (key: SelectableValue<string>) => {
|
||||
const { id, filters } = this.props.variable;
|
||||
@ -48,35 +45,6 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
this.props.addFilter(id, filter);
|
||||
};
|
||||
|
||||
fetchFilterKeys = async () => {
|
||||
const { variable } = this.props;
|
||||
const ds = await getDatasourceSrv().get(variable.datasource!);
|
||||
|
||||
if (!ds || !ds.getTagKeys) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const metrics = await ds.getTagKeys();
|
||||
return metrics.map(m => ({ label: m.text, value: m.text }));
|
||||
};
|
||||
|
||||
fetchFilterKeysWithRemove = async () => {
|
||||
const keys = await this.fetchFilterKeys();
|
||||
return [REMOVE_VALUE, ...keys];
|
||||
};
|
||||
|
||||
fetchFilterValues = async (key: string) => {
|
||||
const { variable } = this.props;
|
||||
const ds = await getDatasourceSrv().get(variable.datasource!);
|
||||
|
||||
if (!ds || !ds.getTagValues) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const metrics = await ds.getTagValues({ key });
|
||||
return metrics.map((m: MetricFindValue) => ({ label: m.text, value: m.text }));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { filters } = this.props.variable;
|
||||
|
||||
@ -84,9 +52,8 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
<div className="gf-form-inline">
|
||||
{this.renderFilters(filters)}
|
||||
<AdHocFilterBuilder
|
||||
datasource={this.props.variable.datasource!}
|
||||
appendBefore={filters.length > 0 ? <ConditionSegment label="AND" /> : null}
|
||||
onLoadKeys={this.fetchFilterKeys}
|
||||
onLoadValues={this.fetchFilterValues}
|
||||
onCompleted={this.appendFilterToVariable}
|
||||
/>
|
||||
</div>
|
||||
@ -96,7 +63,7 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
renderFilters(filters: AdHocVariableFilter[]) {
|
||||
return filters.reduce((segments: ReactNode[], filter, index) => {
|
||||
if (segments.length > 0) {
|
||||
segments.push(<ConditionSegment label="AND" />);
|
||||
segments.push(<ConditionSegment label="AND" key={`condition-${index}`} />);
|
||||
}
|
||||
segments.push(this.renderFilterSegments(filter, index));
|
||||
return segments;
|
||||
@ -106,25 +73,13 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
renderFilterSegments(filter: AdHocVariableFilter, index: number) {
|
||||
return (
|
||||
<React.Fragment key={`filter-${index}`}>
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-key"
|
||||
value={filter.key}
|
||||
onChange={this.onChange(index, 'key')}
|
||||
loadOptions={this.fetchFilterKeysWithRemove}
|
||||
<AdHocFilterRenderer
|
||||
datasource={this.props.variable.datasource!}
|
||||
filter={filter}
|
||||
onKeyChange={this.onChange(index, 'key')}
|
||||
onOperatorChange={this.onChange(index, 'operator')}
|
||||
onValueChange={this.onChange(index, 'value')}
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<OperatorSegment value={filter.operator} onChange={this.onChange(index, 'operator')} />
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<SegmentAsync
|
||||
className="query-segment-value"
|
||||
value={filter.value}
|
||||
onChange={this.onChange(index, 'value')}
|
||||
loadOptions={() => this.fetchFilterValues(filter.key)}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user