diff --git a/public/app/features/dashboard/ad_hoc_filters.ts b/public/app/features/dashboard/ad_hoc_filters.ts
index f962f8ca2f4..47babfdd5dd 100644
--- a/public/app/features/dashboard/ad_hoc_filters.ts
+++ b/public/app/features/dashboard/ad_hoc_filters.ts
@@ -10,9 +10,10 @@ export class AdHocFiltersCtrl {
removeTagFilterSegment: any;
/** @ngInject */
- constructor(private uiSegmentSrv, private datasourceSrv, private $q, private templateSrv, private $rootScope) {
+ constructor(private uiSegmentSrv, private datasourceSrv, private $q, private variableSrv, private $scope, private $rootScope) {
this.removeTagFilterSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove filter --'});
this.buildSegmentModel();
+ this.$rootScope.onAppEvent('template-variable-value-updated', this.buildSegmentModel.bind(this), $scope);
}
buildSegmentModel() {
@@ -141,8 +142,7 @@ export class AdHocFiltersCtrl {
}
this.variable.setFilters(filters);
- this.$rootScope.$emit('template-variable-value-updated');
- this.$rootScope.$broadcast('refresh');
+ this.variableSrv.variableUpdated(this.variable, true);
}
}
diff --git a/public/app/features/dashboard/submenu/submenu.ts b/public/app/features/dashboard/submenu/submenu.ts
index a925ba97f0d..0082f120b5f 100644
--- a/public/app/features/dashboard/submenu/submenu.ts
+++ b/public/app/features/dashboard/submenu/submenu.ts
@@ -22,10 +22,7 @@ export class SubmenuCtrl {
}
variableUpdated(variable) {
- this.variableSrv.variableUpdated(variable).then(() => {
- this.$rootScope.$emit('template-variable-value-updated');
- this.$rootScope.$broadcast('refresh');
- });
+ this.variableSrv.variableUpdated(variable, true);
}
openEditView(editview) {
diff --git a/public/app/features/templating/editor_ctrl.ts b/public/app/features/templating/editor_ctrl.ts
index 97274cb30d0..99c235ef24c 100644
--- a/public/app/features/templating/editor_ctrl.ts
+++ b/public/app/features/templating/editor_ctrl.ts
@@ -55,9 +55,8 @@ export class VariableEditorCtrl {
$scope.add = function() {
if ($scope.isValid()) {
- $scope.variables.push($scope.current);
+ variableSrv.addVariable($scope.current);
$scope.update();
- $scope.dashboard.updateSubmenuVisibility();
}
};
@@ -114,9 +113,8 @@ export class VariableEditorCtrl {
$scope.duplicate = function(variable) {
var clone = _.cloneDeep(variable.getSaveModel());
$scope.current = variableSrv.createVariableFromModel(clone);
- $scope.variables.push($scope.current);
$scope.current.name = 'copy_of_'+variable.name;
- $scope.dashboard.updateSubmenuVisibility();
+ $scope.variableSrv.addVariable($scope.current);
};
$scope.update = function() {
@@ -150,9 +148,7 @@ export class VariableEditorCtrl {
};
$scope.removeVariable = function(variable) {
- var index = _.indexOf($scope.variables, variable);
- $scope.variables.splice(index, 1);
- $scope.dashboard.updateSubmenuVisibility();
+ variableSrv.removeVariable(variable);
};
}
}
diff --git a/public/app/features/templating/specs/variable_srv_specs.ts b/public/app/features/templating/specs/variable_srv_specs.ts
index 85bca8d6068..9afb38056bd 100644
--- a/public/app/features/templating/specs/variable_srv_specs.ts
+++ b/public/app/features/templating/specs/variable_srv_specs.ts
@@ -22,6 +22,7 @@ describe('VariableSrv', function() {
ctx.variableSrv.init({
templating: {list: []},
events: new Emitter(),
+ updateSubmenuVisibility: sinon.stub(),
});
ctx.$rootScope.$digest();
}));
@@ -41,7 +42,9 @@ describe('VariableSrv', function() {
ctx.datasourceSrv.getMetricSources = sinon.stub().returns(scenario.metricSources);
- scenario.variable = ctx.variableSrv.addVariable(scenario.variableModel);
+ scenario.variable = ctx.variableSrv.createVariableFromModel(scenario.variableModel);
+ ctx.variableSrv.addVariable(scenario.variable);
+
ctx.variableSrv.updateOptions(scenario.variable);
ctx.$rootScope.$digest();
});
diff --git a/public/app/features/templating/variable_srv.ts b/public/app/features/templating/variable_srv.ts
index 022203b9eb9..c147c5b6685 100644
--- a/public/app/features/templating/variable_srv.ts
+++ b/public/app/features/templating/variable_srv.ts
@@ -90,17 +90,24 @@ export class VariableSrv {
return variable;
}
- addVariable(model) {
- var variable = this.createVariableFromModel(model);
+ addVariable(variable) {
this.variables.push(variable);
- return variable;
+ this.templateSrv.updateTemplateData();
+ this.dashboard.updateSubmenuVisibility();
+ }
+
+ removeVariable(variable) {
+ var index = _.indexOf(this.variables, variable);
+ this.variables.splice(index, 1);
+ this.templateSrv.updateTemplateData();
+ this.dashboard.updateSubmenuVisibility();
}
updateOptions(variable) {
return variable.updateOptions();
}
- variableUpdated(variable) {
+ variableUpdated(variable, emitChangeEvents?) {
// if there is a variable lock ignore cascading update because we are in a boot up scenario
if (variable.initLock) {
return this.$q.when();
@@ -117,7 +124,12 @@ export class VariableSrv {
}
});
- return this.$q.all(promises);
+ return this.$q.all(promises).then(() => {
+ if (emitChangeEvents) {
+ this.$rootScope.$emit('template-variable-value-updated');
+ this.$rootScope.$broadcast('refresh');
+ }
+ });
}
selectOptionsForCurrentValue(variable) {
@@ -218,6 +230,28 @@ export class VariableSrv {
// update url
this.$location.search(params);
}
+
+ setAdhocFilter(options) {
+ var variable = _.find(this.variables, {type: 'adhoc', datasource: options.datasource});
+ if (!variable) {
+ variable = this.createVariableFromModel({name: 'Filters', type: 'adhoc', datasource: options.datasource});
+ this.addVariable(variable);
+ }
+
+ let filters = variable.filters;
+ let filter = _.find(filters, {key: options.key, value: options.value});
+
+ if (!filter) {
+ filter = {key: options.key, value: options.value};
+ filters.push(filter);
+ }
+
+ filter.operator = options.operator;
+
+ variable.setFilters(filters);
+ this.variableUpdated(variable, true);
+ }
+
}
coreModule.service('variableSrv', VariableSrv);
diff --git a/public/app/plugins/datasource/elasticsearch/elastic_response.ts b/public/app/plugins/datasource/elasticsearch/elastic_response.ts
index a0416e8d91c..47fa14a99a6 100644
--- a/public/app/plugins/datasource/elasticsearch/elastic_response.ts
+++ b/public/app/plugins/datasource/elasticsearch/elastic_response.ts
@@ -100,9 +100,9 @@ ElasticResponse.prototype.processAggregationDocs = function(esAgg, aggDef, targe
// add columns
if (table.columns.length === 0) {
for (let propKey of _.keys(props)) {
- table.addColumn({text: propKey});
+ table.addColumn({text: propKey, filterable: true});
}
- table.addColumn({text: aggDef.field});
+ table.addColumn({text: aggDef.field, filterable: true});
}
// helper func to add values to value array
@@ -265,7 +265,7 @@ ElasticResponse.prototype.nameSeries = function(seriesList, target) {
};
ElasticResponse.prototype.processHits = function(hits, seriesList) {
- var series = {target: 'docs', type: 'docs', datapoints: [], total: hits.total};
+ var series = {target: 'docs', type: 'docs', datapoints: [], total: hits.total, filterable: true};
var propName, hit, doc, i;
for (i = 0; i < hits.hits.length; i++) {
diff --git a/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts
index ff78a99e10e..fde876af4f7 100644
--- a/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts
+++ b/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts
@@ -389,7 +389,7 @@ describe('ElasticResponse', function() {
it('should return table with byte and count', function() {
expect(result.data[0].rows.length).to.be(3);
- expect(result.data[0].columns).to.eql([{text: 'bytes'}, {text: 'Count'}]);
+ expect(result.data[0].columns).to.eql([{text: 'bytes', filterable: true}, {text: 'Count'}]);
});
});
diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts
index 375d3a0a0ae..ba26e853a29 100644
--- a/public/app/plugins/panel/table/module.ts
+++ b/public/app/plugins/panel/table/module.ts
@@ -50,8 +50,9 @@ class TablePanelCtrl extends MetricsPanelCtrl {
};
/** @ngInject */
- constructor($scope, $injector, templateSrv, private annotationsSrv, private $sanitize) {
+ constructor($scope, $injector, templateSrv, private annotationsSrv, private $sanitize, private variableSrv) {
super($scope, $injector);
+
this.pageIndex = 0;
if (this.panel.styles === void 0) {
@@ -223,10 +224,25 @@ class TablePanelCtrl extends MetricsPanelCtrl {
selector: '[data-link-tooltip]'
});
+ function addFilterClicked(e) {
+ let filterData = $(e.currentTarget).data();
+ var options = {
+ datasource: panel.datasource,
+ key: data.columns[filterData.column].text,
+ value: data.rows[filterData.row][filterData.column],
+ operator: filterData.operator,
+ };
+
+ ctrl.variableSrv.setAdhocFilter(options);
+ console.log('clicked', options);
+ }
+
elem.on('click', '.table-panel-page-link', switchPage);
+ elem.on('click', '.table-panel-filter-link', addFilterClicked);
var unbindDestroy = scope.$on('$destroy', function() {
elem.off('click', '.table-panel-page-link');
+ elem.off('click', '.table-panel-filter-link');
unbindDestroy();
});
diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts
index ef53b1d489b..02f0ceca72c 100644
--- a/public/app/plugins/panel/table/renderer.ts
+++ b/public/app/plugins/panel/table/renderer.ts
@@ -140,9 +140,12 @@ export class TableRenderer {
renderCell(columnIndex, rowIndex, value, addWidthHack = false) {
value = this.formatColumnValue(columnIndex, value);
+
+ var column = this.table.columns[columnIndex];
var style = '';
var cellClasses = [];
var cellClass = '';
+
if (this.colorState.cell) {
style = ' style="background-color:' + this.colorState.cell + ';color: white"';
this.colorState.cell = null;
@@ -161,26 +164,25 @@ export class TableRenderer {
if (value === undefined) {
style = ' style="display:none;"';
- this.table.columns[columnIndex].hidden = true;
+ column.hidden = true;
} else {
- this.table.columns[columnIndex].hidden = false;
+ column.hidden = false;
}
- var columnStyle = this.table.columns[columnIndex].style;
- if (columnStyle && columnStyle.preserveFormat) {
+ if (column.style && column.style.preserveFormat) {
cellClasses.push("table-panel-cell-pre");
}
- var columnHtml = value + widthHack;
+ var columnHtml = widthHack + value;
- if (columnStyle && columnStyle.link) {
+ if (column.style && column.style.link) {
// Render cell as link
var scopedVars = this.renderRowVariables(rowIndex);
scopedVars['__cell'] = { value: value };
- var cellLink = this.templateSrv.replace(columnStyle.linkUrl, scopedVars);
- var cellLinkTooltip = this.templateSrv.replace(columnStyle.linkTooltip, scopedVars);
- var cellTarget = columnStyle.linkTargetBlank ? '_blank' : '';
+ var cellLink = this.templateSrv.replace(column.style.linkUrl, scopedVars);
+ var cellLinkTooltip = this.templateSrv.replace(column.style.linkTooltip, scopedVars);
+ var cellTarget = column.style.linkTargetBlank ? '_blank' : '';
cellClasses.push("table-panel-cell-link");
columnHtml = `
@@ -190,6 +192,19 @@ export class TableRenderer {
`;
}
+ if (column.filterable) {
+ cellClasses.push("table-panel-cell-filterable");
+ columnHtml += `
+
+
+
+
+
+ `;
+ }
+
if (cellClasses.length) {
cellClass = ' class="' + cellClasses.join(' ') + '"';
}
diff --git a/public/app/plugins/panel/table/transformers.ts b/public/app/plugins/panel/table/transformers.ts
index 07d183eafa8..cb6d30e627d 100644
--- a/public/app/plugins/panel/table/transformers.ts
+++ b/public/app/plugins/panel/table/transformers.ts
@@ -185,8 +185,16 @@ transformers['json'] = {
},
transform: function(data, panel, model) {
var i, y, z;
- for (i = 0; i < panel.columns.length; i++) {
- model.columns.push({text: panel.columns[i].text});
+
+ for (let column of panel.columns) {
+ var tableCol: any = {text: column.text};
+
+ // if filterable data then set columns to filterable
+ if (data.length > 0 && data[0].filterable) {
+ tableCol.filterable = true;
+ }
+
+ model.columns.push(tableCol);
}
if (model.columns.length === 0) {
diff --git a/public/sass/components/_panel_table.scss b/public/sass/components/_panel_table.scss
index 073d8be4abf..cf81dca4465 100644
--- a/public/sass/components/_panel_table.scss
+++ b/public/sass/components/_panel_table.scss
@@ -91,9 +91,23 @@
&.cell-highlighted:hover {
background-color: $tight-form-func-bg;
}
+
+ &:hover {
+ .table-panel-filter-link {
+ visibility: visible;
+ }
+ }
}
}
+.table-panel-filter-link {
+ visibility: hidden;
+ color: $text-color-weak;
+ float: right;
+ display: block;
+ padding: 0 5px;
+}
+
.table-panel-header-bg {
background: $grafanaListAccent;
border-top: 2px solid $body-bg;