Commit 6170a039 by kay delaney Committed by GitHub

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
parent 6809d2bb
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 { AdHocFilter } from './AdHocFilter';
export const DEFAULT_REMOVE_FILTER_VALUE = '-- remove filter --';
export interface KeyValuePair {
keys: string[];
key: string;
......@@ -25,21 +27,18 @@ export class AdHocFilterField<
> extends React.PureComponent<Props<TQuery, TOptions>, State> {
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) => {
const { datasource, onPairsChanged } = this.props;
const tagValues = datasource.getTagValues ? await datasource.getTagValues({ key }) : [];
const values = tagValues.map(tagValue => tagValue.text);
const newPairs = this.updatePairAt(index, { key, values });
this.setState({ pairs: newPairs });
onPairsChanged(newPairs);
if (key !== DEFAULT_REMOVE_FILTER_VALUE) {
const { datasource, onPairsChanged } = this.props;
const tagValues = datasource.getTagValues ? await datasource.getTagValues({ key }) : [];
const values = tagValues.map(tagValue => tagValue.text);
const newPairs = this.updatePairAt(index, { key, values });
this.setState({ pairs: newPairs });
onPairsChanged(newPairs);
} else {
this.onRemoveFilter(index);
}
};
onValueChanged = (index: number) => (value: string) => {
......@@ -75,6 +74,7 @@ export class AdHocFilterField<
}, []);
this.setState({ pairs: newPairs });
this.props.onPairsChanged(newPairs);
};
private updatePairAt = (index: number, pair: Partial<KeyValuePair>) => {
......@@ -104,12 +104,17 @@ export class AdHocFilterField<
const { pairs } = this.state;
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) => {
const adHocKey = `adhoc-filter-${index}-${pair.key}-${pair.value}`;
return (
<div className="align-items-center flex-grow-1" key={adHocKey}>
<AdHocFilter
keys={pair.keys}
keys={[DEFAULT_REMOVE_FILTER_VALUE].concat(pair.keys)}
values={pair.values}
initialKey={pair.key}
initialOperator={pair.operator}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment