diff --git a/spec/tooltip-spec.js b/spec/tooltip-spec.js index 0b65333..52921d0 100644 --- a/spec/tooltip-spec.js +++ b/spec/tooltip-spec.js @@ -1,29 +1,38 @@ var describe = window.describe, expect = window.expect, it = window.it, + jasmine = window.jasmine, + beforeAll = window.beforeAll, beforeEach = window.beforeEach; describe('c3 chart tooltip', function () { 'use strict'; var chart, d3; - - var args = { - data: { - columns: [ - ['data1', 30, 200, 100, 400, 150, 250], - ['data2', 50, 20, 10, 40, 15, 25], - ['data3', 150, 120, 110, 140, 115, 125] - ] - } + var tooltipConfiguration; + + var args = function () { + return { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ], + }, + tooltip: tooltipConfiguration + }; }; beforeEach(function (done) { - chart = window.initChart(chart, args, done); + chart = window.initChart(chart, args(), done); d3 = chart.internal.d3; }); describe('tooltip position', function () { + beforeAll(function () { + tooltipConfiguration = {}; + }); describe('without left margin', function () { @@ -66,4 +75,35 @@ describe('c3 chart tooltip', function () { }); + describe('tooltip positionFunction', function () { + var topExpected = 37, leftExpected = 79; + + beforeAll(function () { + tooltipConfiguration = { + position: function (data, width, height, element) { + expect(data.length).toBe(args().data.columns.length); + expect(data[0]).toEqual(jasmine.objectContaining({ + index: 2, + value: 100, + id: 'data1' + })); + expect(width).toBeGreaterThan(0); + expect(height).toBeGreaterThan(0); + expect(element).toBe(d3.select('.c3-event-rect-2').node()); + return {top: topExpected, left: leftExpected}; + } + }; + }); + + it('should be set to the coordinate where the function returned', function () { + var eventRect = d3.select('.c3-event-rect-2').node(); + window.setMouseEvent(chart, 'mousemove', 100, 100, eventRect); + + var tooltipContainer = d3.select('.c3-tooltip-container'), + top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), + left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')); + expect(top).toBe(topExpected); + expect(left).toBe(leftExpected); + }); + }); }); diff --git a/src/arc.js b/src/arc.js index b54da30..c1c4bd6 100644 --- a/src/arc.js +++ b/src/arc.js @@ -273,7 +273,7 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf var updated = $$.updateAngle(d), arcData = $$.convertToArcData(updated), selectedData = [arcData]; - $$.showTooltip(selectedData, d3.mouse(this)); + $$.showTooltip(selectedData, this); } : null) .on('mouseout', config.interaction_enabled ? function (d) { var updated, arcData; diff --git a/src/config.js b/src/config.js index d64a783..a53f879 100644 --- a/src/config.js +++ b/src/config.js @@ -183,6 +183,7 @@ c3_chart_internal_fn.getDefaultConfig = function () { tooltip_format_title: undefined, tooltip_format_name: undefined, tooltip_format_value: undefined, + tooltip_position: undefined, tooltip_contents: function (d, defaultTitleFormat, defaultValueFormat, color) { return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : ''; }, diff --git a/src/interaction.js b/src/interaction.js index 0b9039c..c677074 100644 --- a/src/interaction.js +++ b/src/interaction.js @@ -175,7 +175,7 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) { }); if (config.tooltip_grouped) { - $$.showTooltip(selectedData, d3.mouse(this)); + $$.showTooltip(selectedData, this); $$.showXGridFocus(selectedData); } @@ -206,7 +206,7 @@ c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) { eventRect.style('cursor', 'pointer'); } if (!config.tooltip_grouped) { - $$.showTooltip([d], d3.mouse(this)); + $$.showTooltip([d], this); $$.showXGridFocus([d]); if (config.point_focus_expand_enabled) { $$.expandCircles(index, d.id, true); } $$.expandBars(index, d.id, true); @@ -289,7 +289,7 @@ c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) selectedData = sameXData.map(function (d) { return $$.addName(d); }); - $$.showTooltip(selectedData, mouse); + $$.showTooltip(selectedData, this); // expand points if (config.point_focus_expand_enabled) { diff --git a/src/tooltip.js b/src/tooltip.js index d4bad52..d6afd35 100644 --- a/src/tooltip.js +++ b/src/tooltip.js @@ -49,20 +49,12 @@ c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaul } return text + ""; }; -c3_chart_internal_fn.showTooltip = function (selectedData, mouse) { - var $$ = this, config = $$.config; - var tWidth, tHeight, svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; +c3_chart_internal_fn.tooltipPosition = function (dataToShow, tWidth, tHeight, element) { + var $$ = this, config = $$.config, d3 = $$.d3; + var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; var forArc = $$.hasArcType(), - dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }); - if (dataToShow.length === 0 || !config.tooltip_show) { - return; - } - $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block"); - - // Get tooltip dimensions - tWidth = $$.tooltip.property('offsetWidth'); - tHeight = $$.tooltip.property('offsetHeight'); - // Determin tooltip position + mouse = d3.mouse(element); + // Determin tooltip position if (forArc) { tooltipLeft = ($$.width / 2) + mouse[0]; tooltipTop = ($$.height / 2) + mouse[1] + 20; @@ -90,10 +82,28 @@ c3_chart_internal_fn.showTooltip = function (selectedData, mouse) { if (tooltipTop < 0) { tooltipTop = 0; } + return {top: tooltipTop, left: tooltipLeft}; +}; +c3_chart_internal_fn.showTooltip = function (selectedData, element) { + var $$ = this, config = $$.config; + var tWidth, tHeight, position; + var forArc = $$.hasArcType(), + dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), + positionFunction = config.tooltip_position || c3_chart_internal_fn.tooltipPosition; + if (dataToShow.length === 0 || !config.tooltip_show) { + return; + } + $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block"); + + // Get tooltip dimensions + tWidth = $$.tooltip.property('offsetWidth'); + tHeight = $$.tooltip.property('offsetHeight'); + + position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); // Set tooltip $$.tooltip - .style("top", tooltipTop + "px") - .style("left", tooltipLeft + 'px'); + .style("top", position.top + "px") + .style("left", position.left + 'px'); }; c3_chart_internal_fn.hideTooltip = function () { this.tooltip.style("display", "none");