filter FilterBar suggestions by time (according to flag) (#107192) (#107897)

* filter filter bar suggestions by time (according to flag)
add api integration tests for autocomplete apis

* test fix: setDefaultAbsoluteRange

* timeRangeForSuggestionsOverride

* revert

* tests

* doc

* set time range

* Added tests following code review

* eslint

* fun-ctional tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Liza Katz <lizka.k@gmail.com>
This commit is contained in:
Kibana Machine 2021-08-09 12:18:56 -04:00 committed by GitHub
parent 3717f0d47d
commit 5a62b60027
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 427 additions and 57 deletions

View file

@ -39,4 +39,5 @@ export interface QueryStringInputProps
| [size](./kibana-plugin-plugins-data-public.querystringinputprops.size.md) | <code>SuggestionsListSize</code> | |
| [storageKey](./kibana-plugin-plugins-data-public.querystringinputprops.storagekey.md) | <code>string</code> | |
| [submitOnBlur](./kibana-plugin-plugins-data-public.querystringinputprops.submitonblur.md) | <code>boolean</code> | |
| [timeRangeForSuggestionsOverride](./kibana-plugin-plugins-data-public.querystringinputprops.timerangeforsuggestionsoverride.md) | <code>boolean</code> | Override whether autocomplete suggestions are restricted by time range. |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) &gt; [timeRangeForSuggestionsOverride](./kibana-plugin-plugins-data-public.querystringinputprops.timerangeforsuggestionsoverride.md)
## QueryStringInputProps.timeRangeForSuggestionsOverride property
Override whether autocomplete suggestions are restricted by time range.
<b>Signature:</b>
```typescript
timeRangeForSuggestionsOverride?: boolean;
```

View file

@ -7,15 +7,10 @@
*/
import dateMath from '@elastic/datemath';
import { buildQueryFromFilters } from '@kbn/es-query';
import { memoize } from 'lodash';
import { CoreSetup } from 'src/core/public';
import {
IIndexPattern,
IFieldType,
UI_SETTINGS,
buildQueryFromFilters,
ValueSuggestionsMethod,
} from '../../../common';
import { IIndexPattern, IFieldType, UI_SETTINGS, ValueSuggestionsMethod } from '../../../common';
import { TimefilterSetup } from '../../query';
import { AutocompleteUsageCollector } from '../collectors';

View file

@ -1930,6 +1930,7 @@ export interface QueryStringInputProps {
storageKey?: string;
// (undocumented)
submitOnBlur?: boolean;
timeRangeForSuggestionsOverride?: boolean;
}
// @public (undocumented)

View file

@ -37,6 +37,7 @@ interface Props {
indexPatterns: IIndexPattern[];
intl: InjectedIntl;
appName: string;
timeRangeForSuggestionsOverride?: boolean;
}
function FilterBarUI(props: Props) {
@ -65,6 +66,7 @@ function FilterBarUI(props: Props) {
onRemove={() => onRemove(i)}
indexPatterns={props.indexPatterns}
uiSettings={uiSettings!}
timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride}
/>
</EuiFlexItem>
));
@ -112,6 +114,7 @@ function FilterBarUI(props: Props) {
onSubmit={onAdd}
onCancel={() => setIsAddFilterPopoverOpen(false)}
key={JSON.stringify(newFilter)}
timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride}
/>
</div>
</EuiFlexItem>

View file

@ -54,6 +54,7 @@ export interface Props {
onSubmit: (filter: Filter) => void;
onCancel: () => void;
intl: InjectedIntl;
timeRangeForSuggestionsOverride?: boolean;
}
interface State {
@ -356,6 +357,7 @@ class FilterEditorUI extends Component<Props, State> {
value={this.state.params}
onChange={this.onParamsChange}
data-test-subj="phraseValueInput"
timeRangeForSuggestionsOverride={this.props.timeRangeForSuggestionsOverride}
fullWidth
/>
);
@ -366,6 +368,7 @@ class FilterEditorUI extends Component<Props, State> {
field={this.state.selectedField}
values={this.state.params}
onChange={this.onParamsChange}
timeRangeForSuggestionsOverride={this.props.timeRangeForSuggestionsOverride}
fullWidth
/>
);

View file

@ -17,6 +17,7 @@ export interface PhraseSuggestorProps {
kibana: KibanaReactContextValue<IDataPluginServices>;
indexPattern: IIndexPattern;
field?: IFieldType;
timeRangeForSuggestionsOverride?: boolean;
}
export interface PhraseSuggestorState {
@ -63,7 +64,8 @@ export class PhraseSuggestorUI<T extends PhraseSuggestorProps> extends React.Com
protected updateSuggestions = debounce(async (query: string = '') => {
if (this.abortController) this.abortController.abort();
this.abortController = new AbortController();
const { indexPattern, field } = this.props as PhraseSuggestorProps;
const { indexPattern, field, timeRangeForSuggestionsOverride } = this
.props as PhraseSuggestorProps;
if (!field || !this.isSuggestingValues()) {
return;
}
@ -74,8 +76,7 @@ export class PhraseSuggestorUI<T extends PhraseSuggestorProps> extends React.Com
field,
query,
signal: this.abortController.signal,
// Show all results in filter bar autocomplete
useTimeRange: false,
useTimeRange: timeRangeForSuggestionsOverride,
});
this.setState({ suggestions, isLoading: false });

View file

@ -36,6 +36,7 @@ export interface FilterItemProps {
intl: InjectedIntl;
uiSettings: IUiSettingsClient;
hiddenPanelOptions?: PanelOptions[];
timeRangeForSuggestionsOverride?: boolean;
}
interface LabelOptions {
@ -253,6 +254,7 @@ export function FilterItem(props: FilterItemProps) {
onCancel={() => {
setIsPopoverOpen(false);
}}
timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride}
/>
</div>
),

View file

@ -59,6 +59,7 @@ export interface QueryBarTopRowProps {
isClearable?: boolean;
nonKqlMode?: 'lucene' | 'text';
nonKqlModeHelpText?: string;
timeRangeForSuggestionsOverride?: boolean;
}
// Needed for React.lazy
@ -190,6 +191,7 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) {
iconType={props.iconType}
nonKqlMode={props.nonKqlMode}
nonKqlModeHelpText={props.nonKqlModeHelpText}
timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride}
/>
</AutocompleteFtuePopover>
</EuiFlexItem>

View file

@ -76,6 +76,11 @@ export interface QueryStringInputProps {
* @param storageKey this key is used to use user preference between kql and non-kql mode
*/
storageKey?: string;
/**
* Override whether autocomplete suggestions are restricted by time range.
*/
timeRangeForSuggestionsOverride?: boolean;
}
interface Props extends QueryStringInputProps {
@ -211,6 +216,7 @@ export default class QueryStringInputUI extends Component<Props, State> {
selectionStart,
selectionEnd,
signal: this.abortController.signal,
useTimeRange: this.props.timeRangeForSuggestionsOverride,
})) || [];
return [...suggestions, ...recentSearchSuggestions];
} catch (e) {

View file

@ -14,12 +14,13 @@ import { get, isEqual } from 'lodash';
import { EuiIconProps } from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { Query, Filter } from '@kbn/es-query';
import { withKibana, KibanaReactContextValue } from '../../../../kibana_react/public';
import QueryBarTopRow from '../query_string_input/query_bar_top_row';
import { SavedQueryAttributes, TimeHistoryContract, SavedQuery } from '../../query';
import { IDataPluginServices } from '../../types';
import { TimeRange, Query, Filter, IIndexPattern } from '../../../common';
import { TimeRange, IIndexPattern } from '../../../common';
import { FilterBar } from '../filter_bar/filter_bar';
import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form';
import { SavedQueryManagementComponent } from '../saved_query_management';
@ -349,6 +350,8 @@ class SearchBarUI extends Component<SearchBarProps, State> {
/>
);
const timeRangeForSuggestionsOverride = this.props.showDatePicker ? undefined : false;
let queryBar;
if (this.shouldRenderQueryBar()) {
queryBar = (
@ -381,6 +384,7 @@ class SearchBarUI extends Component<SearchBarProps, State> {
iconType={this.props.iconType}
nonKqlMode={this.props.nonKqlMode}
nonKqlModeHelpText={this.props.nonKqlModeHelpText}
timeRangeForSuggestionsOverride={timeRangeForSuggestionsOverride}
/>
);
}
@ -391,6 +395,7 @@ class SearchBarUI extends Component<SearchBarProps, State> {
// eslint-disable-next-line @typescript-eslint/naming-convention
'globalFilterGroup__wrapper-isVisible': this.state.isFiltersVisible,
});
filterBar = (
<div id="GlobalFilterGroup" className={filterGroupClasses}>
<FilterBar
@ -399,6 +404,7 @@ class SearchBarUI extends Component<SearchBarProps, State> {
onFiltersUpdated={this.props.onFiltersUpdated}
indexPatterns={this.props.indexPatterns!}
appName={this.services.appName}
timeRangeForSuggestionsOverride={timeRangeForSuggestionsOverride}
/>
</div>
);

View file

@ -5,6 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import expect from '@kbn/expect';
export default function ({ getService }) {
const esArchiver = getService('esArchiver');
@ -12,46 +13,227 @@ export default function ({ getService }) {
const supertest = getService('supertest');
describe('Suggestions API', function () {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/index_patterns/basic_kibana.json'
);
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/index_patterns/basic_kibana.json'
);
describe('non time based', () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/index_patterns/basic_kibana.json'
);
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/index_patterns/basic_kibana.json'
);
});
it('should return 200 without a query', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
query: '',
})
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expect(body).to.contain('hello');
}));
it('should return 200 without a query and with method set to terms_agg', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
method: 'terms_agg',
query: '',
})
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expect(body).to.contain('hello');
}));
it('should return 200 without a query and with method set to terms_enum', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
method: 'terms_enum',
query: '',
})
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expect(body).to.contain('hello');
}));
it('should return 200 with special characters', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
query: '<something?with:lots&of^ bad characters',
})
.expect(200)
.then(({ body }) => {
expect(body).to.be.empty();
}));
it('should support nested fields', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'nestedField.child',
query: 'nes',
})
.expect(200, ['nestedValue']));
it('should return 404 if index is not found', () =>
supertest
.post('/api/kibana/suggestions/values/not_found')
.send({
field: 'baz.keyword',
query: '1',
})
.expect(404));
it('should return 400 without a query', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
})
.expect(400));
it('should return 400 with a bad method', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
query: '',
method: 'cookie',
})
.expect(400));
});
it('should return 200 with special characters', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'baz.keyword',
query: '<something?with:lots&of^ bad characters',
})
.expect(200));
describe('time based', () => {
before(async () => {
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
it('should support nested fields', () =>
supertest
.post('/api/kibana/suggestions/values/basic_index')
.send({
field: 'nestedField.child',
query: 'nes',
})
.expect(200, ['nestedValue']));
after(async () => {
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
it('should return 404 if index is not found', () =>
supertest
.post('/api/kibana/suggestions/values/not_found')
.send({
field: 'baz.keyword',
query: '1',
})
.expect(404));
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
it('filter is applied on a document level with terms_agg', () =>
supertest
.post('/api/kibana/suggestions/values/logstash-*')
.send({
field: 'extension.raw',
query: '',
method: 'terms_agg',
filters: [
{
range: {
'@timestamp': {
gte: '2015-09-19T23:43:00.000Z',
lte: '2015-09-20T00:25:00.000Z',
format: 'strict_date_optional_time',
},
},
},
],
})
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expect(body).to.contain('jpg');
}));
it('filter returns all results because it was applied on an index level with terms_enum', () =>
supertest
.post('/api/kibana/suggestions/values/logstash-*')
.send({
field: 'extension.raw',
query: '',
method: 'terms_enum',
filters: [
{
range: {
'@timestamp': {
gte: '2015-09-19T23:43:00.000Z',
lte: '2015-09-20T00:25:00.000Z',
format: 'strict_date_optional_time',
},
},
},
],
})
.expect(200)
.then(({ body }) => {
// All indices have
expect(body).to.have.length(5);
}));
it('filter is applied on an index level with terms_enum - find in range', () =>
supertest
.post('/api/kibana/suggestions/values/logstash-*')
.send({
field: 'request.raw',
query: '/uploads/anatoly-art',
method: 'terms_enum',
filters: [
{
range: {
'@timestamp': {
gte: '2015-09-22T00:00:00.000Z',
lte: '2015-09-23T00:00:00.000Z',
format: 'strict_date_optional_time',
},
},
},
],
})
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(2);
}));
it('filter is applied on an index level with terms_enum - DONT find in range', () => {
supertest
.post('/api/kibana/suggestions/values/logstash-*')
.send({
field: 'request.raw',
query: '/uploads/anatoly-art',
method: 'terms_enum',
filters: [
{
range: {
'@timestamp': {
gte: '2015-09-23T00:00:00.000Z',
lte: '2015-09-24T00:00:00.000Z',
format: 'strict_date_optional_time',
},
},
},
],
})
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(0);
});
});
});
});
}

View file

@ -32,6 +32,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
before(async () => {
await PageObjects.visualize.initTests();
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setDefaultAbsoluteRange();
await filterBar.addFilter('extension.raw', 'is', 'jpg');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.saveSearch(savedSearchName);

View file

@ -73,6 +73,14 @@ export class SettingsPageObject extends FtrService {
);
}
async getAdvancedSettingAriaCheckbox(propertyName: string) {
this.log.debug('in getAdvancedSettingAriaCheckbox');
return await this.testSubjects.getAttribute(
`advancedSetting-editField-${propertyName}`,
'aria-checked'
);
}
async clearAdvancedSettings(propertyName: string) {
await this.testSubjects.click(`advancedSetting-resetField-${propertyName}`);
await this.header.waitUntilLoadingHasFinished();
@ -113,7 +121,14 @@ export class SettingsPageObject extends FtrService {
await this.header.waitUntilLoadingHasFinished();
}
async toggleAdvancedSettingCheckbox(propertyName: string) {
async toggleAdvancedSettingCheckbox(propertyName: string, value?: boolean) {
let curValue: string | undefined;
if (value !== undefined) {
curValue = await this.getAdvancedSettingAriaCheckbox(propertyName);
if (curValue === (value ? 'true' : 'false')) return;
}
await this.testSubjects.click(`advancedSetting-editField-${propertyName}`);
await this.header.waitUntilLoadingHasFinished();
await this.testSubjects.click(`advancedSetting-saveButton`);

View file

@ -19,5 +19,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./saved_searches'));
loadTestFile(require.resolve('./visualize_field'));
loadTestFile(require.resolve('./value_suggestions'));
loadTestFile(require.resolve('./value_suggestions_non_timebased'));
});
}

View file

@ -56,6 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('remains available regardless of the saved search state', async () => {
// create new search, csv export is not available
await PageObjects.discover.clickNewSearchButton();
await PageObjects.reporting.setTimepickerInDataRange();
await PageObjects.reporting.openCsvReportingPanel();
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null);
// save search, csv export is available

View file

@ -7,29 +7,127 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
import { UI_SETTINGS } from '../../../../../src/plugins/data/common';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const queryBar = getService('queryBar');
const PageObjects = getPageObjects(['common', 'timePicker']);
const filterBar = getService('filterBar');
const docTable = getService('docTable');
const PageObjects = getPageObjects(['common', 'timePicker', 'settings', 'context']);
async function setAutocompleteUseTimeRange(value: boolean) {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSettings();
await PageObjects.settings.toggleAdvancedSettingCheckbox(
UI_SETTINGS.AUTOCOMPLETE_USE_TIMERANGE,
value
);
}
describe('value suggestions', function describeIndexTests() {
before(async function () {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
await esArchiver.load('x-pack/test/functional/es_archives/dashboard/drilldowns');
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setDefaultAbsoluteRange();
});
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/dashboard/drilldowns');
});
it('show up', async () => {
await queryBar.setQuery('extension.raw : ');
const suggestions = await queryBar.getSuggestions();
expect(suggestions.length).to.be(5);
expect(suggestions).to.contain('"jpg"');
describe('useTimeRange enabled', () => {
before(async () => {
await setAutocompleteUseTimeRange(true);
});
beforeEach(async () => {
await PageObjects.common.navigateToApp('discover');
});
describe('discover', () => {
afterEach(async () => {
await queryBar.clearQuery();
});
it('dont show up if outside of range', async () => {
await PageObjects.timePicker.setAbsoluteRange(
'Mar 1, 2020 @ 00:00:00.000',
'Nov 1, 2020 @ 00:00:00.000'
);
await queryBar.setQuery('extension.raw : ');
const suggestions = await queryBar.getSuggestions();
expect(suggestions.length).to.be(0);
});
it('show up if in range', async () => {
await PageObjects.timePicker.setDefaultAbsoluteRange();
await queryBar.setQuery('extension.raw : ');
const suggestions = await queryBar.getSuggestions();
expect(suggestions.length).to.be(5);
expect(suggestions).to.contain('"jpg"');
});
});
describe('context', () => {
after(async () => {
await filterBar.removeFilter('geo.dest');
});
it('shows all autosuggest options for a filter in discover context app', async () => {
// Set a good time range
await PageObjects.timePicker.setDefaultAbsoluteRange();
// navigate to context
await docTable.clickRowToggle({ rowIndex: 0 });
const rowActions = await docTable.getRowActions({ rowIndex: 0 });
await rowActions[0].click();
await PageObjects.context.waitUntilContextLoadingHasFinished();
// Apply filter in context view
await filterBar.addFilter('geo.dest', 'is', 'US');
});
});
});
describe('useTimeRange disabled', () => {
before(async () => {
await setAutocompleteUseTimeRange(false);
});
beforeEach(async () => {
await PageObjects.common.navigateToApp('discover');
});
afterEach(async () => {
await queryBar.clearQuery();
});
after(async () => {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSettings();
await PageObjects.settings.clearAdvancedSettings(UI_SETTINGS.AUTOCOMPLETE_USE_TIMERANGE);
});
it('DO show up if outside of range', async () => {
await PageObjects.timePicker.setAbsoluteRange(
'Mar 1, 2020 @ 00:00:00.000',
'Nov 1, 2020 @ 00:00:00.000'
);
await queryBar.setQuery('extension.raw : ');
const suggestions = await queryBar.getSuggestions();
expect(suggestions.length).to.be(5);
expect(suggestions).to.contain('"jpg"');
});
it('show up', async () => {
await PageObjects.timePicker.setDefaultAbsoluteRange();
await queryBar.setQuery('extension.raw : ');
const suggestions = await queryBar.getSuggestions();
expect(suggestions.length).to.be(5);
expect(suggestions).to.contain('"jpg"');
});
});
});
}

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const queryBar = getService('queryBar');
const PageObjects = getPageObjects(['common', 'settings', 'context']);
describe('value suggestions non time based', function describeIndexTests() {
before(async function () {
await esArchiver.loadIfNeeded(
'test/functional/fixtures/es_archiver/index_pattern_without_timefield'
);
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/index_pattern_without_timefield'
);
});
it('shows all autosuggest options for a filter in discover context app', async () => {
await PageObjects.common.navigateToApp('discover');
await queryBar.setQuery('type.keyword : ');
const suggestions = await queryBar.getSuggestions();
expect(suggestions.length).to.be(1);
expect(suggestions).to.contain('"apache"');
});
});
}

View file

@ -132,6 +132,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should not carry over filters if creating a new lens visualization from within dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await filterBar.addFilter('geo.src', 'is', 'US');
await filterBar.toggleFilterPinned('geo.src');
await filterBar.addFilter('geo.dest', 'is', 'LS');