mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge pull request #1773 from stormpython/feature/fix_events
Changes the way Events are handled in the Visualization Library
This commit is contained in:
commit
f7ddf50a15
21 changed files with 910 additions and 725 deletions
|
@ -34,6 +34,9 @@ define(function (require) {
|
|||
}
|
||||
|
||||
this.data = data;
|
||||
this.type = this.getDataType();
|
||||
this.labels = (this.type === 'series') ? getLabels(data) : this.pieNames();
|
||||
this.color = color(this.labels);
|
||||
this._normalizeOrdered();
|
||||
|
||||
this._attr = _.defaults(attr || {}, {
|
||||
|
@ -46,6 +49,21 @@ define(function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
Data.prototype.getDataType = function () {
|
||||
var data = this.getVisData();
|
||||
var type;
|
||||
|
||||
data.forEach(function (obj) {
|
||||
if (obj.series) {
|
||||
type = 'series';
|
||||
} else if (obj.slices) {
|
||||
type = 'slices';
|
||||
}
|
||||
});
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of the actual x and y data value objects
|
||||
* from data with series keys
|
||||
|
|
|
@ -8,22 +8,16 @@ define(function (require) {
|
|||
* @class Dispatch
|
||||
* @constructor
|
||||
* @param handler {Object} Reference to Handler Class Object
|
||||
* @param chartData {Object} Elasticsearch data object
|
||||
*/
|
||||
|
||||
function Dispatch(handler, chartData) {
|
||||
function Dispatch(handler) {
|
||||
if (!(this instanceof Dispatch)) {
|
||||
return new Dispatch(handler, chartData);
|
||||
return new Dispatch(handler);
|
||||
}
|
||||
var type = handler._attr.type;
|
||||
|
||||
this.handler = handler;
|
||||
this.chartData = chartData;
|
||||
this.color = type === 'pie' ? handler.data.getPieColorFunc() : handler.data.getColorFunc();
|
||||
this._attr = _.defaults(handler._attr || {}, {
|
||||
yValue: function (d) { return d.y; },
|
||||
dispatch: d3.dispatch('brush', 'click', 'hover', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout')
|
||||
});
|
||||
this.dispatch = d3.dispatch('brush', 'click', 'hover', 'mouseup',
|
||||
'mousedown', 'mouseover');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,21 +25,25 @@ define(function (require) {
|
|||
*
|
||||
* @param d {Object} Data point
|
||||
* @param i {Number} Index number of data point
|
||||
* @returns {{value: *, point: *, label: *, color: *, pointIndex: *, series: *, config: *, data: (Object|*),
|
||||
* @returns {{value: *, point: *, label: *, color: *, pointIndex: *,
|
||||
* series: *, config: *, data: (Object|*),
|
||||
* e: (d3.event|*), handler: (Object|*)}} Event response object
|
||||
*/
|
||||
Dispatch.prototype.eventResponse = function (d, i) {
|
||||
var isPercentage = (this._attr.mode === 'percentage');
|
||||
var label = d.label;
|
||||
var getYValue = this._attr.yValue;
|
||||
var color = this.color;
|
||||
var chartData = this.chartData;
|
||||
var attr = this._attr;
|
||||
var data = d3.event.target.nearestViewportElement.__data__;
|
||||
var label = d.label ? d.label : d.name;
|
||||
var isSeries = !!(data.series);
|
||||
var isSlices = !!(data.slices);
|
||||
var series = isSeries ? data.series : undefined;
|
||||
var slices = isSlices ? data.slices : undefined;
|
||||
var handler = this.handler;
|
||||
var color = handler.data.color;
|
||||
var isPercentage = (handler._attr.mode === 'percentage');
|
||||
|
||||
if (isSeries) {
|
||||
|
||||
if (chartData.series) {
|
||||
// Find object with the actual d value and add it to the point object
|
||||
var object = _.find(chartData.series, { 'label': label });
|
||||
var object = _.find(series, { 'label': d.label });
|
||||
d.value = +object.values[i].y;
|
||||
|
||||
if (isPercentage) {
|
||||
|
@ -56,50 +54,125 @@ define(function (require) {
|
|||
}
|
||||
|
||||
return {
|
||||
value: getYValue(d, i),
|
||||
value: d.y,
|
||||
point: d,
|
||||
label: label,
|
||||
color: color(label),
|
||||
pointIndex: i,
|
||||
series: chartData.series,
|
||||
config: attr,
|
||||
data: chartData,
|
||||
series: series,
|
||||
slices: slices,
|
||||
config: handler._attr,
|
||||
data: data,
|
||||
e: d3.event,
|
||||
handler: handler
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Response to click and hover events for pie charts
|
||||
* Returns a function that adds events and listeners to a D3 selection
|
||||
*
|
||||
* @param d {Object} Data point
|
||||
* @param i {Number} Index number of data point
|
||||
* @returns {{value: (d.value|*), point: *, label: (d.name|*), color: *, pointIndex: *, children: *, parent: *,
|
||||
* appConfig: *, config: *, data: (Object|*), e: (d3.event|*), handler: (Object|*)}} Event response object
|
||||
* @method addEvent
|
||||
* @param event {String}
|
||||
* @param callback {Function}
|
||||
* @returns {Function}
|
||||
*/
|
||||
Dispatch.prototype.pieResponse = function (d, i) {
|
||||
var label = d.name;
|
||||
var color = this.color;
|
||||
var chartData = this.chartData;
|
||||
var attr = this._attr;
|
||||
var handler = this.handler;
|
||||
Dispatch.prototype.addEvent = function (event, callback) {
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
var element = d3.select(this);
|
||||
|
||||
return {
|
||||
value: d.value,
|
||||
point: d,
|
||||
label: label,
|
||||
color: color(label),
|
||||
pointIndex: i,
|
||||
children: d.children ? d.children : undefined,
|
||||
parent: d.parent ? d.parent : undefined,
|
||||
appConfig: d.appConfig,
|
||||
config: attr,
|
||||
data: chartData,
|
||||
e: d3.event,
|
||||
handler: handler
|
||||
if (typeof callback === 'function') {
|
||||
return element.on(event, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @method addHoverEvent
|
||||
* @returns {Function}
|
||||
*/
|
||||
Dispatch.prototype.addHoverEvent = function () {
|
||||
var self = this;
|
||||
var isClickable = (this.dispatch.on('click'));
|
||||
var addEvent = this.addEvent;
|
||||
|
||||
function hover(d, i) {
|
||||
d3.event.stopPropagation();
|
||||
|
||||
// Add pointer if item is clickable
|
||||
if (isClickable) {
|
||||
self.addMousePointer.call(this, arguments);
|
||||
}
|
||||
|
||||
self.dispatch.hover.call(this, self.eventResponse(d, i));
|
||||
}
|
||||
|
||||
return addEvent('mouseover', hover);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @method addClickEvent
|
||||
* @returns {Function}
|
||||
*/
|
||||
Dispatch.prototype.addClickEvent = function () {
|
||||
var self = this;
|
||||
var addEvent = this.addEvent;
|
||||
|
||||
function click(d, i) {
|
||||
d3.event.stopPropagation();
|
||||
self.dispatch.click.call(this, self.eventResponse(d, i));
|
||||
}
|
||||
|
||||
return addEvent('click', click);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param svg
|
||||
* @returns {Function}
|
||||
*/
|
||||
Dispatch.prototype.addBrushEvent = function (svg) {
|
||||
var dispatch = this.dispatch;
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var isBrushable = (dispatch.on('brush'));
|
||||
var brush = this.createBrush(xScale, svg);
|
||||
var addEvent = this.addEvent;
|
||||
|
||||
function brushEnd() {
|
||||
var bar = d3.select(this);
|
||||
var startX = d3.mouse(svg.node());
|
||||
var startXInv = xScale.invert(startX[0]);
|
||||
|
||||
// Reset the brush value
|
||||
brush.extent([startXInv, startXInv]);
|
||||
|
||||
// Magic!
|
||||
// Need to call brush on svg to see brush when brushing
|
||||
// while on top of bars.
|
||||
// Need to call brush on bar to allow the click event to be registered
|
||||
svg.call(brush);
|
||||
bar.call(brush);
|
||||
}
|
||||
|
||||
if (isBrushable) {
|
||||
return addEvent('mousedown', brushEnd);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mouse over Behavior
|
||||
*
|
||||
* @method addMousePointer
|
||||
* @returns {D3.Selection}
|
||||
*/
|
||||
Dispatch.prototype.addMousePointer = function () {
|
||||
return d3.select(this).style('cursor', 'pointer');
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds D3 brush to SVG and returns the brush function
|
||||
*
|
||||
|
@ -107,37 +180,34 @@ define(function (require) {
|
|||
* @param svg {HTMLElement} Reference to SVG
|
||||
* @returns {*} Returns a D3 brush function and a SVG with a brush group attached
|
||||
*/
|
||||
Dispatch.prototype.addBrush = function (xScale, svg) {
|
||||
var dispatch = this._attr.dispatch;
|
||||
var attr = this._attr;
|
||||
var chartData = this.chartData;
|
||||
var isBrush = this._attr.addBrushing;
|
||||
var height = this._attr.height;
|
||||
var margin = this._attr.margin;
|
||||
Dispatch.prototype.createBrush = function (xScale, svg) {
|
||||
var dispatch = this.dispatch;
|
||||
var attr = this.handler._attr;
|
||||
var height = attr.height;
|
||||
var margin = attr.margin;
|
||||
|
||||
// Brush scale
|
||||
var brush = d3.svg.brush()
|
||||
.x(xScale)
|
||||
.on('brushend', function brushEnd() {
|
||||
// response returned on brush
|
||||
return dispatch.brush({
|
||||
range: brush.extent(),
|
||||
config: attr,
|
||||
e: d3.event,
|
||||
data: chartData
|
||||
data: d3.event.sourceEvent.target.__data__
|
||||
});
|
||||
});
|
||||
|
||||
// if `addBrushing` is true, add brush canvas
|
||||
if (isBrush) {
|
||||
svg.append('g')
|
||||
.attr('class', 'brush')
|
||||
.call(brush)
|
||||
.selectAll('rect')
|
||||
.attr('height', height - margin.top - margin.bottom);
|
||||
}
|
||||
if (dispatch.on('brush')) {
|
||||
svg.insert('g', 'g')
|
||||
.attr('class', 'brush')
|
||||
.call(brush)
|
||||
.selectAll('rect')
|
||||
.attr('height', height - margin.top - margin.bottom);
|
||||
|
||||
return brush;
|
||||
return brush;
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch;
|
||||
|
|
|
@ -23,6 +23,8 @@ define(function (require) {
|
|||
this.vis = vis;
|
||||
this.el = vis.el;
|
||||
this.ChartClass = vis.ChartClass;
|
||||
this.charts = [];
|
||||
|
||||
this._attr = _.defaults(vis._attr || {}, {
|
||||
'margin' : { top: 10, right: 3, bottom: 5, left: 3 }
|
||||
});
|
||||
|
@ -58,35 +60,74 @@ define(function (require) {
|
|||
var self = this;
|
||||
var charts = this.charts = [];
|
||||
|
||||
_.forEach(this.renderArray, function (property) {
|
||||
this.renderArray.forEach(function (property) {
|
||||
if (typeof property.render === 'function') {
|
||||
property.render();
|
||||
}
|
||||
});
|
||||
|
||||
// render the chart(s)
|
||||
d3.select(this.el)
|
||||
.selectAll('.chart')
|
||||
.each(function (chartData) {
|
||||
var chart = new self.ChartClass(self, this, chartData);
|
||||
.selectAll('.chart')
|
||||
.each(function (chartData) {
|
||||
var chart = new self.ChartClass(self, this, chartData);
|
||||
var enabledEvents;
|
||||
|
||||
d3.rebind(chart, chart._attr.dispatch, 'on');
|
||||
/*
|
||||
* inside handler: if there are charts, bind events to charts
|
||||
* functionality: track in array that event is enabled
|
||||
* clean up event handlers every time it destroys the chart
|
||||
* rebind them every time it creates the charts
|
||||
*/
|
||||
if (chart.events.dispatch) {
|
||||
enabledEvents = self.vis.eventTypes.enabled;
|
||||
|
||||
// Bubble events up to the Vis Class and Events Class
|
||||
chart.on('click', function (e) {
|
||||
self.vis.emit('click', e);
|
||||
});
|
||||
// Copy dispatch.on methods to chart object
|
||||
d3.rebind(chart, chart.events.dispatch, 'on');
|
||||
|
||||
chart.on('hover', function (e) {
|
||||
self.vis.emit('hover', e);
|
||||
});
|
||||
// Bind events to chart(s)
|
||||
if (enabledEvents.length) {
|
||||
enabledEvents.forEach(function (event) {
|
||||
self.enable(event, chart);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
chart.on('brush', function (e) {
|
||||
self.vis.emit('brush', e);
|
||||
});
|
||||
charts.push(chart);
|
||||
chart.render();
|
||||
});
|
||||
};
|
||||
|
||||
charts.push(chart);
|
||||
chart.render();
|
||||
});
|
||||
|
||||
/**
|
||||
* Enables events, i.e. binds specific events to the chart
|
||||
* object(s) `on` method. For example, `click` or `mousedown` events.
|
||||
* Emits the event to the Events class.
|
||||
*
|
||||
* @method enable
|
||||
* @param event {String} Event type
|
||||
* @param chart {Object} Chart
|
||||
* @returns {*}
|
||||
*/
|
||||
Handler.prototype.enable = function (event, chart) {
|
||||
return chart.on(event, function (e) {
|
||||
this.vis.emit(event, e);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables events by passing null to the event listener.
|
||||
* According to the D3 documentation for event handling:
|
||||
* https://github.com/mbostock/d3/wiki/Selections#on, to remove all
|
||||
* listeners for a particular event type, pass null as the listener.
|
||||
*
|
||||
* @method disable
|
||||
* @param event {String} Event type
|
||||
* @param chart {Object} Chart
|
||||
* @returns {*}
|
||||
*/
|
||||
Handler.prototype.disable = function (event, chart) {
|
||||
return chart.on(event, null);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -106,7 +106,6 @@ define(function (require) {
|
|||
var visEl = d3.select(this.el);
|
||||
var legendDiv = visEl.select('.' + this._attr.legendClass);
|
||||
var items = this.labels;
|
||||
|
||||
this.header(legendDiv, this);
|
||||
this.list(legendDiv, items, this);
|
||||
|
||||
|
@ -117,15 +116,14 @@ define(function (require) {
|
|||
.on('click', function legendClick() {
|
||||
if (self._attr.isOpen) {
|
||||
// close legend
|
||||
visEl.select('ul.legend-ul')
|
||||
.classed('hidden', true);
|
||||
visEl.select('ul.legend-ul').classed('hidden', true);
|
||||
self._attr.isOpen = false;
|
||||
|
||||
// need to add reference to resize function on toggle
|
||||
self.vis.resize();
|
||||
} else {
|
||||
// open legend
|
||||
visEl.select('ul.legend-ul')
|
||||
.classed('hidden', false);
|
||||
visEl.select('ul.legend-ul').classed('hidden', false);
|
||||
self._attr.isOpen = true;
|
||||
|
||||
// need to add reference to resize function on toggle
|
||||
|
@ -147,7 +145,7 @@ define(function (require) {
|
|||
* The default opacity of elements in charts may be modified by the
|
||||
* chart constructor, and so may differ from that of the legend
|
||||
*/
|
||||
visEl.select('.chart')
|
||||
visEl.selectAll('.chart')
|
||||
.selectAll('.color')
|
||||
.style('opacity', self._attr.defaultOpacity);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function TooltipFactory(d3) {
|
||||
return function TooltipFactory(d3, Private) {
|
||||
var $ = require('jquery');
|
||||
|
||||
require('css!components/vislib/styles/main');
|
||||
|
@ -45,11 +45,11 @@ define(function (require) {
|
|||
|
||||
var tooltipDiv = d3.select('.' + self.tooltipClass);
|
||||
|
||||
selection.each(function (data, i) {
|
||||
selection.each(function () {
|
||||
var element = d3.select(this);
|
||||
|
||||
element
|
||||
.on('mousemove.tip', function (d) {
|
||||
.on('mousemove.tip', function (d, i) {
|
||||
var placement = self.getTooltipPlacement(d3.event);
|
||||
var events = self.events ? self.events.eventResponse(d, i) : d;
|
||||
|
||||
|
|
12
src/kibana/components/vislib/styles/_error.less
Normal file
12
src/kibana/components/vislib/styles/_error.less
Normal file
|
@ -0,0 +1,12 @@
|
|||
@import (reference) "lesshat.less";
|
||||
|
||||
.error {
|
||||
.flex(1 1 100%);
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
margin-top: 15%;
|
||||
font-size: 18px;
|
||||
text-wrap: wrap;
|
||||
}
|
||||
}
|
144
src/kibana/components/vislib/styles/_layout.less
Normal file
144
src/kibana/components/vislib/styles/_layout.less
Normal file
|
@ -0,0 +1,144 @@
|
|||
@import (reference) "lesshat.less";
|
||||
|
||||
.visualize-chart {
|
||||
.display(flex);
|
||||
.flex(1 1 100%);
|
||||
}
|
||||
|
||||
.vis-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 1 100%);
|
||||
.flex-direction(row);
|
||||
margin: 10px 0 0 6px;
|
||||
}
|
||||
|
||||
/* YAxis logic */
|
||||
.y-axis-col-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.y-axis-col {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(1 0 50px);
|
||||
}
|
||||
|
||||
.y-axis-spacer-block {
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
.y-axis-div-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
width: 38px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.y-axis-div {
|
||||
.flex(1 1 25px);
|
||||
min-width: 14px;
|
||||
min-height: 14px;
|
||||
}
|
||||
|
||||
.y-axis-title {
|
||||
min-height: 14px;
|
||||
min-width: 14px;
|
||||
}
|
||||
|
||||
.y-axis-chart-title {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
min-height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.y-axis-title text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
.flex(1 1 100%);
|
||||
min-height: 14px;
|
||||
min-width: 14px;
|
||||
}
|
||||
|
||||
.chart-title text {
|
||||
font-size: 11px;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.vis-col-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
overflow: visible;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
||||
.chart-wrapper-column {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
.flex-direction(row);
|
||||
}
|
||||
|
||||
.chart-wrapper-row {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(1 0 100%);
|
||||
}
|
||||
|
||||
.chart {
|
||||
.flex(1 1 100%);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
.chart-column {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
.x-axis-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
min-height: 45px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.x-axis-div-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
min-height: 15px;
|
||||
}
|
||||
|
||||
.x-axis-chart-title {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
min-height: 15px;
|
||||
max-height: 15px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.x-axis-title {
|
||||
min-height: 15px;
|
||||
max-height: 15px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.x-axis-title text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.x-axis-div {
|
||||
margin-top: -5px;
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
}
|
59
src/kibana/components/vislib/styles/_legend.less
Normal file
59
src/kibana/components/vislib/styles/_legend.less
Normal file
|
@ -0,0 +1,59 @@
|
|||
@import (reference) "lesshat.less";
|
||||
|
||||
.legend-col-wrapper {
|
||||
.flex(0 1 auto);
|
||||
z-index: 10;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
min-width: 40px;
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
margin: 0 0 14px 0;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.legend-ul {
|
||||
list-style-type: none;
|
||||
margin: 0 0 0 14px;
|
||||
padding: 0;
|
||||
visibility: visible;
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
|
||||
li.color {
|
||||
min-height: 22px;
|
||||
margin: 0 18px 0 18px;
|
||||
padding: 3px 0 3px 0;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
line-height: 13px;
|
||||
color: #666;
|
||||
cursor: default;
|
||||
text-align: left;
|
||||
.flex-grow(2);
|
||||
word-wrap: break-word;
|
||||
max-width: 150px;
|
||||
|
||||
.dots {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
margin: 1px 0 0 -14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legend-ul.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.legend-toggle {
|
||||
position: relative;
|
||||
float: right;
|
||||
right: 9px;
|
||||
top: 9px;
|
||||
}
|
||||
}
|
63
src/kibana/components/vislib/styles/_svg.less
Normal file
63
src/kibana/components/vislib/styles/_svg.less
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* Axis Styles */
|
||||
.axis {
|
||||
shape-rendering: crispEdges;
|
||||
stroke-width: 1px;
|
||||
|
||||
line, path {
|
||||
stroke: #ddd;
|
||||
fill: none;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
}
|
||||
|
||||
.x.axis path {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tick text {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
/* Brush Styling */
|
||||
.brush .extent {
|
||||
stroke: #fff;
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
/* SVG Element Default Styling */
|
||||
rect {
|
||||
stroke: none;
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
stroke: #333;
|
||||
}
|
||||
}
|
||||
|
||||
circle {
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
stroke: #333;
|
||||
}
|
||||
}
|
||||
|
||||
path {
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Visualization Styling */
|
||||
.line {
|
||||
circle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
stroke: #333;
|
||||
}
|
||||
}
|
36
src/kibana/components/vislib/styles/_tooltip.less
Normal file
36
src/kibana/components/vislib/styles/_tooltip.less
Normal file
|
@ -0,0 +1,36 @@
|
|||
@import (reference) "../../../styles/main";
|
||||
|
||||
.vis-tooltip {
|
||||
visibility: hidden;
|
||||
line-height: 1.1;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 8px;
|
||||
background: rgba(70, 82, 93, 0.95);
|
||||
color: @gray-lighter;
|
||||
border-radius: 4px;
|
||||
position: fixed;
|
||||
z-index: 120;
|
||||
word-wrap: break-word;
|
||||
max-width: 40%;
|
||||
|
||||
table {
|
||||
td,th {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
// if there is a header, give it a border that matches
|
||||
// those in the body
|
||||
thead tr {
|
||||
border-bottom: 1px solid @gray;
|
||||
}
|
||||
|
||||
// only apply to tr in the body, not the header
|
||||
tbody tr {
|
||||
border-top: 1px solid @gray;
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,410 +1,5 @@
|
|||
@import (reference) "../../../styles/main.less";
|
||||
@import (reference) "lesshat.less";
|
||||
|
||||
/* vislib styles */
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
div.columns {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(1 1 100%);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.rows {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(1 1 100%);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.row-labels, .column-labels {
|
||||
margin: 0;
|
||||
padding: 10;
|
||||
}
|
||||
|
||||
visualize {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.visualize-chart {
|
||||
.display(flex);
|
||||
.flex(1 1 100%);
|
||||
}
|
||||
|
||||
.visualize-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
}
|
||||
|
||||
.y-axis-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 1);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.y-axis-filler-div {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
div.x-axis-label {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
bottom: 15px;
|
||||
font-size: 7pt;
|
||||
color: #848e96;
|
||||
}
|
||||
|
||||
div.y-axis-label {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: 42.5%;
|
||||
font-size: 7pt;
|
||||
color: #848e96;
|
||||
-webkit-transform: rotate(270deg);
|
||||
-moz-transform: rotate(270deg);
|
||||
-o-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
/* legend */
|
||||
.legend-col-wrapper {
|
||||
.flex(0 1 auto);
|
||||
z-index: 10;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
.legend {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
.legend-ul {
|
||||
list-style-type: none;
|
||||
margin: 0 0 0 14px;
|
||||
padding: 0;
|
||||
visibility: visible;
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.legend-ul.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
li.color {
|
||||
min-height: 22px;
|
||||
margin: 0 18px 0 18px;
|
||||
padding: 3px 0 3px 0;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
line-height: 13px;
|
||||
color: #666;
|
||||
cursor: default;
|
||||
text-align: left;
|
||||
.flex-grow(2);
|
||||
word-wrap: break-word;
|
||||
max-width: 150px;
|
||||
}
|
||||
.wordwrap {
|
||||
word-wrap: break-word;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.dots {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
margin: 1px 0 0 -14px;
|
||||
}
|
||||
|
||||
.legend-toggle {
|
||||
position: relative;
|
||||
float: right;
|
||||
right: 9px;
|
||||
top: 9px;
|
||||
}
|
||||
|
||||
.column-labels {
|
||||
color: #777;
|
||||
font-size: 10pt;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 1.0em;
|
||||
line-height: 2.0em;
|
||||
}
|
||||
|
||||
/* histogram axis and label styles */
|
||||
.vis-canvas {
|
||||
}
|
||||
|
||||
.chart-bkgd {
|
||||
fill: #ffffff;
|
||||
}
|
||||
|
||||
p.rows-label, p.columns-label {
|
||||
display: block;
|
||||
background: #eff3f4;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 9pt;
|
||||
fill: #46525d;
|
||||
text-align: center;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
.charts-label {
|
||||
font-size: 8pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.x-axis-label, .y-axis-label {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.tick text {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.axis {
|
||||
shape-rendering: crispEdges;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.axis line, .axis path {
|
||||
stroke: #ddd;
|
||||
fill: none;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.legend-box {
|
||||
fill: #ffffff;
|
||||
}
|
||||
|
||||
.brush .extent {
|
||||
stroke: #fff;
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.layer .rect:hover {
|
||||
stroke: 3px;
|
||||
}
|
||||
|
||||
.vis-tooltip {
|
||||
visibility: hidden;
|
||||
line-height: 1.1;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 8px;
|
||||
background: rgba(70, 82, 93, 0.95);
|
||||
color: @gray-lighter;
|
||||
border-radius: 4px;
|
||||
position: fixed;
|
||||
z-index: 120;
|
||||
word-wrap: break-word;
|
||||
max-width: 40%;
|
||||
|
||||
table {
|
||||
td,th {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
// if there is a header, give it a border that matches
|
||||
// those in the body
|
||||
thead tr {
|
||||
border-bottom: 1px solid @gray;
|
||||
}
|
||||
|
||||
// only apply to tr in the body, not the header
|
||||
tbody tr {
|
||||
border-top: 1px solid @gray;
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rect {
|
||||
stroke: transparent;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.rect.hover {
|
||||
stroke: #333;
|
||||
}
|
||||
|
||||
/* Flex Box Layout */
|
||||
.vis-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 1 100%);
|
||||
.flex-direction(row);
|
||||
margin: 10px 0 0 6px;
|
||||
}
|
||||
|
||||
.error {
|
||||
.flex(1 1 100%);
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
margin-top: 15%;
|
||||
font-size: 18px;
|
||||
text-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* YAxis logic */
|
||||
.y-axis-col-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.y-axis-col {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(1 0 50px);
|
||||
}
|
||||
|
||||
.y-axis-spacer-block {
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
.y-axis-div-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
width: 38px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.y-axis-div {
|
||||
.flex(1 1 25px);
|
||||
min-width: 14px;
|
||||
min-height: 14px;
|
||||
}
|
||||
|
||||
.y-axis-title {
|
||||
min-height: 14px;
|
||||
min-width: 14px;
|
||||
}
|
||||
|
||||
.y-axis-chart-title {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
min-height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.y-axis-title text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
.flex(1 1 100%);
|
||||
min-height: 14px;
|
||||
min-width: 14px;
|
||||
}
|
||||
|
||||
.chart-title text {
|
||||
font-size: 11px;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.vis-col-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
overflow: visible;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
||||
.chart-wrapper-column {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
.flex-direction(row);
|
||||
}
|
||||
|
||||
.chart-wrapper-row {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(1 0 100%);
|
||||
}
|
||||
|
||||
.chart {
|
||||
.flex(1 1 100%);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
.chart-column {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
.x-axis-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
min-height: 45px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.x-axis-div-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
min-height: 15px;
|
||||
}
|
||||
|
||||
.x-axis-chart-title {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
min-height: 15px;
|
||||
max-height: 15px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.x-axis-title {
|
||||
min-height: 15px;
|
||||
max-height: 15px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.x-axis-title text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.x-axis-div {
|
||||
margin-top: -5px;
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.x.axis path {
|
||||
display: none;
|
||||
}
|
||||
@import "_error";
|
||||
@import "_layout";
|
||||
@import "_legend";
|
||||
@import "_svg";
|
||||
@import "_tooltip";
|
||||
|
|
|
@ -27,6 +27,9 @@ define(function (require) {
|
|||
this.el = $el.get ? $el.get(0) : $el;
|
||||
this.ChartClass = chartTypes[config.type];
|
||||
this._attr = _.defaults(config || {}, {});
|
||||
this.eventTypes = {
|
||||
enabled: []
|
||||
};
|
||||
|
||||
// bind the resize function so it can be used as an event handler
|
||||
this.resize = _.bind(this.resize, this);
|
||||
|
@ -81,6 +84,7 @@ define(function (require) {
|
|||
/**
|
||||
* Destroys the visualization
|
||||
* Removes chart and all elements associated with it.
|
||||
* Removes chart and all elements associated with it.
|
||||
* Remove event listeners and pass destroy call down to owned objects.
|
||||
*
|
||||
* @method destroy
|
||||
|
@ -114,6 +118,68 @@ define(function (require) {
|
|||
return this._attr[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Turns on event listeners.
|
||||
*
|
||||
* @param event {String}
|
||||
* @param listener{Function}
|
||||
* @returns {*}
|
||||
*/
|
||||
Vis.prototype.on = function (event, listener) {
|
||||
var ret = Events.prototype.on.call(this, event, listener); // Adds event to _listeners array
|
||||
var listeners = this._listeners[event].length;
|
||||
var charts = (this.handler && this.handler.charts);
|
||||
var chartCount = charts ? charts.length : 0;
|
||||
var enabledEvents = this.eventTypes.enabled;
|
||||
var eventAbsent = (enabledEvents.indexOf(event) === -1);
|
||||
|
||||
// if this is the first listener added for the event
|
||||
// and charts are available, bind the event to the chart(s)
|
||||
// `on` method
|
||||
if (listeners === 1 && chartCount > 0) {
|
||||
charts.forEach(function (chart) {
|
||||
this.handler.enable(event, chart);
|
||||
}, this);
|
||||
}
|
||||
|
||||
// Keep track of enabled events
|
||||
if (eventAbsent) {
|
||||
enabledEvents.push(event);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Turns off event listeners.
|
||||
*
|
||||
* @param event {String}
|
||||
* @param listener{Function}
|
||||
* @returns {*}
|
||||
*/
|
||||
Vis.prototype.off = function (event, listener) {
|
||||
var ret = Events.prototype.off.call(this, event, listener); // Removes event from _listeners array
|
||||
var listeners = (!!this._listeners[event] && this._listeners[event].length !== 0);
|
||||
var charts = (this.handler && this.handler.charts);
|
||||
var chartCount = charts ? charts.length : 0;
|
||||
var eventIndex = this.eventTypes.enabled.indexOf(event);
|
||||
var eventPresent = (eventIndex !== -1);
|
||||
|
||||
// Once the listener array reaches zero, turn off event
|
||||
if (!listeners && eventPresent) {
|
||||
if (chartCount > 0) {
|
||||
charts.forEach(function (chart) {
|
||||
this.handler.disable(event, chart);
|
||||
}, this);
|
||||
}
|
||||
|
||||
// Remove event from enabled array
|
||||
this.eventTypes.enabled.splice(eventIndex, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
return Vis;
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@ define(function (require) {
|
|||
this.chartEl = el;
|
||||
this.chartData = chartData;
|
||||
|
||||
var events = this.events = new Dispatch(handler, chartData);
|
||||
var events = this.events = new Dispatch(handler);
|
||||
|
||||
if (handler._attr.addTooltip) {
|
||||
var $el = this.handler.el;
|
||||
|
|
|
@ -136,38 +136,15 @@ define(function (require) {
|
|||
* Adds Events to SVG circles
|
||||
*
|
||||
* @method addCircleEvents
|
||||
* @param circles {D3.UpdateSelection} SVG circles
|
||||
* @returns {HTMLElement} circles with event listeners attached
|
||||
* @param element {D3.UpdateSelection} SVG circles
|
||||
* @returns {D3.Selection} circles with event listeners attached
|
||||
*/
|
||||
AreaChart.prototype.addCircleEvents = function (circles) {
|
||||
AreaChart.prototype.addCircleEvents = function (element) {
|
||||
var events = this.events;
|
||||
var dispatch = this.events._attr.dispatch;
|
||||
|
||||
circles
|
||||
.on('mouseover.circle', function mouseOverCircle(d, i) {
|
||||
var circle = this;
|
||||
|
||||
d3.select(circle)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer')
|
||||
.style('opacity', 1);
|
||||
|
||||
dispatch.hover(events.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('click.circle', function clickCircle(d, i) {
|
||||
dispatch.click(events.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout.circle', function mouseOutCircle() {
|
||||
var circle = this;
|
||||
|
||||
d3.select(circle)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null)
|
||||
.style('opacity', 0);
|
||||
});
|
||||
return element
|
||||
.call(events.addHoverEvent())
|
||||
.call(events.addClickEvent());
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -179,7 +156,6 @@ define(function (require) {
|
|||
* @returns {D3.UpdateSelection} SVG with circles added
|
||||
*/
|
||||
AreaChart.prototype.addCircles = function (svg, data) {
|
||||
var self = this;
|
||||
var color = this.handler.data.getColorFunc();
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
|
@ -196,7 +172,7 @@ define(function (require) {
|
|||
.data(data)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'points');
|
||||
.attr('class', 'points area');
|
||||
|
||||
// Append the bars
|
||||
circles = layer
|
||||
|
@ -237,8 +213,7 @@ define(function (require) {
|
|||
}
|
||||
return yScale(d.y0 + d.y);
|
||||
})
|
||||
.attr('r', circleRadius)
|
||||
.style('opacity', 0);
|
||||
.attr('r', circleRadius);
|
||||
|
||||
// Add tooltip
|
||||
if (isTooltip) {
|
||||
|
@ -328,9 +303,6 @@ define(function (require) {
|
|||
// add clipPath to hide circles when they go out of bounds
|
||||
self.addClipPath(svg, width, height);
|
||||
|
||||
// addBrush canvas
|
||||
self.events.addBrush(xScale, svg);
|
||||
|
||||
// add path
|
||||
path = self.addPath(svg, layers);
|
||||
|
||||
|
|
|
@ -217,77 +217,27 @@ define(function (require) {
|
|||
|
||||
/**
|
||||
* Adds Events to SVG rect
|
||||
* Visualization is only brushable when a brush event is added
|
||||
* If a brush event is added, then a function should be returned.
|
||||
*
|
||||
* @method addBarEvents
|
||||
* @param svg {HTMLElement} chart SVG
|
||||
* @param bars {D3.UpdateSelection} SVG rect
|
||||
* @param brush {Function} D3 brush function
|
||||
* @returns {HTMLElement} rect with event listeners attached
|
||||
* @param element {D3.UpdateSelection} target
|
||||
* @param svg {D3.UpdateSelection} chart SVG
|
||||
* @returns {D3.Selection} rect with event listeners attached
|
||||
*/
|
||||
ColumnChart.prototype.addBarEvents = function (svg, bars, brush) {
|
||||
var self = this;
|
||||
ColumnChart.prototype.addBarEvents = function (element, svg) {
|
||||
var events = this.events;
|
||||
var dispatch = this.events._attr.dispatch;
|
||||
var addBrush = this._attr.addBrushing;
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var isBrushable = (typeof events.dispatch.on('brush') === 'function');
|
||||
var brush = isBrushable ? events.addBrushEvent(svg) : undefined;
|
||||
var hover = events.addHoverEvent();
|
||||
var click = events.addClickEvent();
|
||||
var attachedEvents = element.call(hover).call(click);
|
||||
|
||||
bars
|
||||
.on('mouseover.bar', function (d, i) {
|
||||
self.mouseOverBar(this);
|
||||
dispatch.hover(events.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mousedown.bar', function () {
|
||||
if (addBrush) {
|
||||
var bar = d3.select(this);
|
||||
var startX = d3.mouse(svg.node());
|
||||
var startXInv = xScale.invert(startX[0]);
|
||||
if (isBrushable) {
|
||||
attachedEvents.call(brush);
|
||||
}
|
||||
|
||||
// Reset the brush value
|
||||
brush.extent([startXInv, startXInv]);
|
||||
|
||||
// Magic!
|
||||
// Need to call brush on svg to see brush when brushing
|
||||
// while on top of bars.
|
||||
// Need to call brush on bar to allow the click event to be registered
|
||||
svg.call(brush);
|
||||
bar.call(brush);
|
||||
}
|
||||
})
|
||||
.on('click.bar', function (d, i) {
|
||||
dispatch.click(events.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout.bar', function () {
|
||||
self.mouseOutBar(this);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mouseover Behavior
|
||||
*
|
||||
* @method mouseOverBar
|
||||
* @param that {Object} Reference to this object
|
||||
* @returns {D3.Selection} this object with '.hover' class true
|
||||
*/
|
||||
ColumnChart.prototype.mouseOverBar = function (that) {
|
||||
return d3.select(that)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
};
|
||||
|
||||
/**
|
||||
* Mouseout Behavior
|
||||
*
|
||||
* @method mouseOutBar
|
||||
* @param that {Object} Reference to this object
|
||||
* @returns {D3.Selection} this object with '.hover' class false
|
||||
*/
|
||||
ColumnChart.prototype.mouseOutBar = function (that) {
|
||||
return d3.select(that)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
return attachedEvents;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -298,20 +248,17 @@ define(function (require) {
|
|||
*/
|
||||
ColumnChart.prototype.draw = function () {
|
||||
var self = this;
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var $elem = $(this.chartEl);
|
||||
var margin = this._attr.margin;
|
||||
var elWidth = this._attr.width = $elem.width();
|
||||
var elHeight = this._attr.height = $elem.height();
|
||||
var minWidth = 20;
|
||||
var minHeight = 20;
|
||||
var isEvents = this._attr.addEvents;
|
||||
var div;
|
||||
var svg;
|
||||
var width;
|
||||
var height;
|
||||
var layers;
|
||||
var brush;
|
||||
var bars;
|
||||
|
||||
return function (selection) {
|
||||
|
@ -333,12 +280,10 @@ define(function (require) {
|
|||
.append('g')
|
||||
.attr('transform', 'translate(0,' + margin.top + ')');
|
||||
|
||||
brush = self.events.addBrush(xScale, svg);
|
||||
bars = self.addBars(svg, layers);
|
||||
|
||||
if (isEvents) {
|
||||
self.addBarEvents(svg, bars, brush);
|
||||
}
|
||||
// Adds event listeners
|
||||
self.addBarEvents(bars, svg);
|
||||
|
||||
var line = svg.append('line')
|
||||
.attr('x1', 0)
|
||||
|
|
|
@ -36,36 +36,15 @@ define(function (require) {
|
|||
* Adds Events to SVG circle
|
||||
*
|
||||
* @method addCircleEvents
|
||||
* @param circles {D3.UpdateSelection} Reference to SVG circle
|
||||
* @returns {D3.UpdateSelection} SVG circles with event listeners attached
|
||||
* @param element{D3.UpdateSelection} Reference to SVG circle
|
||||
* @returns {D3.Selection} SVG circles with event listeners attached
|
||||
*/
|
||||
LineChart.prototype.addCircleEvents = function (circles) {
|
||||
LineChart.prototype.addCircleEvents = function (element) {
|
||||
var events = this.events;
|
||||
var dispatch = this.events._attr.dispatch;
|
||||
|
||||
circles
|
||||
.on('mouseover.circle', function mouseOverCircle(d, i) {
|
||||
var circle = this;
|
||||
|
||||
d3.select(circle)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
dispatch.hover(events.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('click.circle', function clickCircle(d, i) {
|
||||
dispatch.click(events.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout.circle', function mouseOutCircle() {
|
||||
var circle = this;
|
||||
|
||||
d3.select(circle)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
});
|
||||
return element
|
||||
.call(events.addHoverEvent())
|
||||
.call(events.addClickEvent());
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -93,7 +72,7 @@ define(function (require) {
|
|||
.data(data)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'points');
|
||||
.attr('class', 'points line');
|
||||
|
||||
circles = layer
|
||||
.selectAll('rect')
|
||||
|
@ -274,12 +253,8 @@ define(function (require) {
|
|||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
self.addClipPath(svg, width, height);
|
||||
|
||||
self.events.addBrush(xScale, svg);
|
||||
|
||||
lines = self.addLines(svg, data.series);
|
||||
circles = self.addCircles(svg, layers);
|
||||
|
||||
self.addCircleEvents(circles);
|
||||
|
||||
var line = svg
|
||||
|
|
|
@ -24,12 +24,9 @@ define(function (require) {
|
|||
}
|
||||
PieChart.Super.apply(this, arguments);
|
||||
|
||||
this.columns = handler.data.data.raw.columns;
|
||||
|
||||
this._attr = _.defaults(handler._attr || {}, {
|
||||
isDonut: handler._attr.isDonut || false,
|
||||
getSize: function (d) { return d.size; },
|
||||
dispatch: d3.dispatch('brush', 'click', 'hover', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout')
|
||||
getSize: function (d) { return d.size; }
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -37,30 +34,15 @@ define(function (require) {
|
|||
* Adds Events to SVG paths
|
||||
*
|
||||
* @method addPathEvents
|
||||
* @param path {D3.Selection} Reference to SVG path
|
||||
* @param element {D3.Selection} Reference to SVG path
|
||||
* @returns {D3.Selection} SVG path with event listeners attached
|
||||
*/
|
||||
PieChart.prototype.addPathEvents = function (path) {
|
||||
PieChart.prototype.addPathEvents = function (element) {
|
||||
var events = this.events;
|
||||
var dispatch = this.events._attr.dispatch;
|
||||
|
||||
path
|
||||
.on('mouseover.pie', function mouseOverPie(d, i) {
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
dispatch.hover(events.pieResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('click.pie', function clickPie(d, i) {
|
||||
dispatch.click(events.pieResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout.pie', function mouseOutPie() {
|
||||
d3.select(this)
|
||||
.classed('hover', false);
|
||||
});
|
||||
return element
|
||||
.call(events.addHoverEvent())
|
||||
.call(events.addClickEvent());
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -109,7 +91,9 @@ define(function (require) {
|
|||
var isTooltip = this._attr.addTooltip;
|
||||
var self = this;
|
||||
var path;
|
||||
var fieldFormatter = function (d) { return d; };
|
||||
var fieldFormatter = function (label) {
|
||||
return label;
|
||||
};
|
||||
|
||||
path = svg
|
||||
.datum(slices)
|
||||
|
@ -149,7 +133,6 @@ define(function (require) {
|
|||
*/
|
||||
PieChart.prototype.draw = function () {
|
||||
var self = this;
|
||||
var isEvents = this._attr.addEvents;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function (data) {
|
||||
|
@ -160,6 +143,7 @@ define(function (require) {
|
|||
var height = $(el).height();
|
||||
var minWidth = 20;
|
||||
var minHeight = 20;
|
||||
var path;
|
||||
|
||||
if (width <= minWidth || height <= minHeight) {
|
||||
throw new errors.ContainerTooSmall();
|
||||
|
@ -171,11 +155,8 @@ define(function (require) {
|
|||
.append('g')
|
||||
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
|
||||
|
||||
var path = self.addPath(width, height, svg, slices);
|
||||
|
||||
if (isEvents) {
|
||||
self.addPathEvents(path);
|
||||
}
|
||||
path = self.addPath(width, height, svg, slices);
|
||||
self.addPathEvents(path);
|
||||
|
||||
return svg;
|
||||
});
|
||||
|
|
|
@ -640,8 +640,6 @@ define(function (require) {
|
|||
type: 'histogram',
|
||||
vislibParams: {
|
||||
addLegend: false,
|
||||
addEvents: true,
|
||||
addBrushing: true,
|
||||
},
|
||||
listeners: {
|
||||
click: function (e) {
|
||||
|
|
|
@ -6,7 +6,6 @@ module.exports = {
|
|||
'<%= src %>/kibana/components/*/*.less',
|
||||
'<%= src %>/kibana/styles/main.less',
|
||||
'<%= src %>/kibana/components/vislib/styles/main.less',
|
||||
'<%= src %>/kibana/components/**/*.less',
|
||||
'<%= plugins %>/dashboard/styles/main.less',
|
||||
'<%= plugins %>/discover/styles/main.less',
|
||||
'<%= plugins %>/settings/styles/main.less',
|
||||
|
|
|
@ -8,15 +8,6 @@ define(function (require) {
|
|||
angular.module('HandlerFactory', ['kibana']);
|
||||
|
||||
describe('VisLib Handler Test Suite', function () {
|
||||
var Vis;
|
||||
var Data;
|
||||
var Handler;
|
||||
var handler;
|
||||
var ColumnHandler;
|
||||
var vis;
|
||||
var el;
|
||||
var example;
|
||||
var config;
|
||||
var data = {
|
||||
hits : 621,
|
||||
label : '',
|
||||
|
@ -81,7 +72,15 @@ define(function (require) {
|
|||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
var Vis;
|
||||
var Data;
|
||||
var Handler;
|
||||
var handler;
|
||||
var ColumnHandler;
|
||||
var vis;
|
||||
var el;
|
||||
var config;
|
||||
var events;
|
||||
|
||||
beforeEach(function () {
|
||||
module('VisFactory');
|
||||
|
@ -106,54 +105,93 @@ define(function (require) {
|
|||
addLegend: true
|
||||
};
|
||||
|
||||
events = [
|
||||
'click',
|
||||
'brush'
|
||||
];
|
||||
|
||||
vis = new Vis(el[0][0], config);
|
||||
vis.data = data;
|
||||
|
||||
handler = ColumnHandler(vis);
|
||||
|
||||
// handler.render(data);
|
||||
vis.render(data);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
vis.destroy();
|
||||
el.remove();
|
||||
});
|
||||
|
||||
// describe('render Method', function () {
|
||||
// it('should instantiate all constructors ', function () {
|
||||
// expect(!!handler.layout).to.be(true);
|
||||
// expect(!!handler.legend).to.be(true);
|
||||
// expect(!!handler.tooltip).to.be(true);
|
||||
// expect(!!handler.xAxis).to.be(true);
|
||||
// expect(!!handler.yAxis).to.be(true);
|
||||
// expect(!!handler.axisTitle).to.be(true);
|
||||
// expect(!!handler.chartTitle).to.be(true);
|
||||
// });
|
||||
//
|
||||
// it('should append all DOM Elements for the visualization', function () {
|
||||
// expect($('.vis-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-col-wrapper').length).to.be(1);
|
||||
// expect($('.vis-col-wrapper').length).to.be(1);
|
||||
// expect($('.legend-col-wrapper').length).to.be(1);
|
||||
// expect($('.k4tip').length).to.be(1);
|
||||
// expect($('.y-axis-col').length).to.be(1);
|
||||
// expect($('.y-axis-title').length).to.be(1);
|
||||
// expect($('.y-axis-chart-title').length).to.be(0);
|
||||
// expect($('.y-axis-div-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-spacer-block').length).to.be(1);
|
||||
// expect($('.chart-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-div-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-chart-title').length).to.be(0);
|
||||
// expect($('.x-axis-title').length).to.be(1);
|
||||
// expect($('svg').length).to.be(5);
|
||||
// });
|
||||
// });
|
||||
describe('render Method', function () {
|
||||
//it('should instantiate all constructors ', function () {
|
||||
// expect(!!vis.handler.layout).to.be(true);
|
||||
// expect(!!vis.handler.xAxis).to.be(true);
|
||||
// expect(!!vis.handler.yAxis).to.be(true);
|
||||
// expect(!!vis.handler.axisTitle).to.be(true);
|
||||
// expect(!!vis.handler.chartTitle).to.be(true);
|
||||
//});
|
||||
//
|
||||
//it('should append all DOM Elements for the visualization', function () {
|
||||
// expect($('.vis-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-col-wrapper').length).to.be(1);
|
||||
// expect($('.vis-col-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-col').length).to.be(1);
|
||||
// expect($('.y-axis-title').length).to.be(1);
|
||||
// expect($('.y-axis-chart-title').length).to.be(0);
|
||||
// expect($('.y-axis-div-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-spacer-block').length).to.be(1);
|
||||
// expect($('.chart-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-div-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-chart-title').length).to.be(0);
|
||||
// expect($('.x-axis-title').length).to.be(1);
|
||||
// expect($('svg').length).to.be(5);
|
||||
//});
|
||||
});
|
||||
|
||||
describe('enable Method', function () {
|
||||
var charts;
|
||||
|
||||
beforeEach(function () {
|
||||
charts = vis.handler.charts;
|
||||
|
||||
charts.forEach(function (chart) {
|
||||
events.forEach(function (event) {
|
||||
vis.handler.enable(event, chart);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should add events to chart and emit to the Events class', function () {
|
||||
charts.forEach(function (chart, i) {
|
||||
expect(typeof chart.on(events[i])).to.be('function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('disable Method', function () {
|
||||
var charts;
|
||||
|
||||
beforeEach(function () {
|
||||
charts = vis.handler.charts;
|
||||
|
||||
charts.forEach(function (chart) {
|
||||
events.forEach(function (event) {
|
||||
vis.handler.disable(event, chart);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove events from the chart', function () {
|
||||
charts.forEach(function (chart, i) {
|
||||
expect(typeof chart.on(events[i])).to.be('undefined');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('removeAll Method', function () {
|
||||
beforeEach(function () {
|
||||
inject(function () {
|
||||
handler.removeAll(el[0][0]);
|
||||
vis.handler.removeAll(el[0][0]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -164,7 +202,7 @@ define(function (require) {
|
|||
|
||||
describe('error Method', function () {
|
||||
beforeEach(function () {
|
||||
handler.error('This is an error!');
|
||||
vis.handler.error('This is an error!');
|
||||
});
|
||||
|
||||
it('should return an error classed DOM element with a text message', function () {
|
||||
|
|
|
@ -6,6 +6,8 @@ define(function (require) {
|
|||
angular.module('VisFactory', ['kibana']);
|
||||
|
||||
describe('VisLib Vis Test Suite', function () {
|
||||
var beforeEvent = 'click';
|
||||
var afterEvent = 'brush';
|
||||
var Vis;
|
||||
var vis;
|
||||
var el;
|
||||
|
@ -87,8 +89,10 @@ define(function (require) {
|
|||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
vis.off(beforeEvent);
|
||||
vis.off(afterEvent);
|
||||
vis.destroy();
|
||||
el.remove();
|
||||
});
|
||||
|
||||
describe('render Method', function () {
|
||||
|
@ -145,5 +149,176 @@ define(function (require) {
|
|||
expect(vis.get('type')).to.be('histogram');
|
||||
});
|
||||
});
|
||||
|
||||
describe('on Method', function () {
|
||||
var events = [
|
||||
beforeEvent,
|
||||
afterEvent
|
||||
];
|
||||
var listeners;
|
||||
var listener1;
|
||||
var listener2;
|
||||
|
||||
beforeEach(function () {
|
||||
listeners = [];
|
||||
listener1 = function (e) {
|
||||
console.log(e, 'listener1');
|
||||
};
|
||||
listener2 = function (e) {
|
||||
console.log(e, 'listener2');
|
||||
};
|
||||
listeners.push(listener1);
|
||||
listeners.push(listener2);
|
||||
|
||||
// Add event and listeners to chart
|
||||
listeners.forEach(function (listener) {
|
||||
vis.on(beforeEvent, listener);
|
||||
});
|
||||
|
||||
// Render chart
|
||||
vis.render(data);
|
||||
|
||||
// Add event after charts have rendered
|
||||
listeners.forEach(function (listener) {
|
||||
vis.on(afterEvent, listener);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
vis.off(beforeEvent);
|
||||
vis.off(afterEvent);
|
||||
});
|
||||
|
||||
it('should add an event and its listeners to the _listeners object', function () {
|
||||
// Test for presence of beforeEvent in _listener object
|
||||
expect(vis._listeners[beforeEvent] instanceof Array).to.be(true);
|
||||
|
||||
vis._listeners[beforeEvent].forEach(function (listener, i) {
|
||||
expect(typeof listener.handler).to.be('function');
|
||||
expect(listener.handler).to.be(listeners[i]);
|
||||
});
|
||||
|
||||
vis._listeners[afterEvent].forEach(function (listener, i) {
|
||||
expect(typeof listener.handler).to.be('function');
|
||||
expect(listener.handler).to.be(listeners[i]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add an event to the eventTypes.enabled array', function () {
|
||||
vis.eventTypes.enabled.forEach(function (eventType, i) {
|
||||
expect(eventType).to.be(events[i]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should attach an event and its listeners to the chart', function () {
|
||||
var charts = vis.handler.charts;
|
||||
|
||||
charts.forEach(function (chart, i) {
|
||||
expect(typeof chart.on(beforeEvent) === 'function');
|
||||
expect(typeof chart.on(afterEvent) === 'function');
|
||||
expect(chart.on(beforeEvent) === listeners[i]);
|
||||
expect(chart.on(afterEvent) === listeners[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('off Method', function () {
|
||||
var listeners;
|
||||
var listener1;
|
||||
var listener2;
|
||||
|
||||
beforeEach(function () {
|
||||
listeners = [];
|
||||
listener1 = function (e) {
|
||||
console.log(e, 'listener1');
|
||||
};
|
||||
listener2 = function (e) {
|
||||
console.log(e, 'listener2');
|
||||
};
|
||||
listeners.push(listener1);
|
||||
listeners.push(listener2);
|
||||
|
||||
// Add event and listeners to chart
|
||||
listeners.forEach(function (listener) {
|
||||
vis.on(beforeEvent, listener);
|
||||
});
|
||||
|
||||
// Turn off event listener before chart rendered
|
||||
vis.off(beforeEvent, listener1);
|
||||
|
||||
// Render chart
|
||||
vis.render(data);
|
||||
|
||||
// Add event after charts have rendered
|
||||
listeners.forEach(function (listener) {
|
||||
vis.on(afterEvent, listener);
|
||||
});
|
||||
|
||||
// Turn off event listener after chart is rendered
|
||||
vis.off(afterEvent, listener1);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
vis.off(beforeEvent);
|
||||
vis.off(afterEvent);
|
||||
});
|
||||
|
||||
it('should remove a listener from the _listeners[event] array', function () {
|
||||
var charts = vis.handler.charts;
|
||||
|
||||
expect(vis._listeners[beforeEvent].length).to.be(1);
|
||||
expect(vis._listeners[afterEvent].length).to.be(1);
|
||||
|
||||
// should still have the 2 events in the eventTypes enabled array
|
||||
expect(vis.eventTypes.enabled.length).to.be(2);
|
||||
|
||||
// Test that listener that was not removed is still present
|
||||
vis._listeners[beforeEvent].forEach(function (listener) {
|
||||
expect(typeof listener.handler).to.be('function');
|
||||
expect(listener.handler).to.be(listener2);
|
||||
});
|
||||
|
||||
vis._listeners[afterEvent].forEach(function (listener) {
|
||||
expect(typeof listener.handler).to.be('function');
|
||||
expect(listener.handler).to.be(listener2);
|
||||
});
|
||||
|
||||
// Events should still be attached to charts
|
||||
charts.forEach(function (chart) {
|
||||
expect(typeof chart.on(beforeEvent)).to.be('function');
|
||||
expect(typeof chart.on(afterEvent)).to.be('function');
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove the event and all listeners when only event passed an argument', function () {
|
||||
var charts = vis.handler.charts;
|
||||
vis.off(afterEvent);
|
||||
|
||||
// should remove 'brush' from _listeners object
|
||||
expect(vis._listeners[afterEvent]).to.be(undefined);
|
||||
|
||||
// should remove 'brush' from eventTypes.enabled array
|
||||
expect(vis.eventTypes.enabled.length).to.be(1);
|
||||
|
||||
// should remove the event from the charts
|
||||
charts.forEach(function (chart) {
|
||||
expect(typeof chart.on(afterEvent)).to.be('undefined');
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove the event from the eventTypes.enabled array as well as ' +
|
||||
'from the chart when the _listeners array has a length of 0', function () {
|
||||
var charts = vis.handler.charts;
|
||||
vis.off(afterEvent, listener2);
|
||||
|
||||
expect(vis._listeners[afterEvent].length).to.be(0);
|
||||
expect(vis.eventTypes.enabled.length).to.be(1);
|
||||
|
||||
charts.forEach(function (chart) {
|
||||
expect(typeof chart.on(afterEvent)).to.be('undefined');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue