Merge branch 'master' into better_tooltips

Conflicts:
	src/kibana/components/vislib/lib/tooltip.js
This commit is contained in:
Spencer Alger 2014-09-30 17:45:18 -07:00
commit 0054b82108
10 changed files with 145 additions and 50 deletions

View file

@ -172,21 +172,25 @@ define(function (require) {
timefilter.enabled = !!timefield;
});
// options are 'loading', 'ready', 'none', undefined
$scope.$watchMulti([
'rows',
'fetchStatus'
], (function updateResultState() {
var prev = {};
var status = {
LOADING: 'loading', // initial data load
READY: 'ready', // results came back
NO_RESULTS: 'none' // no results came back
};
function pick(rows, oldRows, fetchStatus, oldFetchStatus) {
// initial state, pretend we are loading
if (rows == null && oldRows == null) return 'loading';
if (rows == null && oldRows == null) return status.LOADING;
var rowsEmpty = _.isEmpty(rows);
if (rowsEmpty && fetchStatus) return 'loading';
else if (!rowsEmpty) return 'ready';
else return 'none';
if (rowsEmpty && fetchStatus) return status.LOADING;
else if (!rowsEmpty) return status.READY;
else return status.NO_RESULTS;
}
return function () {
@ -242,6 +246,15 @@ define(function (require) {
};
$scope.opts.fetch = $scope.fetch = function () {
// flag used to set the scope based on data from segmentedFetch
var resetRows = true;
function flushResponseData() {
$scope.hits = 0;
$scope.rows = [];
$scope.rows.fieldCounts = {};
}
// ignore requests to fetch before the app inits
if (!init.complete) return;
@ -288,11 +301,17 @@ define(function (require) {
$scope.fetchStatus = status;
},
first: function (resp) {
$scope.hits = 0;
$scope.rows = [];
$scope.rows.fieldCounts = {};
if (!$scope.rows) {
flushResponseData();
}
},
each: notify.timed('handle each segment', function (resp, req) {
if (resetRows) {
if (!resp.hits.total) return;
resetRows = false;
flushResponseData();
}
$scope.hits += resp.hits.total;
var rows = $scope.rows;
var counts = rows.fieldCounts;
@ -339,10 +358,15 @@ define(function (require) {
});
}),
eachMerged: function (merged) {
$scope.mergedEsResp = merged;
if (!resetRows) {
$scope.mergedEsResp = merged;
}
}
})
.finally(function () {
if (resetRows) {
flushResponseData();
}
$scope.fetchStatus = false;
});
})
@ -601,6 +625,7 @@ define(function (require) {
$scope.searchSource.set('aggs', undefined);
delete $scope.vis;
}
// we shouldn't have one, or already do, return whatever we already have
if (!$scope.opts.timefield || $scope.vis) return Promise.resolve($scope.vis);

View file

@ -1,6 +1,11 @@
.vis-editor {
.flex-parent();
@vis-editor-sidebar-basis: (100/12) * 2%; // two of twelve columns
@vis-editor-sidebar-min-width: 350px;
@vis-editor-nesting-width: 8px;
@vis-editor-agg-editor-spacing: 5px;
navbar {
.bitty-modal-container {
position: relative;
@ -33,12 +38,28 @@
&-sidebar {
.flex-parent(0, 0, auto);
overflow: auto;
// overrided for tablet and desktop
@media (min-width: @screen-md-min) {
.flex-basis(@vis-editor-sidebar-basis);
min-width: @vis-editor-sidebar-min-width;
max-width: @vis-editor-sidebar-min-width;
margin-bottom: (@input-height-base * 2) - 3;
}
&-buttons {
// overrides for tablet and desktop
@media (min-width: @screen-md-min) {
position: absolute;
bottom: 0;
min-width: @vis-editor-sidebar-min-width;
}
}
.sidebar-item {
border-top: 0 !important;
}
.sidebar-container {
@ -79,7 +100,6 @@
&-group {
.flex-parent();
color: @text-color;
margin-bottom: 10px;
}
&-header {

View file

@ -29,13 +29,15 @@
</vis-editor-agg-group>
<!-- apply/discard -->
<li class="sidebar-item" ng-if="vis.dirty">
<li class="vis-editor-sidebar-buttons sidebar-item">
<button
ng-disabled="!vis.dirty"
type="submit"
class="sidebar-item-button success">
Apply
</button>
<button
ng-disabled="!vis.dirty"
type="button"
ng-click="reset()"
class="sidebar-item-button warn">

View file

@ -19,4 +19,4 @@
font-size: 1.2em;
}
@import "../editor/editor.less";
@import "../editor/editor.less";

View file

@ -1,14 +1,16 @@
define(function (require) {
var _ = require('lodash');
return function AppStateProvider(Private) {
return function AppStateProvider(Private, $rootScope) {
var State = Private(require('components/state_management/state'));
_(AppState).inherits(State);
function AppState(defaults) {
AppState.Super.call(this, '_a', defaults);
}
// When we have a route change, destroy the app state
$rootScope.$on('$routeChangeStart', _.bindKey(this, 'destroy'));
}
return AppState;
};

View file

@ -29,12 +29,9 @@ define(function (require) {
return function (selection) {
// if tooltip not appended to body, append one
if (d3.select('body').select('.' + self.tooltipClass)[0][0] === null) {
d3.select('body').append('div').attr('class', self.tooltipClass);
}
// if container not saved on Tooltip, save it
if (self.container === undefined || self.container !== d3.select(self.el).select('.' + self.containerClass)) {
self.container = d3.select(self.el).select('.' + self.containerClass);
}
@ -42,67 +39,62 @@ define(function (require) {
var tooltipDiv = d3.select('.' + self.tooltipClass);
selection.each(function (data, i) {
// DOM element on which the tooltip is called
var element = d3.select(this);
// define selections relative to el of tooltip
var offset;
element
.on('mousemove.tip', function (d) {
// get x and y coordinates of the mouse event
var mouseMove = {
left: d3.event.clientX,
top: d3.event.clientY
};
offset = self.getOffsets(tooltipDiv, mouseMove);
offset = self.getOffsets(mouseMove);
// return text and position for tooltip
return tooltipDiv.datum(self.events.eventResponse(d, i))
.html(tooltipFormatter)
.style('display', 'block')
.style('visibility', 'visible')
.style('left', mouseMove.left + offset.left + 'px')
.style('top', mouseMove.top + offset.top + 'px');
})
.on('mouseout.tip', function () {
// hide tooltip
return tooltipDiv.style('display', 'none');
return tooltipDiv.style('visibility', 'hidden')
.style('left', '-500px')
.style('top', '-500px');
});
});
};
};
Tooltip.prototype.getOffsets = function (tooltipDiv, mouseMove) {
Tooltip.prototype.getOffsets = function (mouseMove) {
var self = this;
var offset = {top: 10, left: 10};
var offset = {top: -10, left: 10};
if ($(self.el).find('.' + self.containerClass)) {
var container = $(self.el).find('.' + self.containerClass);
var container = $(self.el).find('.' + self.containerClass);
var chartXOffset = container.offset().left;
var chartYOffset = container.offset().top;
var chartWidth = container.width();
var chartHeight = container.height();
var tipWidth = tooltipDiv[0][0].clientWidth;
var tipHeight = tooltipDiv[0][0].clientHeight;
var chartWidth = container.width();
var chartHeight = container.height();
var tip = $('.' + self.tooltipClass)[0];
var tipWidth = tip.clientWidth;
var tipHeight = tip.clientHeight;
// change xOffset to keep tooltip within container
// if tip width + xOffset puts it over right edge of container, flip left
// unless flip left puts it over left edge of container
if ((mouseMove.left + offset.left + tipWidth) > (chartXOffset + chartWidth) &&
(mouseMove.left - tipWidth - 10) > chartXOffset) {
// change yOffset to keep tooltip within container
if (mouseMove.left + offset.left + tipWidth > chartXOffset + chartWidth &&
mouseMove.left - tipWidth - 10 > chartXOffset) {
offset.left = -10 - tipWidth;
}
// change yOffset to keep tooltip within container
if ((mouseMove.top + tipHeight - 10) > (chartYOffset + chartHeight)) {
if (mouseMove.top + tipHeight - 10 > chartYOffset + chartHeight) {
offset.top = chartYOffset + chartHeight;
}
}
return offset;
};

View file

@ -212,8 +212,8 @@ p.rows-label, p.columns-label {
stroke: 3px;
}
.k4tip, .vis-tooltip {
display: none;
.vis-tooltip {
visibility: hidden;
line-height: 1.1;
font-size: 12px;
font-weight: normal;

View file

@ -16,9 +16,4 @@
@sidebar-active-color: @component-active-color;
@sidebar-active-bg: @component-active-bg;
@sidebar-active-hover-bg: @component-active-bg;
@sidebar-active-hover-color: @component-active-color;
@vis-editor-sidebar-basis: (100/12) * 2%; // two of twelve columns
@vis-editor-sidebar-min-width: 350px;
@vis-editor-nesting-width: 8px;
@vis-editor-agg-editor-spacing: 5px;
@sidebar-active-hover-color: @component-active-color;

View file

@ -107,6 +107,7 @@
'specs/factories/base_object',
'specs/state_management/state',
'specs/state_management/global_state',
'specs/state_management/app_state',
'specs/utils/diff_object',
'specs/factories/events',
'specs/vislib/color',

View file

@ -0,0 +1,58 @@
define(function (require) {
var sinon = require('test_utils/auto_release_sinon');
require('components/state_management/app_state');
describe('State Management', function () {
var $rootScope, AppState;
beforeEach(function () {
module('kibana');
inject(function (_$rootScope_, _$location_, Private) {
$rootScope = _$rootScope_;
AppState = Private(require('components/state_management/app_state'));
});
});
describe('App State', function () {
var appState;
beforeEach(function () {
appState = new AppState();
});
it('should have _urlParam of _a', function () {
expect(appState).to.have.property('_urlParam');
expect(appState._urlParam).to.equal('_a');
});
it('should use passed in params', function () {
var params = {
test: true,
mock: false
};
appState = new AppState(params);
expect(appState).to.have.property('_defaults');
Object.keys(params).forEach(function (key) {
expect(appState._defaults).to.have.property(key);
expect(appState._defaults[key]).to.equal(params[key]);
});
});
it('should have a destroy method', function () {
expect(appState).to.have.property('destroy');
});
it('should be destroyed on $routeChangeStart', function () {
var destroySpy = sinon.spy(appState, 'destroy');
var url = '/test/path';
$rootScope.$emit('$routeChangeStart');
expect(destroySpy.callCount).to.be(1);
});
});
});
});