From b9ed2e53fb5dc5e97af6649ec855f8e6cb4b15a4 Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Wed, 22 Oct 2014 03:43:06 +0300 Subject: [PATCH 01/16] almost there --- .../apps/discover/controllers/discover.js | 4 +- src/kibana/components/vislib/lib/data.js | 2 + src/kibana/components/vislib/lib/dispatch.js | 157 +++++++++--------- .../components/vislib/lib/handler/handler.js | 39 +++-- src/kibana/components/vislib/vis.js | 51 ++++++ .../vislib/visualizations/_chart.js | 2 +- .../vislib/visualizations/column_chart.js | 102 ++++-------- .../vislib/visualizations/pie_chart.js | 2 +- 8 files changed, 183 insertions(+), 176 deletions(-) diff --git a/src/kibana/apps/discover/controllers/discover.js b/src/kibana/apps/discover/controllers/discover.js index a21d700cb4d8..a754d0e82331 100644 --- a/src/kibana/apps/discover/controllers/discover.js +++ b/src/kibana/apps/discover/controllers/discover.js @@ -639,8 +639,8 @@ define(function (require) { type: 'histogram', vislibParams: { addLegend: false, - addEvents: true, - addBrushing: true, +// addEvents: true, +// addBrushing: true, }, listeners: { click: function (e) { diff --git a/src/kibana/components/vislib/lib/data.js b/src/kibana/components/vislib/lib/data.js index 46371139e2f1..6fa0bedbd09b 100644 --- a/src/kibana/components/vislib/lib/data.js +++ b/src/kibana/components/vislib/lib/data.js @@ -22,6 +22,8 @@ define(function (require) { } this.data = data; + this.labels = getLabels(data); + this.color = color(this.labels); this._normalizeOrdered(); this._attr = attr; diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index 3fb2599b49fa..1972d8479a3d 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -8,24 +8,26 @@ 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; + + var self = this; 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'); + this.listeners = Object.keys(this.dispatch); + + // Add eventResponse data to be returned on dispatch + this.listeners.forEach(function (listener) { + this.dispatch.on(listener, function (d, i) { + self.eventResponse(d, i); + d3.event.stopPropagation(); + }); + }, this); } /** @@ -37,56 +39,51 @@ define(function (require) { * e: (d3.event|*), handler: (Object|*)}} Event response object */ Dispatch.prototype.eventResponse = function (d, i) { - 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 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; return { - value: getYValue(d, i), + value: d.y, point: d, - label: label, - color: color(label), + label: d.label, + color: color(d.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 + * Mouse over Behavior * - * @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 mouseOverBar + * @param target {Object} Reference to this object + * @returns {D3.Selection} this object with '.hover' class true */ - 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.onMouseOver = function () { + return d3.select(this).classed('hover', true) + .style('stroke', '#333') + .style('cursor', 'pointer'); + }; - 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 - }; + /** + * Mouse out Behavior + * + * @method mouseOutBar + * @param target {Object} Reference to this object + * @returns {D3.Selection} this object with '.hover' class false + */ + Dispatch.prototype.onMouseOut = function () { + return d3.select(this).classed('hover', false).style('stroke', null); }; /** @@ -96,38 +93,38 @@ 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; - - // 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 - }); - }); - - // 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); - } - - return brush; - }; +// 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; +// +// // 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 +// }); +// }); +// +// // 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); +// } +// +// return brush; +// }; return Dispatch; }; diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index 0c055a2544ef..075af9f5a825 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -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 } }); @@ -65,28 +67,29 @@ define(function (require) { }); 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 listeners = self.vis._listeners; + var keys = Object.keys(listeners); - d3.rebind(chart, chart._attr.dispatch, 'on'); + // Copy dispatch.on methods to chart object + d3.rebind(chart, chart.events.dispatch, 'on'); - // Bubble events up to the Vis Class and Events Class - chart.on('click', function (e) { - self.vis.emit('click', e); + // if there are listeners, dispatch listeners to chart + if (keys.length) { + keys.forEach(function (key) { + listeners[key].forEach(function (obj, i) { + chart.on(key + '.' + i, function (e) { + obj.handler.call(this, arguments); + }); + }); }); + } - chart.on('hover', function (e) { - self.vis.emit('hover', e); - }); - - chart.on('brush', function (e) { - self.vis.emit('brush', e); - }); - - charts.push(chart); - chart.render(); - }); + charts.push(chart); + chart.render(); + }); }; /** diff --git a/src/kibana/components/vislib/vis.js b/src/kibana/components/vislib/vis.js index 2ac42bf35e28..bad4aec241a1 100644 --- a/src/kibana/components/vislib/vis.js +++ b/src/kibana/components/vislib/vis.js @@ -114,6 +114,57 @@ define(function (require) { return this._attr[name]; }; + Vis.prototype.on = function (event, handler) { + + // Adds handler to _listeners[listener] array + var ret = Events.prototype.on.call(this, event, handler); + var handlerIndex; + + // Check if the charts array is available + if (this.handler && this.handler.charts) { + handlerIndex = this._listeners[event].length - 1; + + // Dispatch listener to chart + this.handler.charts.forEach(function (chart) { + chart.on(event + '.' + handlerIndex, function (e) { + handler.call(this, arguments); + }); + }); + } + + return ret; + }; + + /* + * To turn off event listeners, need to pass null as handler to + * d3.dispatch. In addition, we need to track down the particular handler + * from which to turn off. + */ + Vis.prototype.off = function (event, handler) { + var ret = Events.prototype.off.call(this, event, handler); + var handlerIndex; + + if (this._listeners[event] && this.handler.charts) { + + // if no handler, set all listener handlers to null + if (!handler) { + this.handler.charts.forEach(function (chart) { + chart.on(event, null); + }); + } else { + + // if handler, get index of handler and set to null. + handlerIndex = _.findIndex(this._listeners[event], handler); + + this.handler.charts.forEach(function (chart) { + chart.on(event + '.' + handlerIndex, null); + }); + } + } + + return ret; + }; + return Vis; }; }); \ No newline at end of file diff --git a/src/kibana/components/vislib/visualizations/_chart.js b/src/kibana/components/vislib/visualizations/_chart.js index aa093ffd7688..445ba9ce1258 100644 --- a/src/kibana/components/vislib/visualizations/_chart.js +++ b/src/kibana/components/vislib/visualizations/_chart.js @@ -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; diff --git a/src/kibana/components/vislib/visualizations/column_chart.js b/src/kibana/components/vislib/visualizations/column_chart.js index 727f0523417e..7aab1025192d 100644 --- a/src/kibana/components/vislib/visualizations/column_chart.js +++ b/src/kibana/components/vislib/visualizations/column_chart.js @@ -23,17 +23,6 @@ define(function (require) { return new ColumnChart(handler, chartEl, chartData); } - // TODO: refactor - var raw; - var fieldIndex; - - if (handler.data.data.raw) { - raw = handler.data.data.raw.columns; - fieldIndex = _.findIndex(raw, {'categoryName': 'group'}); - } - - this.fieldFormatter = (raw && raw[fieldIndex]) ? raw[fieldIndex].field.format.convert : function (d) { return d; }; - ColumnChart.Super.apply(this, arguments); // Column chart specific attributes @@ -105,10 +94,10 @@ define(function (require) { .enter() .append('rect') .attr('class', function (d) { - return self.colorToClass(color(self.fieldFormatter(d.label))); + return self.colorToClass(color(d.label)); }) .attr('fill', function (d) { - return color(self.fieldFormatter(d.label)); + return color(d.label); }); bars @@ -150,72 +139,42 @@ define(function (require) { * @param brush {Function} D3 brush function * @returns {HTMLElement} rect with event listeners attached */ - ColumnChart.prototype.addBarEvents = function (svg, bars, brush) { - var self = this; + ColumnChart.prototype.addBarEvents = function (svg, bars) { var events = this.events; - var dispatch = this.events._attr.dispatch; - var addBrush = this._attr.addBrushing; - var xScale = this.handler.xAxis.xScale; + var dispatch = this.events.dispatch; +// var addBrush = this._attr.addBrushing; +// var xScale = this.handler.xAxis.xScale; +// var height = this._attr.height; +// var margin = this._attr.margin; 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]); - - // 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); - } + events.onMouseOver.call(this, arguments); + dispatch.hover.call(this, d, i); }) +// .on('mousedown.bar', function () { +// 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); +// }) .on('click.bar', function (d, i) { - dispatch.click(events.eventResponse(d, i)); - d3.event.stopPropagation(); + dispatch.click.call(this, events.eventResponse.call(this, d, i)); }) .on('mouseout.bar', function () { - self.mouseOutBar(this); + events.onMouseOut.call(this, arguments); }); }; - /** - * 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); - }; - /** * Renders d3 visualization * @@ -231,13 +190,11 @@ define(function (require) { 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) { @@ -259,12 +216,9 @@ 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); - } + self.addBarEvents(svg, bars); var line = svg.append('line') .attr('x1', 0) diff --git a/src/kibana/components/vislib/visualizations/pie_chart.js b/src/kibana/components/vislib/visualizations/pie_chart.js index 85ee0b5094ac..c60374c6047e 100644 --- a/src/kibana/components/vislib/visualizations/pie_chart.js +++ b/src/kibana/components/vislib/visualizations/pie_chart.js @@ -29,7 +29,7 @@ define(function (require) { 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') + dispatch: d3.dispatch('brush', 'click', 'hover', 'mouseover', 'mouseout') }); } From 8eb169687585b642af86e97e26c8bad4d10a4c7a Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Wed, 22 Oct 2014 21:19:28 +0300 Subject: [PATCH 02/16] troubleshooting --- .../apps/discover/controllers/discover.js | 2 - src/kibana/components/vislib/lib/dispatch.js | 21 +++----- .../components/vislib/lib/handler/handler.js | 6 ++- src/kibana/components/vislib/lib/tooltip.js | 6 +-- src/kibana/components/vislib/vis.js | 50 +++++++++++++------ .../vislib/visualizations/column_chart.js | 6 ++- 6 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/kibana/apps/discover/controllers/discover.js b/src/kibana/apps/discover/controllers/discover.js index a754d0e82331..ba994a5174ba 100644 --- a/src/kibana/apps/discover/controllers/discover.js +++ b/src/kibana/apps/discover/controllers/discover.js @@ -639,8 +639,6 @@ define(function (require) { type: 'histogram', vislibParams: { addLegend: false, -// addEvents: true, -// addBrushing: true, }, listeners: { click: function (e) { diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index 1972d8479a3d..a0994cf35c92 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -15,19 +15,9 @@ define(function (require) { return new Dispatch(handler); } - var self = this; - this.handler = handler; - this.dispatch = d3.dispatch('brush', 'click', 'hover'); - this.listeners = Object.keys(this.dispatch); - - // Add eventResponse data to be returned on dispatch - this.listeners.forEach(function (listener) { - this.dispatch.on(listener, function (d, i) { - self.eventResponse(d, i); - d3.event.stopPropagation(); - }); - }, this); + this.dispatch = d3.dispatch('brush', 'click', 'hover', 'mouseup', + 'mousedown', 'mouseover'); } /** @@ -35,7 +25,8 @@ 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) { @@ -71,8 +62,8 @@ define(function (require) { */ Dispatch.prototype.onMouseOver = function () { return d3.select(this).classed('hover', true) - .style('stroke', '#333') - .style('cursor', 'pointer'); + .style('stroke', '#333') + .style('cursor', 'pointer'); }; /** diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index 075af9f5a825..db1786a51ae7 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -76,12 +76,16 @@ define(function (require) { // Copy dispatch.on methods to chart object d3.rebind(chart, chart.events.dispatch, 'on'); + // if listeners.length, chart.on(event,) + // Don't bind handlers individually, use emit instead + // + // if there are listeners, dispatch listeners to chart if (keys.length) { keys.forEach(function (key) { listeners[key].forEach(function (obj, i) { chart.on(key + '.' + i, function (e) { - obj.handler.call(this, arguments); + obj.handler.call(this, e); }); }); }); diff --git a/src/kibana/components/vislib/lib/tooltip.js b/src/kibana/components/vislib/lib/tooltip.js index 7cc260810c65..c33e8baf4901 100644 --- a/src/kibana/components/vislib/lib/tooltip.js +++ b/src/kibana/components/vislib/lib/tooltip.js @@ -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; diff --git a/src/kibana/components/vislib/vis.js b/src/kibana/components/vislib/vis.js index bad4aec241a1..6c011d8e68a9 100644 --- a/src/kibana/components/vislib/vis.js +++ b/src/kibana/components/vislib/vis.js @@ -114,6 +114,13 @@ define(function (require) { return this._attr[name]; }; + /** + * Turns on event listeners. + * + * @param event {String} + * @param handler {Function} + * @returns {*} + */ Vis.prototype.on = function (event, handler) { // Adds handler to _listeners[listener] array @@ -121,24 +128,35 @@ define(function (require) { var handlerIndex; // Check if the charts array is available - if (this.handler && this.handler.charts) { - handlerIndex = this._listeners[event].length - 1; + // if handler count changed from 0 to 1, call handler object + // this.handler.enable(event) - // Dispatch listener to chart - this.handler.charts.forEach(function (chart) { - chart.on(event + '.' + handlerIndex, function (e) { - handler.call(this, arguments); - }); - }); - } + // 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 everytime it creates the charts + +// if (this.handler && this.handler.charts) { +// handlerIndex = this._listeners[event].length - 1; +// +// // Dispatch listener to chart +// this.handler.charts.forEach(function (chart) { +// chart.on(event + '.' + handlerIndex, function (e) { +// handler.call(this, e); +// }); +// }); +// } return ret; }; - /* - * To turn off event listeners, need to pass null as handler to - * d3.dispatch. In addition, we need to track down the particular handler - * from which to turn off. + /** + * Turns off event listeners. Passes the null value as the handler to + * event listeners. + * + * @param event {String} + * @param handler {Function} + * @returns {*} */ Vis.prototype.off = function (event, handler) { var ret = Events.prototype.off.call(this, event, handler); @@ -146,6 +164,8 @@ define(function (require) { if (this._listeners[event] && this.handler.charts) { + // Once the handler array reaches zero, then set event to null + // if no handler, set all listener handlers to null if (!handler) { this.handler.charts.forEach(function (chart) { @@ -153,8 +173,8 @@ define(function (require) { }); } else { - // if handler, get index of handler and set to null. - handlerIndex = _.findIndex(this._listeners[event], handler); + // if handler, turn off a specific handler + handlerIndex = _.findIndex(this._listeners[event], {'handler': handler}); this.handler.charts.forEach(function (chart) { chart.on(event + '.' + handlerIndex, null); diff --git a/src/kibana/components/vislib/visualizations/column_chart.js b/src/kibana/components/vislib/visualizations/column_chart.js index 7aab1025192d..67e96bbbe77b 100644 --- a/src/kibana/components/vislib/visualizations/column_chart.js +++ b/src/kibana/components/vislib/visualizations/column_chart.js @@ -150,7 +150,7 @@ define(function (require) { bars .on('mouseover.bar', function (d, i) { events.onMouseOver.call(this, arguments); - dispatch.hover.call(this, d, i); + dispatch.hover.call(this, events.eventResponse(d, i)); }) // .on('mousedown.bar', function () { // var bar = d3.select(this); @@ -168,10 +168,12 @@ define(function (require) { // bar.call(brush); // }) .on('click.bar', function (d, i) { - dispatch.click.call(this, events.eventResponse.call(this, d, i)); + dispatch.click.call(this, events.eventResponse(d, i)); + d3.event.stopPropagation(); }) .on('mouseout.bar', function () { events.onMouseOut.call(this, arguments); + d3.event.stopPropagation(); }); }; From f345ebd8bc64af4a51838af1f2b0e29149bff34c Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Wed, 22 Oct 2014 21:34:08 +0300 Subject: [PATCH 03/16] troubleshooting --- src/kibana/components/vislib/lib/handler/handler.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index db1786a51ae7..b9b6c0c4da13 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -78,15 +78,10 @@ define(function (require) { // if listeners.length, chart.on(event,) // Don't bind handlers individually, use emit instead - // - - // if there are listeners, dispatch listeners to chart if (keys.length) { keys.forEach(function (key) { - listeners[key].forEach(function (obj, i) { - chart.on(key + '.' + i, function (e) { - obj.handler.call(this, e); - }); + chart.on(key, function (e) { + self.vis.emit(key, e); }); }); } From 8bd26e5cdcebf0ca385e2e8781a281b64d989f3c Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Wed, 22 Oct 2014 22:40:43 +0300 Subject: [PATCH 04/16] more changes, experimenting --- src/kibana/components/vislib/lib/handler/handler.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index b9b6c0c4da13..39cc1ede9d63 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -70,14 +70,11 @@ define(function (require) { .selectAll('.chart') .each(function (chartData) { var chart = new self.ChartClass(self, this, chartData); - var listeners = self.vis._listeners; - var keys = Object.keys(listeners); + var keys = Object.keys(self.vis._listeners); // Copy dispatch.on methods to chart object d3.rebind(chart, chart.events.dispatch, 'on'); - // if listeners.length, chart.on(event,) - // Don't bind handlers individually, use emit instead if (keys.length) { keys.forEach(function (key) { chart.on(key, function (e) { From 0b67b7886ebba868bd50ee4600d96f779f02339d Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Fri, 24 Oct 2014 19:09:26 +0300 Subject: [PATCH 05/16] working thru the issues --- src/kibana/components/vislib/lib/dispatch.js | 62 ++++++++------- .../components/vislib/lib/handler/handler.js | 48 +++++++++--- src/kibana/components/vislib/vis.js | 75 +++++++++---------- .../vislib/visualizations/column_chart.js | 44 ++++++----- 4 files changed, 128 insertions(+), 101 deletions(-) diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index a0994cf35c92..991a584c2e8a 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -84,38 +84,36 @@ 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; -// -// // 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 -// }); -// }); -// -// // 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); -// } -// -// return brush; -// }; + Dispatch.prototype.addBrush = function (xScale, svg) { + var dispatch = this.dispatch; + var attr = this.handler._attr; + var chartData = this.handler.chartData; + var height = attr.height; + var margin = attr.margin; + + // Brush scale + var brush = d3.svg.brush() + .x(xScale) + .on('brushend', function brushEnd() { + return dispatch.brush({ + range: brush.extent(), + config: attr, + e: d3.event, + data: chartData + }); + }); + + // if `addBrushing` is true, add brush canvas + 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 Dispatch; }; diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index 39cc1ede9d63..5bb15289c7a8 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -60,27 +60,31 @@ 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); - var keys = Object.keys(self.vis._listeners); + var enabledEvents; - // Copy dispatch.on methods to chart object - d3.rebind(chart, chart.events.dispatch, 'on'); + if (chart.events.dispatch) { + enabledEvents = self.vis.eventTypes.enabled; - if (keys.length) { - keys.forEach(function (key) { - chart.on(key, function (e) { - self.vis.emit(key, e); + // Copy dispatch.on methods to chart object + d3.rebind(chart, chart.events.dispatch, 'on'); + + // Bind events to chart(s) + if (enabledEvents.length) { + enabledEvents.forEach(function (event) { + self.enable(event, chart); }); - }); + } } charts.push(chart); @@ -88,6 +92,32 @@ define(function (require) { }); }; + + // 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 + /** + * + * @param event + * @param chart + */ + Handler.prototype.enable = function (event, chart) { + return chart.on(event, function (e) { + this.vis.emit(event, e); + }.bind(this)); + }; + + /** + * + * @param event + * @param chart + * @returns {*} + */ + Handler.prototype.disable = function (event, chart) { + return chart.on(event, null); + }; + /** * Removes all DOM elements from the HTML element provided * diff --git a/src/kibana/components/vislib/vis.js b/src/kibana/components/vislib/vis.js index 6c011d8e68a9..c187bfc4bb69 100644 --- a/src/kibana/components/vislib/vis.js +++ b/src/kibana/components/vislib/vis.js @@ -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); @@ -122,63 +125,53 @@ define(function (require) { * @returns {*} */ Vis.prototype.on = function (event, handler) { - - // Adds handler to _listeners[listener] array - var ret = Events.prototype.on.call(this, event, handler); - var handlerIndex; + var ret = Events.prototype.on.call(this, event, handler); // 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); // Check if the charts array is available - // if handler count changed from 0 to 1, call handler object + // if chart count changed from 0 to 1, call handler object // this.handler.enable(event) + if (listeners === 1 && chartCount > 0) { + charts.forEach(function (chart) { + this.handler.enable(event, chart); + }, this); + } - // 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 everytime it creates the charts - -// if (this.handler && this.handler.charts) { -// handlerIndex = this._listeners[event].length - 1; -// -// // Dispatch listener to chart -// this.handler.charts.forEach(function (chart) { -// chart.on(event + '.' + handlerIndex, function (e) { -// handler.call(this, e); -// }); -// }); -// } + // update the eventType as enabled + if (eventAbsent) { + enabledEvents.push(event); + } return ret; }; /** - * Turns off event listeners. Passes the null value as the handler to - * event listeners. + * Turns off event listeners. * * @param event {String} * @param handler {Function} * @returns {*} */ Vis.prototype.off = function (event, handler) { - var ret = Events.prototype.off.call(this, event, handler); - var handlerIndex; + var ret = Events.prototype.off.call(this, event, handler); // 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); - if (this._listeners[event] && this.handler.charts) { - - // Once the handler array reaches zero, then set event to null - - // if no handler, set all listener handlers to null - if (!handler) { - this.handler.charts.forEach(function (chart) { - chart.on(event, null); - }); + // Once the handler array reaches zero, then turn off event + if (!listeners && eventPresent) { + if (chartCount > 0) { + charts.forEach(function (chart) { + this.handler.disable(event, chart); + }, this); } else { - - // if handler, turn off a specific handler - handlerIndex = _.findIndex(this._listeners[event], {'handler': handler}); - - this.handler.charts.forEach(function (chart) { - chart.on(event + '.' + handlerIndex, null); - }); + this.eventTypes.enabled.splice(eventIndex, 1); } } @@ -187,4 +180,4 @@ define(function (require) { return Vis; }; -}); \ No newline at end of file +}); diff --git a/src/kibana/components/vislib/visualizations/column_chart.js b/src/kibana/components/vislib/visualizations/column_chart.js index 67e96bbbe77b..d8098f2786cd 100644 --- a/src/kibana/components/vislib/visualizations/column_chart.js +++ b/src/kibana/components/vislib/visualizations/column_chart.js @@ -139,35 +139,21 @@ define(function (require) { * @param brush {Function} D3 brush function * @returns {HTMLElement} rect with event listeners attached */ - ColumnChart.prototype.addBarEvents = function (svg, bars) { + ColumnChart.prototype.addBarEvents = function (svg, bars, brush) { var events = this.events; var dispatch = this.events.dispatch; // var addBrush = this._attr.addBrushing; -// var xScale = this.handler.xAxis.xScale; + var xScale = this.handler.xAxis.xScale; // var height = this._attr.height; // var margin = this._attr.margin; bars .on('mouseover.bar', function (d, i) { - events.onMouseOver.call(this, arguments); dispatch.hover.call(this, events.eventResponse(d, i)); + d3.event.stopPropagation(); }) -// .on('mousedown.bar', function () { -// 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); -// }) .on('click.bar', function (d, i) { + events.onMouseOver.call(this, arguments); dispatch.click.call(this, events.eventResponse(d, i)); d3.event.stopPropagation(); }) @@ -175,6 +161,25 @@ define(function (require) { events.onMouseOut.call(this, arguments); d3.event.stopPropagation(); }); + + if (dispatch.on('brush')) { + bars + .on('mousedown.bar', function () { + 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); + }); + } }; /** @@ -220,7 +225,8 @@ define(function (require) { bars = self.addBars(svg, layers); - self.addBarEvents(svg, bars); + var brush = self.events.addBrush(xScale, svg); + self.addBarEvents(svg, bars, brush); var line = svg.append('line') .attr('x1', 0) From 0bc4caf162dd5dbcc79dcf3752ca16e8fc46589c Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Sat, 25 Oct 2014 01:29:48 +0300 Subject: [PATCH 06/16] reorganizing dispatch events class --- src/kibana/components/vislib/lib/dispatch.js | 13 ++- .../components/vislib/lib/handler/handler.js | 27 +++++-- src/kibana/components/vislib/vis.js | 22 +++--- .../vislib/visualizations/column_chart.js | 79 ++++++++++++------- 4 files changed, 91 insertions(+), 50 deletions(-) diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index 991a584c2e8a..fc2f5df056cf 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -53,6 +53,16 @@ define(function (require) { }; }; + Dispatch.prototype.addEvent = function (event, callback) { + return function (selection) { + selection.each(function () { + var element = d3.select(this); + + element.on(event, callback); + }); + }; + }; + /** * Mouse over Behavior * @@ -87,7 +97,6 @@ define(function (require) { Dispatch.prototype.addBrush = function (xScale, svg) { var dispatch = this.dispatch; var attr = this.handler._attr; - var chartData = this.handler.chartData; var height = attr.height; var margin = attr.margin; @@ -99,7 +108,7 @@ define(function (require) { range: brush.extent(), config: attr, e: d3.event, - data: chartData + data: d3.event.sourceEvent.target.__data__ }); }); diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index 5bb15289c7a8..492e3cd23c87 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -73,6 +73,12 @@ define(function (require) { var chart = new self.ChartClass(self, this, chartData); var enabledEvents; + /* + * 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; @@ -93,14 +99,15 @@ define(function (require) { }; - // 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 /** + * 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. * - * @param event - * @param chart + * @method enable + * @param event {String} Event type + * @param chart {Object} Chart + * @returns {*} */ Handler.prototype.enable = function (event, chart) { return chart.on(event, function (e) { @@ -109,9 +116,13 @@ define(function (require) { }; /** + * Disables events. 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. * - * @param event - * @param chart + * @method disable + * @param event {String} Event type + * @param chart {Object} Chart * @returns {*} */ Handler.prototype.disable = function (event, chart) { diff --git a/src/kibana/components/vislib/vis.js b/src/kibana/components/vislib/vis.js index c187bfc4bb69..bf1c0a603fd3 100644 --- a/src/kibana/components/vislib/vis.js +++ b/src/kibana/components/vislib/vis.js @@ -121,27 +121,27 @@ define(function (require) { * Turns on event listeners. * * @param event {String} - * @param handler {Function} + * @param listener{Function} * @returns {*} */ - Vis.prototype.on = function (event, handler) { - var ret = Events.prototype.on.call(this, event, handler); // Adds event to _listeners array + 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); - // Check if the charts array is available - // if chart count changed from 0 to 1, call handler object - // this.handler.enable(event) + // 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); } - // update the eventType as enabled + // Keep track of enabled events if (eventAbsent) { enabledEvents.push(event); } @@ -153,18 +153,18 @@ define(function (require) { * Turns off event listeners. * * @param event {String} - * @param handler {Function} + * @param listener{Function} * @returns {*} */ - Vis.prototype.off = function (event, handler) { - var ret = Events.prototype.off.call(this, event, handler); // Removes event from _listeners array + 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 handler array reaches zero, then turn off event + // Once the listener array reaches zero, turn off event if (!listeners && eventPresent) { if (chartCount > 0) { charts.forEach(function (chart) { diff --git a/src/kibana/components/vislib/visualizations/column_chart.js b/src/kibana/components/vislib/visualizations/column_chart.js index ee2b0b23471a..f454195bde4d 100644 --- a/src/kibana/components/vislib/visualizations/column_chart.js +++ b/src/kibana/components/vislib/visualizations/column_chart.js @@ -219,37 +219,31 @@ define(function (require) { * Adds Events to SVG rect * * @method addBarEvents + * @param element {d3.UpdateSelection} target * @param svg {HTMLElement} chart SVG - * @param bars {D3.UpdateSelection} SVG rect - * @param brush {Function} D3 brush function * @returns {HTMLElement} rect with event listeners attached */ - ColumnChart.prototype.addBarEvents = function (svg, bars, brush) { - var events = this.events; - var dispatch = this.events.dispatch; -// var addBrush = this._attr.addBrushing; - var xScale = this.handler.xAxis.xScale; -// var height = this._attr.height; -// var margin = this._attr.margin; + ColumnChart.prototype.addBarEvents = function (element, svg) { + var addEvent = this.events.addEvent; + var mouseOver = addEvent('mouseover.bar', this.mouseOver()); + var mouseOut = addEvent('mouseout.bar', this.mouseOut()); + var brush = addEvent('mousedown.bar', this.brush(svg)); + var click = addEvent('click.bar', this.click()); - bars - .on('mouseover.bar', function (d, i) { - dispatch.hover.call(this, events.eventResponse(d, i)); - d3.event.stopPropagation(); - }) - .on('click.bar', function (d, i) { - events.onMouseOver.call(this, arguments); - dispatch.click.call(this, events.eventResponse(d, i)); - d3.event.stopPropagation(); - }) - .on('mouseout.bar', function () { - events.onMouseOut.call(this, arguments); - d3.event.stopPropagation(); - }); + return element + .call(mouseOver) + .call(mouseOut) + .call(brush) + .call(click); + }; + + ColumnChart.prototype.brush = function (svg) { + var dispatch = this.events.dispatch; + var xScale = this.handler.xAxis.xScale; + var brush = this.events.addBrush(xScale, svg); if (dispatch.on('brush')) { - bars - .on('mousedown.bar', function () { + return function () { var bar = d3.select(this); var startX = d3.mouse(svg.node()); var startXInv = xScale.invert(startX[0]); @@ -263,10 +257,38 @@ define(function (require) { // Need to call brush on bar to allow the click event to be registered svg.call(brush); bar.call(brush); - }); + }; } }; + ColumnChart.prototype.mouseOver = function () { + var self = this; + + return function (d, i) { + self.events.onMouseOver.call(this, arguments); + self.events.dispatch.hover.call(this, self.events.eventResponse(d, i)); + d3.event.stopPropagation(); + }; + }; + + ColumnChart.prototype.mouseOut = function () { + var self = this; + + return function () { + self.events.onMouseOut.call(this, arguments); + d3.event.stopPropagation(); + }; + }; + + ColumnChart.prototype.click = function () { + var self = this; + + return function (d, i) { + self.events.dispatch.click.call(this, self.events.eventResponse(d, i)); + d3.event.stopPropagation(); + }; + }; + /** * Renders d3 visualization * @@ -275,7 +297,6 @@ 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(); @@ -310,8 +331,8 @@ define(function (require) { bars = self.addBars(svg, layers); - var brush = self.events.addBrush(xScale, svg); - self.addBarEvents(svg, bars, brush); + // Adds event listeners + self.addBarEvents(bars, svg); var line = svg.append('line') .attr('x1', 0) From 58d83b8bdb010285ba448ab2ee2fd230bc13e121 Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Mon, 27 Oct 2014 19:32:38 +0200 Subject: [PATCH 07/16] adding more comments --- src/kibana/components/vislib/lib/handler/handler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kibana/components/vislib/lib/handler/handler.js b/src/kibana/components/vislib/lib/handler/handler.js index 492e3cd23c87..1dbbe2f16fe3 100644 --- a/src/kibana/components/vislib/lib/handler/handler.js +++ b/src/kibana/components/vislib/lib/handler/handler.js @@ -116,7 +116,8 @@ define(function (require) { }; /** - * Disables events. According to the D3 documentation for event handling: + * 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. * From 5cf67cf2f1f01995ca8098e923962575784a267d Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Tue, 28 Oct 2014 17:30:52 +0200 Subject: [PATCH 08/16] making changes to get pie charts working --- src/kibana/components/vislib/lib/data.js | 18 ++++++++++++- src/kibana/components/vislib/lib/dispatch.js | 1 + .../vislib/visualizations/pie_chart.js | 25 ++++++++----------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/kibana/components/vislib/lib/data.js b/src/kibana/components/vislib/lib/data.js index 917c5c63288b..35ccc21ed992 100644 --- a/src/kibana/components/vislib/lib/data.js +++ b/src/kibana/components/vislib/lib/data.js @@ -34,7 +34,8 @@ define(function (require) { } this.data = data; - this.labels = getLabels(data); + this.type = this.getDataType(); + this.labels = (this.type === 'series') ? getLabels(data) : this.pieNames(); this.color = color(this.labels); this._normalizeOrdered(); @@ -48,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 diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index f7545b81f345..47bcd4359191 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -40,6 +40,7 @@ define(function (require) { var isPercentage = (handler._attr.mode === 'percentage'); if (isSeries) { + // Find object with the actual d value and add it to the point object var object = _.find(series, { 'label': d.label }); d.value = +object.values[i].y; diff --git a/src/kibana/components/vislib/visualizations/pie_chart.js b/src/kibana/components/vislib/visualizations/pie_chart.js index c60374c6047e..6828c1af27a7 100644 --- a/src/kibana/components/vislib/visualizations/pie_chart.js +++ b/src/kibana/components/vislib/visualizations/pie_chart.js @@ -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', 'mouseover', 'mouseout') + getSize: function (d) { return d.size; } }); } @@ -42,7 +39,7 @@ define(function (require) { */ PieChart.prototype.addPathEvents = function (path) { var events = this.events; - var dispatch = this.events._attr.dispatch; + var dispatch = this.events.dispatch; path .on('mouseover.pie', function mouseOverPie(d, i) { @@ -50,11 +47,11 @@ define(function (require) { .classed('hover', true) .style('cursor', 'pointer'); - dispatch.hover(events.pieResponse(d, i)); + dispatch.hover(events.eventResponse(d, i)); d3.event.stopPropagation(); }) .on('click.pie', function clickPie(d, i) { - dispatch.click(events.pieResponse(d, i)); + dispatch.click(events.eventResponse(d, i)); d3.event.stopPropagation(); }) .on('mouseout.pie', function mouseOutPie() { @@ -109,7 +106,9 @@ define(function (require) { var isTooltip = this._attr.addTooltip; var self = this; var path; - var fieldFormatter; + var fieldFormatter = function (label) { + return label; + }; path = svg .datum(slices) @@ -121,18 +120,14 @@ define(function (require) { .attr('class', function (d) { if (d.depth === 0) { return; } - fieldFormatter = self.columns[d.depth - 1].field ? - self.columns[d.depth - 1].field.format.convert : - function (d) { return d; }; + fieldFormatter = d.aggConfig ? d.aggConfig.params.field.format.convert : fieldFormatter; return self.colorToClass(color(fieldFormatter(d.name))); }) .style('stroke', '#fff') .style('fill', function (d) { if (d.depth === 0) { return 'none'; } - fieldFormatter = self.columns[d.depth - 1].field ? - self.columns[d.depth - 1].field.format.convert : - function (d) { return d; }; + fieldFormatter = d.aggConfig ? d.aggConfig.params.field.format.convert : fieldFormatter; return color(fieldFormatter(d.name)); }); @@ -186,4 +181,4 @@ define(function (require) { return PieChart; }; -}); \ No newline at end of file +}); From a150ef51f587b0e85e1176cad6a9df0c27668aeb Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Tue, 28 Oct 2014 18:03:55 +0200 Subject: [PATCH 09/16] fixing area chart and line chart --- src/kibana/components/vislib/lib/dispatch.js | 8 ++++++++ src/kibana/components/vislib/visualizations/area_chart.js | 2 +- src/kibana/components/vislib/visualizations/line_chart.js | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index 47bcd4359191..65d2c4fe8246 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -67,6 +67,14 @@ define(function (require) { }; }; + /** + * Returns a function that adds events and listeners to a D3 selection + * + * @method addEvent + * @param event {String} + * @param callback {Function} + * @returns {Function} + */ Dispatch.prototype.addEvent = function (event, callback) { return function (selection) { selection.each(function () { diff --git a/src/kibana/components/vislib/visualizations/area_chart.js b/src/kibana/components/vislib/visualizations/area_chart.js index f4a709881296..fed69b590738 100644 --- a/src/kibana/components/vislib/visualizations/area_chart.js +++ b/src/kibana/components/vislib/visualizations/area_chart.js @@ -141,7 +141,7 @@ define(function (require) { */ AreaChart.prototype.addCircleEvents = function (circles) { var events = this.events; - var dispatch = this.events._attr.dispatch; + var dispatch = this.events.dispatch; circles .on('mouseover.circle', function mouseOverCircle(d, i) { diff --git a/src/kibana/components/vislib/visualizations/line_chart.js b/src/kibana/components/vislib/visualizations/line_chart.js index 8457c06a5307..c9ab1e7aece9 100644 --- a/src/kibana/components/vislib/visualizations/line_chart.js +++ b/src/kibana/components/vislib/visualizations/line_chart.js @@ -41,7 +41,7 @@ define(function (require) { */ LineChart.prototype.addCircleEvents = function (circles) { var events = this.events; - var dispatch = this.events._attr.dispatch; + var dispatch = this.events.dispatch; circles .on('mouseover.circle', function mouseOverCircle(d, i) { From 0372e81d63b1b0728a1e966918fa8f613d2cf652 Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Thu, 30 Oct 2014 18:59:32 +0200 Subject: [PATCH 10/16] restructured the less and css files into separate files which are imported into a main file, removed event code from visualizations and moved them into the Dispatch class to reduce code redundancy, fixed issues with legend not toggling --- src/kibana/components/vislib/lib/dispatch.js | 98 ++++- src/kibana/components/vislib/lib/legend.js | 13 +- .../components/vislib/styles/_error.less | 12 + .../components/vislib/styles/_layout.less | 144 ++++++ .../components/vislib/styles/_legend.less | 59 +++ src/kibana/components/vislib/styles/_svg.less | 63 +++ .../components/vislib/styles/_tooltip.less | 36 ++ src/kibana/components/vislib/styles/main.less | 415 +----------------- src/kibana/components/vislib/vis.js | 1 + .../vislib/visualizations/area_chart.js | 44 +- .../vislib/visualizations/column_chart.js | 77 +--- .../vislib/visualizations/line_chart.js | 39 +- .../vislib/visualizations/pie_chart.js | 34 +- tasks/config/less.js | 1 - 14 files changed, 445 insertions(+), 591 deletions(-) create mode 100644 src/kibana/components/vislib/styles/_error.less create mode 100644 src/kibana/components/vislib/styles/_layout.less create mode 100644 src/kibana/components/vislib/styles/_legend.less create mode 100644 src/kibana/components/vislib/styles/_svg.less create mode 100644 src/kibana/components/vislib/styles/_tooltip.less diff --git a/src/kibana/components/vislib/lib/dispatch.js b/src/kibana/components/vislib/lib/dispatch.js index 65d2c4fe8246..e394fb8f9902 100644 --- a/src/kibana/components/vislib/lib/dispatch.js +++ b/src/kibana/components/vislib/lib/dispatch.js @@ -31,6 +31,7 @@ define(function (require) { */ Dispatch.prototype.eventResponse = function (d, i) { 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; @@ -55,8 +56,8 @@ define(function (require) { return { value: d.y, point: d, - label: d.label, - color: color(d.label), + label: label, + color: color(label), pointIndex: i, series: series, slices: slices, @@ -80,31 +81,96 @@ define(function (require) { selection.each(function () { var element = d3.select(this); - element.on(event, callback); + if (typeof callback === 'function') { + return element.on(event, callback); + } }); }; }; /** - * Mouse over Behavior * - * @method mouseOverBar - * @returns {D3.Selection} this object with '.hover' class true + * @method addHoverEvent + * @returns {Function} */ - Dispatch.prototype.onMouseOver = function () { - return d3.select(this).classed('hover', true) - .style('stroke', '#333') - .style('cursor', 'pointer'); + 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); }; /** - * Mouse out Behavior * - * @method mouseOutBar - * @returns {D3.Selection} this object with '.hover' class false + * @method addClickEvent + * @returns {Function} */ - Dispatch.prototype.onMouseOut = function () { - return d3.select(this).classed('hover', false).style('stroke', null); + 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'); }; /** @@ -114,7 +180,7 @@ 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) { + Dispatch.prototype.createBrush = function (xScale, svg) { var dispatch = this.dispatch; var attr = this.handler._attr; var height = attr.height; diff --git a/src/kibana/components/vislib/lib/legend.js b/src/kibana/components/vislib/lib/legend.js index 872b5b0161a2..25fa86e19b77 100644 --- a/src/kibana/components/vislib/lib/legend.js +++ b/src/kibana/components/vislib/lib/legend.js @@ -105,26 +105,25 @@ define(function (require) { var visEl = d3.select(this.el); var legendDiv = visEl.select('.' + this._attr.legendClass); var items = this.labels; - var headerIcon = visEl.select('.legend-toggle'); + var headerIcon = '.legend-toggle'; var self = this; this.header(legendDiv, this); this.list(legendDiv, items, this); // toggle - headerIcon + visEl.select(headerIcon) .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 @@ -146,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); diff --git a/src/kibana/components/vislib/styles/_error.less b/src/kibana/components/vislib/styles/_error.less new file mode 100644 index 000000000000..0163b15bd22e --- /dev/null +++ b/src/kibana/components/vislib/styles/_error.less @@ -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; + } +} diff --git a/src/kibana/components/vislib/styles/_layout.less b/src/kibana/components/vislib/styles/_layout.less new file mode 100644 index 000000000000..6bc0783fd2f1 --- /dev/null +++ b/src/kibana/components/vislib/styles/_layout.less @@ -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; +} diff --git a/src/kibana/components/vislib/styles/_legend.less b/src/kibana/components/vislib/styles/_legend.less new file mode 100644 index 000000000000..d2907c487ffd --- /dev/null +++ b/src/kibana/components/vislib/styles/_legend.less @@ -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; + } +} diff --git a/src/kibana/components/vislib/styles/_svg.less b/src/kibana/components/vislib/styles/_svg.less new file mode 100644 index 000000000000..e15f23a2e8ca --- /dev/null +++ b/src/kibana/components/vislib/styles/_svg.less @@ -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; + } +} diff --git a/src/kibana/components/vislib/styles/_tooltip.less b/src/kibana/components/vislib/styles/_tooltip.less new file mode 100644 index 000000000000..20d6b1a43475 --- /dev/null +++ b/src/kibana/components/vislib/styles/_tooltip.less @@ -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; + } + } + } +} diff --git a/src/kibana/components/vislib/styles/main.less b/src/kibana/components/vislib/styles/main.less index 3f8f1f7b112f..e7533d883223 100644 --- a/src/kibana/components/vislib/styles/main.less +++ b/src/kibana/components/vislib/styles/main.less @@ -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"; diff --git a/src/kibana/components/vislib/vis.js b/src/kibana/components/vislib/vis.js index bf1c0a603fd3..b5ed0773907a 100644 --- a/src/kibana/components/vislib/vis.js +++ b/src/kibana/components/vislib/vis.js @@ -84,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 diff --git a/src/kibana/components/vislib/visualizations/area_chart.js b/src/kibana/components/vislib/visualizations/area_chart.js index fed69b590738..b6c1964baf6f 100644 --- a/src/kibana/components/vislib/visualizations/area_chart.js +++ b/src/kibana/components/vislib/visualizations/area_chart.js @@ -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.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); diff --git a/src/kibana/components/vislib/visualizations/column_chart.js b/src/kibana/components/vislib/visualizations/column_chart.js index 36268f5d35d6..939b7987124a 100644 --- a/src/kibana/components/vislib/visualizations/column_chart.js +++ b/src/kibana/components/vislib/visualizations/column_chart.js @@ -217,76 +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 element {d3.UpdateSelection} target - * @param svg {HTMLElement} chart SVG - * @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 (element, svg) { - var addEvent = this.events.addEvent; - var mouseOver = addEvent('mouseover.bar', this.mouseOver()); - var mouseOut = addEvent('mouseout.bar', this.mouseOut()); - var brush = addEvent('mousedown.bar', this.brush(svg)); - var click = addEvent('click.bar', this.click()); + var events = this.events; + var isBrushable = (typeof events.addBrushEvent(svg) === 'function'); + var brush = isBrushable ? events.addBrushEvent(svg) : undefined; + var hover = events.addHoverEvent(); + var click = events.addClickEvent(); + var attachedEvents = element.call(hover).call(click); - return element - .call(mouseOver) - .call(mouseOut) - .call(brush) - .call(click); - }; - - ColumnChart.prototype.brush = function (svg) { - var dispatch = this.events.dispatch; - var xScale = this.handler.xAxis.xScale; - var brush = this.events.addBrush(xScale, svg); - - if (dispatch.on('brush')) { - return function () { - 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) { + attachedEvents.call(brush); } - }; - ColumnChart.prototype.mouseOver = function () { - var self = this; - - return function (d, i) { - self.events.onMouseOver.call(this, arguments); - self.events.dispatch.hover.call(this, self.events.eventResponse(d, i)); - d3.event.stopPropagation(); - }; - }; - - ColumnChart.prototype.mouseOut = function () { - var self = this; - - return function () { - self.events.onMouseOut.call(this, arguments); - d3.event.stopPropagation(); - }; - }; - - ColumnChart.prototype.click = function () { - var self = this; - - return function (d, i) { - self.events.dispatch.click.call(this, self.events.eventResponse(d, i)); - d3.event.stopPropagation(); - }; + return attachedEvents; }; /** diff --git a/src/kibana/components/vislib/visualizations/line_chart.js b/src/kibana/components/vislib/visualizations/line_chart.js index c9ab1e7aece9..229eaccca753 100644 --- a/src/kibana/components/vislib/visualizations/line_chart.js +++ b/src/kibana/components/vislib/visualizations/line_chart.js @@ -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.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 diff --git a/src/kibana/components/vislib/visualizations/pie_chart.js b/src/kibana/components/vislib/visualizations/pie_chart.js index ccd2407c40cb..b024e7a89bfd 100644 --- a/src/kibana/components/vislib/visualizations/pie_chart.js +++ b/src/kibana/components/vislib/visualizations/pie_chart.js @@ -34,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.dispatch; - path - .on('mouseover.pie', function mouseOverPie(d, i) { - d3.select(this) - .classed('hover', true) - .style('cursor', 'pointer'); - - dispatch.hover(events.eventResponse(d, i)); - d3.event.stopPropagation(); - }) - .on('click.pie', function clickPie(d, i) { - dispatch.click(events.eventResponse(d, i)); - d3.event.stopPropagation(); - }) - .on('mouseout.pie', function mouseOutPie() { - d3.select(this) - .classed('hover', false); - }); + return element + .call(events.addHoverEvent()) + .call(events.addClickEvent()); }; /** @@ -148,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) { @@ -159,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(); @@ -170,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; }); diff --git a/tasks/config/less.js b/tasks/config/less.js index 0f3d4b56ec1d..27c8c4fd7522 100644 --- a/tasks/config/less.js +++ b/tasks/config/less.js @@ -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', From 95635f88b625d6bc2eaf4200c7a33114d9ede9b9 Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Thu, 30 Oct 2014 20:25:19 +0200 Subject: [PATCH 11/16] fixing issue where brush svg was being appended twice --- src/kibana/components/vislib/visualizations/column_chart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kibana/components/vislib/visualizations/column_chart.js b/src/kibana/components/vislib/visualizations/column_chart.js index 939b7987124a..1465f2b13d00 100644 --- a/src/kibana/components/vislib/visualizations/column_chart.js +++ b/src/kibana/components/vislib/visualizations/column_chart.js @@ -227,7 +227,7 @@ define(function (require) { */ ColumnChart.prototype.addBarEvents = function (element, svg) { var events = this.events; - var isBrushable = (typeof events.addBrushEvent(svg) === 'function'); + var isBrushable = (typeof events.dispatch.on('brush') === 'function'); var brush = isBrushable ? events.addBrushEvent(svg) : undefined; var hover = events.addHoverEvent(); var click = events.addClickEvent(); From adcd177ebb2e9338c7129d57b944f841760ee11b Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Thu, 30 Oct 2014 23:54:56 +0200 Subject: [PATCH 12/16] adding tests for the on and off methods to vis.js --- src/kibana/components/vislib/vis.js | 5 +- test/unit/specs/vislib/lib/handler.js | 8 ++ test/unit/specs/vislib/vis.js | 177 +++++++++++++++++++++++++- 3 files changed, 187 insertions(+), 3 deletions(-) diff --git a/src/kibana/components/vislib/vis.js b/src/kibana/components/vislib/vis.js index b5ed0773907a..e126ed975bcf 100644 --- a/src/kibana/components/vislib/vis.js +++ b/src/kibana/components/vislib/vis.js @@ -171,9 +171,10 @@ define(function (require) { charts.forEach(function (chart) { this.handler.disable(event, chart); }, this); - } else { - this.eventTypes.enabled.splice(eventIndex, 1); } + + // Remove event from enabled array + this.eventTypes.enabled.splice(eventIndex, 1); } return ret; diff --git a/test/unit/specs/vislib/lib/handler.js b/test/unit/specs/vislib/lib/handler.js index 96976a1140f7..371cb98d6bc6 100644 --- a/test/unit/specs/vislib/lib/handler.js +++ b/test/unit/specs/vislib/lib/handler.js @@ -150,6 +150,14 @@ define(function (require) { // }); // }); + describe('enable Method', function () { + + }); + + describe('disable Method', function () { + + }); + describe('removeAll Method', function () { beforeEach(function () { inject(function () { diff --git a/test/unit/specs/vislib/vis.js b/test/unit/specs/vislib/vis.js index eaaf9bdbeac0..463698763593 100644 --- a/test/unit/specs/vislib/vis.js +++ b/test/unit/specs/vislib/vis.js @@ -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'); + }); + }); + }); + }); }); From b1be135432d343c22aac21e1489cfa7d46acb4e2 Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Fri, 31 Oct 2014 00:12:50 +0200 Subject: [PATCH 13/16] adding tests for handler.enable and handler.disable methods --- test/unit/specs/vislib/lib/handler.js | 124 ++++++++++++++++---------- 1 file changed, 77 insertions(+), 47 deletions(-) diff --git a/test/unit/specs/vislib/lib/handler.js b/test/unit/specs/vislib/lib/handler.js index 371cb98d6bc6..d8c1e0927121 100644 --- a/test/unit/specs/vislib/lib/handler.js +++ b/test/unit/specs/vislib/lib/handler.js @@ -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,62 +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]); }); }); @@ -172,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 () { From 0b3199ae55b09f1276773b0b8bb717b1fcd1816e Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Fri, 31 Oct 2014 00:15:24 +0200 Subject: [PATCH 14/16] commenting out render method testing in handler, needs refactoring --- test/unit/specs/vislib/lib/handler.js | 48 +++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/unit/specs/vislib/lib/handler.js b/test/unit/specs/vislib/lib/handler.js index d8c1e0927121..5892b8d4309c 100644 --- a/test/unit/specs/vislib/lib/handler.js +++ b/test/unit/specs/vislib/lib/handler.js @@ -121,30 +121,30 @@ define(function (require) { }); 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); - }); + //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 () { From 6f6e2a858adad5932001f55a7732e2022bd18311 Mon Sep 17 00:00:00 2001 From: Shelby Sturgis Date: Mon, 3 Nov 2014 19:18:49 +0200 Subject: [PATCH 15/16] fixing conflict due to merge with master in legend.js --- src/kibana/components/vislib/lib/legend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kibana/components/vislib/lib/legend.js b/src/kibana/components/vislib/lib/legend.js index 367c63272bd3..339cf877b412 100644 --- a/src/kibana/components/vislib/lib/legend.js +++ b/src/kibana/components/vislib/lib/legend.js @@ -112,7 +112,7 @@ define(function (require) { var headerIcon = visEl.select('.legend-toggle'); // toggle - visEl.select(headerIcon) + headerIcon .on('click', function legendClick() { if (self._attr.isOpen) { // close legend From 0798c41ee1b88e208e5ac9a1fe4e807f3c51e9f1 Mon Sep 17 00:00:00 2001 From: Spencer Alger Date: Mon, 3 Nov 2014 13:25:17 -0700 Subject: [PATCH 16/16] [visType/pie] filter the metric aggs again since slice sizing isn't easily fixable --- src/kibana/filters/_prop_filter.js | 52 +++++++++++++++++++ src/kibana/filters/field_type.js | 33 ++---------- .../plugins/visualize/editor/agg_filter.js | 36 ++----------- 3 files changed, 62 insertions(+), 59 deletions(-) create mode 100644 src/kibana/filters/_prop_filter.js diff --git a/src/kibana/filters/_prop_filter.js b/src/kibana/filters/_prop_filter.js new file mode 100644 index 000000000000..85ece1384979 --- /dev/null +++ b/src/kibana/filters/_prop_filter.js @@ -0,0 +1,52 @@ +define(function (require) { + var _ = require('lodash'); + + /** + * Filters out a list by a given filter. This is currently used to impelment: + * - fieldType filters a list of fields by their type property + * - aggFilter filters a list of aggs by their name property + * + * @returns {function} - the filter function which can be registered with angular + */ + function propFilter(prop) { + /** + * List filtering function which accepts an array or list of values that a property + * must contain + * + * @param {array} list - array of items to filter + * @param {array|string} filters - the values to match against the list. Can be + * an array, a single value as a string, or a comma + * -seperated list of items + * @return {array} - the filtered list + */ + return function (list, filters) { + if (!filters) return filters; + if (!_.isArray(filters)) filters = filters.split(','); + if (_.contains(filters, '*')) return filters; + + filters = filters.map(function (filter) { + var match = true; + var value = filter; + + if (filter.charAt(0) === '!') { + match = false; + value = filter.substr(1); + } + + return { + match: match, + value: value + }; + }); + + return list.filter(function (item) { + for (var i = 0; i < filters.length; i++) { + var filter = filters[i]; + if ((item[prop] === filter.value) === filter.match) return true; + } + }); + }; + } + + return propFilter; +}); \ No newline at end of file diff --git a/src/kibana/filters/field_type.js b/src/kibana/filters/field_type.js index 126772dec539..35179a327526 100644 --- a/src/kibana/filters/field_type.js +++ b/src/kibana/filters/field_type.js @@ -3,34 +3,11 @@ // Or an array of types to get all fields of that type define(function (require) { var _ = require('lodash'); + var propFilter = require('filters/_prop_filter'); require('modules') - .get('kibana') - .filter('fieldType', function () { - return function (fields, types) { - if (!types) return fields; - if (!_.isArray(types)) types = [types]; - if (_.contains(types, '*')) return fields; - - var filters = types.map(function (type) { - var filter = { - match: true, - type: type - }; - - if (type.charAt(0) === '!') { - filter.match = false; - filter.type = type.substr(1); - } - return filter; - }); - - return fields.filter(function (field) { - for (var i = 0; i < filters.length; i++) { - var filter = filters[i]; - if ((field.type === filter.type) === filter.match) return true; - } - }); - }; - }); + .get('kibana') + .filter('fieldType', function () { + return propFilter('type'); + }); }); \ No newline at end of file diff --git a/src/kibana/plugins/visualize/editor/agg_filter.js b/src/kibana/plugins/visualize/editor/agg_filter.js index e839c639329c..bc0f5b6ac589 100644 --- a/src/kibana/plugins/visualize/editor/agg_filter.js +++ b/src/kibana/plugins/visualize/editor/agg_filter.js @@ -1,36 +1,10 @@ -// Gets all fields of a given type. -// You may also pass "*" to get all types -// Or an array of types to get all fields of that type define(function (require) { var _ = require('lodash'); + var propFilter = require('filters/_prop_filter'); require('modules') - .get('kibana') - .filter('aggFilter', function () { - return function (aggs, names) { - if (!names) return aggs; - if (!_.isArray(names)) names = [names]; - if (_.contains(names, '*')) return aggs; - - var filters = names.map(function (name) { - var filter = { - match: true, - name: name - }; - - if (name.charAt(0) === '!') { - filter.match = false; - filter.name = name.substr(1); - } - return filter; - }); - - return aggs.filter(function (agg) { - for (var i = 0; i < filters.length; i++) { - var filter = filters[i]; - if ((agg.name === filter.name) === filter.match) return true; - } - }); - }; - }); + .get('kibana') + .filter('aggFilter', function () { + return propFilter('name'); + }); }); \ No newline at end of file