Merge branch 'master' into fix/3604

This commit is contained in:
Shelby Sturgis 2015-04-17 15:42:57 -04:00
commit ea9129775e
18 changed files with 168 additions and 30 deletions

View file

@ -22,9 +22,17 @@
name="field"
required
ng-model="agg.params.field"
ng-if="indexedFields.length"
auto-select-if-only-one="indexedFields"
ng-options="field as field.displayName group by field.type for field in indexedFields"
ng-change="aggParam.onChange(agg)">
</select>
<div class="hintbox" ng-if="!indexedFields.length">
<p>
<i class="fa fa-danger text-danger"></i>
<strong>No Compatible Fields:</strong> The "{{ vis.indexPattern.id }}" index pattern does not any of the following field types: {{ agg.type.params.byName.field.filterFieldTypes | commaList:false }}
</p>
</div>
</div>

View file

@ -17,6 +17,15 @@
</div>
</div>
</div>
<input ng-model="agg.params.filters.length" name="filterLength" required min="1" type="number" class="ng-hide">
<div class="hintbox" ng-show="aggForm.filterLength.$invalid">
<p>
<i class="fa fa-danger text-danger"></i>
<strong>Required:</strong> You must specify at least one filter
</p>
</div>
<div
click-focus="'filter'+(agg.params.filters.length-1)"
ng-click="agg.params.filters.push({input:{}})"

View file

@ -18,9 +18,12 @@
</div>
<input ng-model="validLength" name="validLength" required type="hidden">
<p ng-show="aggForm.validLength.$invalid" class="text-danger text-center">
You mush specify at least one percentile
</p>
<div class="hintbox" ng-show="aggForm.validLength.$invalid">
<p>
<i class="fa fa-danger text-danger"></i>
<strong>Required:</strong> You mush specify at least one percentile
</p>
</div>
<button
ng-click="add()"
@ -28,4 +31,4 @@
class="sidebar-item-button primary">
<i class="fa fa-plus"></i> Add Percent
</button>
</div>
</div>

View file

@ -3,7 +3,11 @@
<label>JSON Input</label>
<i class="fa fa-info-circle"></i>
</span>
<div class="hintbox" ng-show="showJsonHint">Any JSON formatted properties you add here will be merged with the elasticsearch aggregation definition for this section. For example <i>shard_size</i> on a <i>terms</i> aggregation</div>
<div class="hintbox" ng-show="showJsonHint">
<p>
Any JSON formatted properties you add here will be merged with the elasticsearch aggregation definition for this section. For example <i>shard_size</i> on a <i>terms</i> aggregation
</p>
</div>
<p>
<textarea
type="text"
@ -12,4 +16,4 @@
validate-json
></textarea>
</p>
</div>
</div>

View file

@ -17,9 +17,12 @@
</div>
<input ng-model="validLength" name="validLength" required type="hidden">
<p ng-show="aggForm.validLength.$invalid" class="text-danger text-center">
You must specify at least one value
</p>
<div class="hintbox" ng-show="aggForm.validLength.$invalid">
<p>
<i class="fa fa-danger text-danger"></i>
<strong>Required:</strong> You must specify at least one value
</p>
</div>
<button
ng-click="add()"
@ -27,4 +30,4 @@
class="sidebar-item-button primary">
<i class="fa fa-plus"></i> Add value
</button>
</div>
</div>

View file

@ -0,0 +1,26 @@
define(function (require) {
var _ = require('lodash');
require('modules')
.get('kibana')
.filter('commaList', function () {
/**
* Angular filter that accepts either an array or a comma-seperated string
* and outputs either an array, or a comma-seperated string for presentation.
*
* @param {String|Array} input - The comma-seperated list or array
* @param {Boolean} inclusive - Should the list be joined with an "and"?
* @return {String}
*/
return function (input, inclusive) {
var list = _.commaSeperatedList(input);
if (list.length < 2) {
return list.join('');
}
var conj = inclusive ? ' and ' : ' or ';
return list.slice(0, -1).join(', ') + conj + _.last(list);
};
});
});

View file

@ -5,16 +5,16 @@
</ul>
<div class="content">
<table class="table table-condensed" ng-show="mode == 'table'" bindonce>
<table class="table table-condensed" ng-show="mode == 'table'">
<tbody>
<tr ng-repeat="field in fields" bindonce>
<tr ng-repeat="field in fields">
<td field-name="field"
field-type="mapping[field].type"
width="1%"
class="doc-viewer-field">
</td>
<td width="1%" class="doc-viewer-buttons" ng-if="filter">
<span bo-if="mapping[field].filterable">
<span ng-if="mapping[field].filterable">
<i ng-click="filter(mapping[field], flattened[field], '+')"
tooltip="Filter for value"
tooltip-append-to-body="1"
@ -24,11 +24,11 @@
tooltip-append-to-body="1"
class="fa fa-search-minus"></i>
</span>
<span bo-if="!mapping[field].filterable" tooltip="Unindexed fields can not be searched">
<span ng-if="!mapping[field].filterable" tooltip="Unindexed fields can not be searched">
<i class="fa fa-search-plus text-muted"></i>
<i class="fa fa-search-minus text-muted"></i>
</span>
<span bo-if="columns">
<span ng-if="columns">
<i ng-click="toggleColumn(field)"
tooltip="Toggle column in table"
tooltip-append-to-body="1"
@ -37,15 +37,15 @@
</td>
<td>
<i bo-if="!mapping[field] && field[0] === '_'"
<i ng-if="!mapping[field] && field[0] === '_'"
tooltip-placement="top"
tooltip="Field names beginning with _ are not supported"
class="fa fa-warning text-color-warning ng-scope doc-viewer-underscore"></i>
<i bo-if="!mapping[field] && field[0] !== '_' && !showArrayInObjectsWarning(doc, field)"
<i ng-if="!mapping[field] && field[0] !== '_' && !showArrayInObjectsWarning(doc, field)"
tooltip-placement="top"
tooltip="No cached mapping for this field. Refresh your mapping from the Settings > Indices page"
class="fa fa-warning text-color-warning ng-scope doc-viewer-no-mapping"></i>
<i bo-if="showArrayInObjectsWarning(doc, field)"
<i ng-if="showArrayInObjectsWarning(doc, field)"
tooltip-placement="top"
tooltip="Objects in arrays are not well supported."
class="fa fa-warning text-color-warning ng-scope doc-viewer-object-array"></i>

View file

@ -77,6 +77,9 @@ define(function (require) {
Handler.prototype.render = function () {
var self = this;
var charts = this.charts = [];
var selection = d3.select(this.el);
selection.selectAll('*').remove();
this._validateData();
this.renderArray.forEach(function (property) {
@ -86,8 +89,7 @@ define(function (require) {
});
// render the chart(s)
d3.select(this.el)
.selectAll('.chart')
selection.selectAll('.chart')
.each(function (chartData) {
var chart = new self.ChartClass(self, this, chartData);
var enabledEvents;

View file

@ -100,10 +100,14 @@ define(function (require) {
* @method destroy
*/
Vis.prototype.destroy = function () {
var selection = d3.select(this.el).select('.vis-wrapper');
this.resizeChecker.off('resize', this.resize);
this.resizeChecker.destroy();
if (this.handler) this._runOnHandler('destroy');
d3.select(this.el).selectAll('*').remove();
selection.remove();
selection = null;
};
/**

View file

@ -44,7 +44,10 @@ define(function (require) {
* @returns {HTMLElement} Contains the D3 chart
*/
Chart.prototype.render = function () {
return d3.select(this.chartEl).call(this.draw());
var selection = d3.select(this.chartEl);
selection.selectAll('*').remove();
selection.call(this.draw());
};
/**
@ -68,7 +71,10 @@ define(function (require) {
* @method destroy
*/
Chart.prototype.destroy = function () {
d3.select(this.chartEl).selectAll('*').remove();
var selection = d3.select(this.chartEl);
selection.remove();
selection = null;
};
return Chart;

View file

@ -45,10 +45,10 @@
<i aria-hidden="true" class="fa-link fa"></i>
</a>
</p>
<div bo-if="indexPattern.timeFieldName && indexPattern.intervalName" class="alert alert-info">
<div ng-if="indexPattern.timeFieldName && indexPattern.intervalName" class="alert alert-info">
This index uses a <strong>Time-based index pattern</strong> which repeats <span bo-text="indexPattern.getInterval().display"></span>
</div>
<div bo-if="conflictFields.length" class="alert alert-warning">
<div ng-if="conflictFields.length" class="alert alert-warning">
<strong>Mapping conflict!</strong> {{conflictFields.length > 1 ? conflictFields.length : 'A'}} field{{conflictFields.length > 1 ? 's' : ''}} {{conflictFields.length > 1 ? 'are' : 'is'}} defined as several types (string, integer, etc) across the indices that match this pattern. You may still be able to use these conflict fields in parts of Kibana, but they will be unavailable for functions that require Kibana to know their type. Correcting this issue will require reindexing your data.
</div>
</div>

View file

@ -38,7 +38,9 @@ define(function (require) {
if (!tab) $scope.changeTab($scope.fieldTypes[0]);
});
$scope.conflictFields = _.filter($scope.indexPattern.fields, {type: 'conflict'});
$scope.$watchCollection('indexPattern.fields', function () {
$scope.conflictFields = _.filter($scope.indexPattern.fields, {type: 'conflict'});
});
$scope.refreshFields = function () {
$scope.indexPattern.refreshFields();
@ -73,4 +75,4 @@ define(function (require) {
return $scope.indexPattern.save();
};
});
});
});

View file

@ -4,7 +4,7 @@
<h5>
Index Patterns&nbsp;
<a
bo-if="edittingId"
ng-if="edittingId"
href="#/settings/indices"
class="btn btn-primary btn-xs"
aria-label="Add New">

View file

@ -8,6 +8,7 @@ define(function (require) {
require('directives/saved_object_finder');
require('components/visualize/visualize');
require('components/clipboard/clipboard');
require('components/comma_list_filter');
require('filters/uriescape');

View file

@ -256,5 +256,25 @@ define(function (require) {
get: function (obj, path) {
return _.deepGet(obj, path);
},
/**
* Parse a comma-seperated list into an array
* efficiently, or just return if already an array
*
* @param {string|array} input - the comma-seperated list
* @return {array}
*/
commaSeperatedList: function (input) {
if (_.isArray(input)) return input;
var source = String(input || '').split(',');
var list = [];
while (source.length) {
var item = source.shift().trim();
if (item) list.push(item);
}
return list;
},
};
});

View file

@ -0,0 +1,32 @@
define(function (require) {
require('components/comma_list_filter');
describe('Comma-List filter', function () {
var commaList;
beforeEach(module('kibana'));
beforeEach(inject(function ($injector) {
commaList = $injector.get('commaListFilter');
}));
it('converts a string to a pretty list', function () {
expect(commaList('john,jaine,jim', true)).to.be('john, jaine and jim');
expect(commaList('john,jaine,jim', false)).to.be('john, jaine or jim');
});
it('can accept an array too', function () {
expect(commaList(['john', 'jaine', 'jim'])).to.be('john, jaine or jim');
});
it('handles undefined ok', function () {
expect(commaList()).to.be('');
});
it('handls single values ok', function () {
expect(commaList(['john'])).to.be('john');
});
});
});

View file

@ -27,6 +27,7 @@ define(function (require) {
var beforeEvent = 'click';
var afterEvent = 'brush';
var vis;
var secondVis;
var numberOfCharts;
beforeEach(function () {
@ -36,12 +37,14 @@ define(function (require) {
beforeEach(function () {
inject(function (d3, Private) {
vis = Private(require('vislib_fixtures/_vis_fixture'))();
secondVis = Private(require('vislib_fixtures/_vis_fixture'))();
require('css!components/vislib/styles/main');
});
});
afterEach(function () {
$(vis.el).remove();
$(secondVis.el).remove();
vis = null;
});
@ -86,11 +89,17 @@ define(function (require) {
describe('destroy Method', function () {
beforeEach(function () {
vis.destroy();
vis.render(data);
secondVis.render(data);
secondVis.destroy();
});
it('should remove all DOM elements from el', function () {
expect($('.vis-wrapper').length).to.be(0);
expect($(secondVis.el).find('.vis-wrapper').length).to.be(0);
});
it('should not remove visualizations that have not been destroyed', function () {
expect($(vis.el).find('.vis-wrapper').length).to.be(1);
});
});

View file

@ -120,5 +120,14 @@ define(function (require) {
expect(typeof myChart.render === 'function').to.be(true);
});
it('should destroy the chart element', function () {
// Once destroy is called, a chart should not be able to be drawn
myChart.destroy();
expect(function () {
myChart.draw();
}).to.throwError();
});
});
});