[6.4] fixes pinned filters in visualize and dashboard (#21463) (#21633)

* fixing embedded mode in visualize (#21468)

# Conflicts:
#	test/functional/config.js
#	test/functional/services/index.js

* fixing tests
This commit is contained in:
Peter Pisljar 2018-08-03 15:55:18 +02:00 committed by GitHub
parent 5f66ba8463
commit 2d4a54efa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 163 additions and 31 deletions

View file

@ -200,16 +200,22 @@ export class DashboardStateManager {
store.dispatch(updateDescription(this.getDescription()));
}
if (!_.isEqual(
FilterUtils.cleanFiltersForComparison(this.appState.filters),
FilterUtils.cleanFiltersForComparison(getFilters(state))
)) {
store.dispatch(updateFilters(this.appState.filters));
}
if (getQuery(state) !== this.getQuery()) {
store.dispatch(updateQuery(this.getQuery()));
}
this._pushFiltersToStore();
}
_pushFiltersToStore() {
const state = store.getState();
const dashboardFilters = this.getDashboardFilterBars();
if (!_.isEqual(
FilterUtils.cleanFiltersForComparison(dashboardFilters),
FilterUtils.cleanFiltersForComparison(getFilters(state))
)) {
store.dispatch(updateFilters(dashboardFilters));
}
}
_handleStoreChanges() {
@ -582,6 +588,8 @@ export class DashboardStateManager {
this.savedDashboard.searchSource.setField('query', query);
this.savedDashboard.searchSource.setField('filter', filters);
this.saveState();
// pinned filters go on global state, therefore are not propagated to store via app state and have to be pushed manually.
this._pushFiltersToStore();
}
/**

View file

@ -27,6 +27,7 @@
</a>
&nbsp;
<a
data-test-subj="unlinkSavedSearch"
href=""
ng-dblclick="unlink()"
tooltip="Double click to unlink from Saved Search"

View file

@ -125,6 +125,19 @@ function VisEditor(
// vis is instance of src/ui/public/vis/vis.js.
// SearchSource is a promise-based stream of search results that can inherit from other search sources.
const { vis, searchSource } = savedVis;
// adds top level search source to the stack to which global filters are applied
const getTopLevelSearchSource = (searchSource) => {
if (searchSource.getParent()) return getTopLevelSearchSource(searchSource.getParent());
return searchSource;
};
const topLevelSearchSource = getTopLevelSearchSource(searchSource);
const globalFiltersSearchSource = searchSource.create();
globalFiltersSearchSource.setField('index', searchSource.getField('index'));
topLevelSearchSource.setParent(globalFiltersSearchSource);
$scope.vis = vis;
$scope.topNavMenu = [{
@ -281,8 +294,10 @@ function VisEditor(
// update the searchSource when query updates
$scope.fetch = function () {
$state.save();
const globalFilters = queryFilter.getGlobalFilters();
savedVis.searchSource.setField('query', $state.query);
savedVis.searchSource.setField('filter', $state.filters);
globalFiltersSearchSource.setField('filter', globalFilters);
$scope.vis.forceReload();
};
@ -361,27 +376,20 @@ function VisEditor(
$scope.unlink = function () {
if (!$state.linked) return;
toastNotifications.addSuccess(`Unlinked from saved search '${savedVis.savedSearch.title}'`);
$state.linked = false;
const searchSourceParent = searchSource.getParent(true);
const searchSourceGrandparent = searchSourceParent.getParent(true);
const searchSourceParent = searchSource.getParent();
const searchSourceGrandparent = searchSourceParent.getParent();
delete savedVis.savedSearchId;
searchSourceParent.setField('filter', _.union(searchSource.getOwnField('filter'), searchSourceParent.getOwnField('filter')));
// copy over all state except "aggs", "query" and "filter"
_(searchSourceParent.toJSON())
.omit(['aggs', 'filter', 'query'])
.forOwn(function (val, key) {
searchSource.setField(key, val);
})
.commit();
$state.query = searchSource.getField('query');
$state.filters = searchSource.getField('filter');
$state.query = searchSourceParent.getField('query');
$state.filters = searchSourceParent.getField('filter');
searchSource.setField('index', searchSourceParent.getField('index'));
searchSource.setParent(searchSourceGrandparent);
toastNotifications.addSuccess(`Unlinked from saved search '${savedVis.savedSearch.title}'`);
$scope.fetch();
};

View file

@ -104,6 +104,63 @@ export default function ({ getService, getPageObjects }) {
});
});
describe.skip('using a pinned filter that excludes all data', async () => {
before(async () => {
await filterBar.toggleFilterPinned('bytes');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
});
it('filters on pie charts', async () => {
await dashboardExpect.pieSliceCount(0);
});
it('area, bar and heatmap charts filtered', async () => {
await dashboardExpect.seriesElementCount(0);
});
it('data tables are filtered', async () => {
await dashboardExpect.dataTableRowCount(0);
});
it('goal and guages are filtered', async () => {
await dashboardExpect.goalAndGuageLabelsExist(['0', '0%']);
});
it('tsvb time series shows no data message', async () => {
expect(await testSubjects.exists('noTSVBDataMessage')).to.be(true);
await dashboardExpect.tsvbTimeSeriesLegendCount(0);
});
it('metric value shows no data', async () => {
await dashboardExpect.metricValuesExist(['-']);
});
it('tag cloud values are filtered', async () => {
await dashboardExpect.emptyTagCloudFound();
});
it('tsvb metric is filtered', async () => {
await dashboardExpect.tsvbMetricValuesExist(['0 custom template']);
});
it('tsvb top n is filtered', async () => {
await dashboardExpect.tsvbTopNValuesExist(['0', '0']);
});
it('saved search is filtered', async () => {
await dashboardExpect.savedSearchRowCount(0);
});
it('timelion is filtered', async () => {
await dashboardExpect.timelionLegendCount(0);
});
it('vega is filtered', async () => {
await dashboardExpect.vegaTextsDoNotExist(['5,000']);
});
});
// Skipped because it depends on filter applied by disabled test
describe.skip('disabling a filter unfilters the data on', async () => {
before(async () => {

View file

@ -161,7 +161,15 @@ export default function ({ getService, getPageObjects }) {
it('should correctly filter for applied time filter on the main timefield', async () => {
await filterBar.addFilter('@timestamp', 'is between', ['2015-09-19', '2015-09-21']);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.common.sleep(1000);
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim().split('\n')).to.be.eql([
'2015-09-20', '4,757',
]);
});
it('should correctly filter for pinned filters', async () => {
await filterBar.toggleFilterPinned('@timestamp');
await PageObjects.header.waitUntilLoadingHasFinished();
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim().split('\n')).to.be.eql([
'2015-09-20', '4,757',

View file

@ -19,11 +19,11 @@
import expect from 'expect.js';
export default function ({ getPageObjects }) {
export default function ({ getService, getPageObjects }) {
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'discover', 'visualize', 'header']);
// Blocked by: https://github.com/elastic/kibana/issues/19750
describe.skip('visualize app', function describeIndexTests() {
describe('visualize app', function describeIndexTests() {
const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
@ -32,8 +32,12 @@ export default function ({ getPageObjects }) {
const savedSearchName = 'vis_saved_search';
before(async () => {
await PageObjects.common.navigateToUrl('discover', '');
await PageObjects.common.navigateToApp('discover');
await filterBar.addFilter('extension.raw', 'is', 'jpg');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.saveSearch(savedSearchName);
// TODO: Remove this once https://github.com/elastic/kibana/issues/19750 is properly resolved
await PageObjects.common.sleep(500);
});
it('should create a visualization from a saved search', async () => {
@ -43,19 +47,44 @@ export default function ({ getPageObjects }) {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim()).to.be('14,004');
expect(data.trim()).to.be('9,109');
});
it('should respect the time filter when linked to a saved search', async () => {
await PageObjects.header.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-21 10:00:00.000');
await PageObjects.header.waitUntilLoadingHasFinished();
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim()).to.be('6,086');
expect(data.trim()).to.be('3,950');
});
});
after(async () => {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
it('should allow adding filters while having a linked saved search', async () => {
await filterBar.addFilter('bytes', 'is between', ['100', '3000']);
await PageObjects.header.waitUntilLoadingHasFinished();
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim()).to.be('707');
});
it('should allow unlinking from a linked search', async () => {
await PageObjects.visualize.clickUnlinkSavedSearch();
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim()).to.be('707');
// The filter on the saved search should now be in the editor
expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true);
// Disabling this filter should now result in different values, since
// the visualization should not be linked anymore with the saved search.
await filterBar.toggleFilterEnabled('extension.raw');
await PageObjects.header.waitUntilLoadingHasFinished();
const unfilteredData = await PageObjects.visualize.getTableVisData();
expect(unfilteredData.trim()).to.be('1,293');
});
it('should not break when saving after unlinking', async () => {
await PageObjects.visualize.saveVisualization('Unlinked before saved');
await PageObjects.header.waitUntilLoadingHasFinished();
const data = await PageObjects.visualize.getTableVisData();
expect(data.trim()).to.be('1,293');
});
});
});
}

View file

@ -449,6 +449,11 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
await PageObjects.header.waitUntilLoadingHasFinished();
}
async clickUnlinkSavedSearch() {
await testSubjects.doubleClick('unlinkSavedSearch');
await PageObjects.header.waitUntilLoadingHasFinished();
}
async setValue(newValue) {
await find.clickByCssSelector('button[ng-click="numberListCntr.add()"]', defaultFindTimeout * 2);
const input = await find.byCssSelector('input[ng-model="numberListCntr.getList()[$index]"]');

View file

@ -50,6 +50,12 @@ export function FilterBarProvider({ getService }) {
await testSubjects.click(`filter & filter-key-${key} disableFilter-${key}`);
}
async toggleFilterPinned(key) {
const filterElement = await testSubjects.find(`filter & filter-key-${key}`);
await remote.moveMouseTo(filterElement);
await testSubjects.click(`filter & filter-key-${key} pinFilter-${key}`);
}
async addFilter(field, operator, values) {
if (!Array.isArray(values)) {
values = [values];

View file

@ -54,6 +54,16 @@ export function TestSubjectsProvider({ getService }) {
});
}
async doubleClick(selector, timeout = defaultFindTimeout) {
log.debug(`TestSubjects.doubleClick(${selector})`);
return await retry.try(async () => {
const element = await this.find(selector, timeout);
await remote.moveMouseTo(element);
await remote.doubleClick();
});
}
async descendantExists(selector, parentElement) {
return await find.descendantExistsByCssSelector(testSubjSelector(selector), parentElement);
}