mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Adds ability to remove filter from <AdHocFilterField /> key dropdown (#17553)
Also adds tests to validate behaviour Query now reruns when filter is removed Changes <AdHocFilterField /> such that after a measurement and field have been chosen, just a '+' button is displayed, rather than an empty <AdHocFilter /> Closes #17544 Closes #17497
This commit is contained in:
parent
6809d2bb29
commit
6170a039e5
64
public/app/features/explore/AdHocFilterField.test.tsx
Normal file
64
public/app/features/explore/AdHocFilterField.test.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
|
||||||
|
import { AdHocFilterField, DEFAULT_REMOVE_FILTER_VALUE } from './AdHocFilterField';
|
||||||
|
import { AdHocFilter } from './AdHocFilter';
|
||||||
|
import { MockDataSourceApi } from '../../../test/mocks/datasource_srv';
|
||||||
|
|
||||||
|
describe('<AdHocFilterField />', () => {
|
||||||
|
let mockDataSourceApi;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockDataSourceApi = new MockDataSourceApi();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initially have no filters', () => {
|
||||||
|
const mockOnPairsChanged = jest.fn();
|
||||||
|
const wrapper = shallow(<AdHocFilterField datasource={mockDataSourceApi} onPairsChanged={mockOnPairsChanged} />);
|
||||||
|
expect(wrapper.state('pairs')).toEqual([]);
|
||||||
|
expect(wrapper.find(AdHocFilter).exists()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add <AdHocFilter /> when onAddFilter is invoked', () => {
|
||||||
|
const mockOnPairsChanged = jest.fn();
|
||||||
|
const wrapper = shallow(<AdHocFilterField datasource={mockDataSourceApi} onPairsChanged={mockOnPairsChanged} />);
|
||||||
|
expect(wrapper.state('pairs')).toEqual([]);
|
||||||
|
wrapper
|
||||||
|
.find('button')
|
||||||
|
.first()
|
||||||
|
.simulate('click');
|
||||||
|
expect(wrapper.find(AdHocFilter).exists()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should remove the relavant filter when the '${DEFAULT_REMOVE_FILTER_VALUE}' key is selected`, () => {
|
||||||
|
const mockOnPairsChanged = jest.fn();
|
||||||
|
const wrapper = shallow(<AdHocFilterField datasource={mockDataSourceApi} onPairsChanged={mockOnPairsChanged} />);
|
||||||
|
expect(wrapper.state('pairs')).toEqual([]);
|
||||||
|
|
||||||
|
wrapper
|
||||||
|
.find('button')
|
||||||
|
.first()
|
||||||
|
.simulate('click');
|
||||||
|
expect(wrapper.find(AdHocFilter).exists()).toBeTruthy();
|
||||||
|
|
||||||
|
wrapper.find(AdHocFilter).prop('onKeyChanged')(DEFAULT_REMOVE_FILTER_VALUE);
|
||||||
|
expect(wrapper.find(AdHocFilter).exists()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should call onPairsChanged when a filter is removed', async () => {
|
||||||
|
const mockOnPairsChanged = jest.fn();
|
||||||
|
const wrapper = shallow(<AdHocFilterField datasource={mockDataSourceApi} onPairsChanged={mockOnPairsChanged} />);
|
||||||
|
expect(wrapper.state('pairs')).toEqual([]);
|
||||||
|
|
||||||
|
wrapper
|
||||||
|
.find('button')
|
||||||
|
.first()
|
||||||
|
.simulate('click');
|
||||||
|
expect(wrapper.find(AdHocFilter).exists()).toBeTruthy();
|
||||||
|
|
||||||
|
wrapper.find(AdHocFilter).prop('onKeyChanged')(DEFAULT_REMOVE_FILTER_VALUE);
|
||||||
|
expect(wrapper.find(AdHocFilter).exists()).toBeFalsy();
|
||||||
|
|
||||||
|
expect(mockOnPairsChanged.mock.calls.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
@ -2,6 +2,8 @@ import React from 'react';
|
|||||||
import { DataSourceApi, DataQuery, DataSourceJsonData } from '@grafana/ui';
|
import { DataSourceApi, DataQuery, DataSourceJsonData } from '@grafana/ui';
|
||||||
import { AdHocFilter } from './AdHocFilter';
|
import { AdHocFilter } from './AdHocFilter';
|
||||||
|
|
||||||
|
export const DEFAULT_REMOVE_FILTER_VALUE = '-- remove filter --';
|
||||||
|
|
||||||
export interface KeyValuePair {
|
export interface KeyValuePair {
|
||||||
keys: string[];
|
keys: string[];
|
||||||
key: string;
|
key: string;
|
||||||
@ -25,14 +27,8 @@ export class AdHocFilterField<
|
|||||||
> extends React.PureComponent<Props<TQuery, TOptions>, State> {
|
> extends React.PureComponent<Props<TQuery, TOptions>, State> {
|
||||||
state: State = { pairs: [] };
|
state: State = { pairs: [] };
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
const tagKeys = this.props.datasource.getTagKeys ? await this.props.datasource.getTagKeys({}) : [];
|
|
||||||
const keys = tagKeys.map(tagKey => tagKey.text);
|
|
||||||
const pairs = [{ key: null, operator: null, value: null, keys, values: [] }];
|
|
||||||
this.setState({ pairs });
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyChanged = (index: number) => async (key: string) => {
|
onKeyChanged = (index: number) => async (key: string) => {
|
||||||
|
if (key !== DEFAULT_REMOVE_FILTER_VALUE) {
|
||||||
const { datasource, onPairsChanged } = this.props;
|
const { datasource, onPairsChanged } = this.props;
|
||||||
const tagValues = datasource.getTagValues ? await datasource.getTagValues({ key }) : [];
|
const tagValues = datasource.getTagValues ? await datasource.getTagValues({ key }) : [];
|
||||||
const values = tagValues.map(tagValue => tagValue.text);
|
const values = tagValues.map(tagValue => tagValue.text);
|
||||||
@ -40,6 +36,9 @@ export class AdHocFilterField<
|
|||||||
|
|
||||||
this.setState({ pairs: newPairs });
|
this.setState({ pairs: newPairs });
|
||||||
onPairsChanged(newPairs);
|
onPairsChanged(newPairs);
|
||||||
|
} else {
|
||||||
|
this.onRemoveFilter(index);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onValueChanged = (index: number) => (value: string) => {
|
onValueChanged = (index: number) => (value: string) => {
|
||||||
@ -75,6 +74,7 @@ export class AdHocFilterField<
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
this.setState({ pairs: newPairs });
|
this.setState({ pairs: newPairs });
|
||||||
|
this.props.onPairsChanged(newPairs);
|
||||||
};
|
};
|
||||||
|
|
||||||
private updatePairAt = (index: number, pair: Partial<KeyValuePair>) => {
|
private updatePairAt = (index: number, pair: Partial<KeyValuePair>) => {
|
||||||
@ -104,12 +104,17 @@ export class AdHocFilterField<
|
|||||||
const { pairs } = this.state;
|
const { pairs } = this.state;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{pairs.length < 1 && (
|
||||||
|
<button className="gf-form-label gf-form-label--btn query-part" onClick={this.onAddFilter}>
|
||||||
|
<i className="fa fa-plus" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{pairs.map((pair, index) => {
|
{pairs.map((pair, index) => {
|
||||||
const adHocKey = `adhoc-filter-${index}-${pair.key}-${pair.value}`;
|
const adHocKey = `adhoc-filter-${index}-${pair.key}-${pair.value}`;
|
||||||
return (
|
return (
|
||||||
<div className="align-items-center flex-grow-1" key={adHocKey}>
|
<div className="align-items-center flex-grow-1" key={adHocKey}>
|
||||||
<AdHocFilter
|
<AdHocFilter
|
||||||
keys={pair.keys}
|
keys={[DEFAULT_REMOVE_FILTER_VALUE].concat(pair.keys)}
|
||||||
values={pair.values}
|
values={pair.values}
|
||||||
initialKey={pair.key}
|
initialKey={pair.key}
|
||||||
initialOperator={pair.operator}
|
initialOperator={pair.operator}
|
||||||
|
Loading…
Reference in New Issue
Block a user