From 4cf489ee87f2f2670141569215a476822b2105e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 3 Oct 2017 16:15:57 -0400 Subject: [PATCH 01/13] introduce assertHoverLabelContent custom assertion --- test/jasmine/assets/custom_assertions.js | 86 ++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/test/jasmine/assets/custom_assertions.js b/test/jasmine/assets/custom_assertions.js index 138020ac3a8..a2d90df5844 100644 --- a/test/jasmine/assets/custom_assertions.js +++ b/test/jasmine/assets/custom_assertions.js @@ -59,6 +59,92 @@ exports.assertHoverLabelStyle = function(g, expectation, msg, textSelector) { expect(textStyle.fill).toBe(expectation.fontColor, msg + ': font.color'); }; +function assertLabelContent(label, expectation, msg) { + var lines = label.selectAll('tspan'); + var lineCnt = lines.size(); + var expectMultiLine = Array.isArray(expectation); + + function extract(sel) { + return sel.node() ? sel.html() : null; + } + + if(lineCnt > 0) { + if(expectMultiLine) { + expect(lines.size()).toBe(expectation.length, msg + ': # of lines'); + lines.each(function(_, i) { + var l = d3.select(this); + expect(extract(l)).toBe(expectation[i], msg + ': tspan line ' + i); + }); + } else { + fail('Expected a single-line label, found multiple lines'); + } + } else { + if(!expectMultiLine) { + expect(extract(label)).toBe(expectation, msg + ': text content'); + } else { + fail('Expected a multi-line label, found single'); + } + } +} + +function count(selector) { + return d3.selectAll(selector).size(); +} + +exports.assertHoverLabelContent = function(expectation, msg) { + if(!msg) msg = ''; + + var ptSelector = 'g.hovertext'; + var ptExpectation = expectation[0]; + var ptMsg = msg + ' point hover label'; + var ptCnt = count(ptSelector); + + var axSelector = 'g.axistext'; + var axExpectation = expectation[1]; + var axMsg = 'common axis hover label'; + var axCnt = count(axSelector); + + if(ptCnt === 1) { + assertLabelContent( + d3.select(ptSelector + '> text.nums'), + ptExpectation[0], + ptMsg + ' (nums)' + ); + assertLabelContent( + d3.select(ptSelector + '> text.name'), + ptExpectation[1], + ptMsg + ' (name)' + ); + } else if(ptCnt > 1) { + expect(ptCnt).toBe(ptExpectation.length, ptMsg + ' # of visible nodes'); + + d3.selectAll(ptSelector).each(function(_, i) { + assertLabelContent( + d3.select(this).select('text.nums'), + ptExpectation[i][0], + ptMsg + ' (nums ' + i + ')' + ); + assertLabelContent( + d3.select(this).select('text.name'), + ptExpectation[i][1], + ptMsg + ' (name ' + i + ')' + ); + }); + } else { + expect(ptExpectation).toBe(null, ptMsg + ' should not be displayed'); + } + + if(axCnt > 0) { + assertLabelContent( + d3.select(axSelector + '> text'), + axExpectation, + axMsg + ); + } else { + expect(axExpectation).toBe(null, axMsg + ' should not be displayed'); + } +}; + exports.assertClip = function(sel, isClipped, size, msg) { expect(sel.size()).toBe(size, msg + ' clip path (selection size)'); From 90e2de132bfddbc8d089df79a111b92c2bb2c8a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 3 Oct 2017 16:21:51 -0400 Subject: [PATCH 02/13] replace hover-label test boiler-plate with assertHoverLabelContent --- test/jasmine/tests/gl2d_click_test.js | 34 +-- test/jasmine/tests/gl_plot_interact_test.js | 19 +- test/jasmine/tests/hover_label_test.js | 244 +++++++++----------- test/jasmine/tests/mapbox_test.js | 19 +- test/jasmine/tests/pie_test.js | 19 +- test/jasmine/tests/scattergeo_test.js | 37 +-- test/jasmine/tests/ternary_test.js | 58 +++-- 7 files changed, 211 insertions(+), 219 deletions(-) diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index c966a3bc205..e05f3682b9b 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -5,7 +5,10 @@ var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var fail = require('../assets/fail_test.js'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; // cartesian click events events use the hover data // from the mousemove events and then simulate @@ -131,25 +134,6 @@ describe('Test hover and click interactions', function() { expect(String(pt.pointNumber)).toBe(String(expected.pointNumber), msg + ' - point number'); } - function assertHoveLabelContent(expected) { - var label = expected.label; - - if(label === undefined) return; - - var g = d3.select('.hovertext'); - - if(label === null) { - expect(g.size()).toBe(0); - } else { - var lines = g.selectAll('text.nums'); - - expect(lines.size()).toBe(label.length); - lines.each(function(_, i) { - expect(d3.select(this).text()).toEqual(label[i]); - }); - } - } - // returns basic hover/click/unhover runner for one xy position function makeRunner(pos, expected, opts) { opts = opts || {}; @@ -166,12 +150,16 @@ describe('Test hover and click interactions', function() { .then(_hover) .then(function(eventData) { assertEventData(eventData, expected); + var g = d3.select('g.hovertext'); if(g.node() === null) { expect(expected.noHoverLabel).toBe(true); + } else { + assertHoverLabelStyle(g, expected, opts.msg); + } + if(expected.label) { + assertHoverLabelContent([expected.label, null]); } - else assertHoverLabelStyle(g, expected, opts.msg); - assertHoveLabelContent(expected); }) .then(_click) .then(function(eventData) { @@ -211,7 +199,7 @@ describe('Test hover and click interactions', function() { var run = makeRunner([634, 321], { x: 15.772, y: 0.387, - label: ['0.387'], + label: ['0.387', null], curveNumber: 0, pointNumber: 33, bgcolor: 'rgb(0, 0, 255)', diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index c76bda805a5..776a2e4c2d1 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -11,7 +11,10 @@ var fail = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var selectButton = require('../assets/modebar_button'); var delay = require('../assets/delay'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; function countCanvases() { return d3.selectAll('canvas').size(); @@ -33,17 +36,9 @@ describe('Test gl3d plots', function() { var mock3 = require('@mocks/gl3d_autocolorscale'); function assertHoverText(xLabel, yLabel, zLabel, textLabel) { - var node = d3.selectAll('g.hovertext'); - expect(node.size()).toEqual(1, 'hover text group'); - - var tspan = d3.selectAll('g.hovertext').selectAll('tspan')[0]; - expect(tspan[0].innerHTML).toEqual(xLabel, 'x val'); - expect(tspan[1].innerHTML).toEqual(yLabel, 'y val'); - expect(tspan[2].innerHTML).toEqual(zLabel, 'z val'); - - if(textLabel) { - expect(tspan[3].innerHTML).toEqual(textLabel, 'text label'); - } + var content = [xLabel, yLabel, zLabel]; + if(textLabel) content.push(textLabel); + assertHoverLabelContent([[content, null], null]); } function assertEventData(x, y, z, curveNumber, pointNumber, extra) { diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index df7bef8e771..c413116fd31 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -12,7 +12,10 @@ var click = require('../assets/click'); var delay = require('../assets/delay'); var doubleClick = require('../assets/double_click'); var fail = require('../assets/fail_test'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; describe('hover info', function() { 'use strict'; @@ -40,10 +43,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('1'); + assertHoverLabelContent([ + ['1', null], + '0.388' + ]); }); }); @@ -67,9 +70,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(0); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + assertHoverLabelContent([ + null, + '0.388' + ]); }); }); @@ -93,9 +97,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('1'); + assertHoverLabelContent([ + ['1', null], + null + ]); }); }); @@ -123,10 +128,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').select('text').html()) - .toEqual('hover text with spaces not newlines'); + assertHoverLabelContent([ + ['hover text with spaces not newlines', null], + null + ]); }); }); @@ -152,12 +157,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text.nums').selectAll('tspan').size()).toEqual(2); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text'); + assertHoverLabelContent([ + [['1', 'hover text'], 'PV learning ...'], + '0.388' + ]); }); }); @@ -190,13 +193,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text.nums').selectAll('tspan').size()).toEqual(2); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text'); - expect(d3.selectAll('g.hovertext').selectAll('text.name').node().innerHTML).toEqual('<img src=x o...'); + assertHoverLabelContent([ + [['1', 'hover text'], '<img src=x o...'], + '0.388' + ]); }); }); @@ -213,10 +213,13 @@ describe('hover info', function() { Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover y+text', function() { + it('responds to hover y', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.hovertext').selectAll('text.nums').node().innerHTML).toEqual('1e+9'); + assertHoverLabelContent([ + ['1e+9', null], + null + ]); }); }); @@ -242,11 +245,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').selectAll('tspan').size()).toEqual(2); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text'); + assertHoverLabelContent([ + [['1', 'hover text'], null], + null + ]); }); }); @@ -272,10 +274,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('hover text'); + assertHoverLabelContent([ + ['hover text', null], + '0.388' + ]); }); }); @@ -292,8 +294,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388 ± 1'); + assertHoverLabelContent([ + ['1', null], + '0.388 ± 1' + ]); }); }); @@ -310,8 +314,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + assertHoverLabelContent([ + ['1', null], + '0.388' + ]); }); }); @@ -328,8 +334,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + assertHoverLabelContent([ + ['1', null], + '0.388' + ]); }); }); @@ -355,11 +363,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('hover'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('text'); - expect(d3.selectAll('g.hovertext').select('text').selectAll('tspan').size()).toEqual(2); + assertHoverLabelContent([ + [['hover', 'text'], null], + null + ]); }); }); @@ -377,6 +384,7 @@ describe('hover info', function() { Fx.hover('graph', evt, 'xy'); expect(gd._hoverdata, undefined); + assertHoverLabelContent([null, null]); }); }); @@ -400,8 +408,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(0); + assertHoverLabelContent([null, null]); }); }); @@ -432,13 +439,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.33); expect(hoverTrace.y).toEqual(1.25); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - - var expectations = ['PV learning ...', '(0.33, 1.25)']; - d3.selectAll('g.hovertext').selectAll('text').each(function(_, i) { - expect(d3.select(this).html()).toEqual(expectations[i]); - }); + assertHoverLabelContent([ + ['(0.33, 1.25)', 'PV learning ...'], + null + ]); }); it('render only non-hoverinfo \'none\' hover labels', function(done) { @@ -456,15 +460,12 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.33); expect(hoverTrace.y).toEqual(1.25); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - - var text = d3.selectAll('g.hovertext').select('text'); - expect(text.size()).toEqual(1); - expect(text.html()).toEqual('PV learning ...'); - - done(); - }); + assertHoverLabelContent([ + ['PV learning ...', null], + null + ]); + }) + .then(done); }); }); @@ -474,16 +475,6 @@ describe('hover info', function() { Lib.clearThrottle(); } - function _assert(nameLabel, lines) { - expect(d3.select('g.axistext').size()).toEqual(0, 'no common label'); - - var sel = d3.select('g.hovertext'); - expect(sel.select('text.name').html()).toEqual(nameLabel, 'name label'); - sel.select('text.nums').selectAll('tspan').each(function(_, i) { - expect(d3.select(this).html()).toEqual(lines[i], 'lines ' + i); - }); - } - it('should display correct label content', function(done) { var gd = createGraphDiv(); @@ -504,11 +495,17 @@ describe('hover info', function() { }) .then(function() { _hover(gd, 250, 100); - _assert('two', ['x: 1', 'y: 3', 'z: 2']); + assertHoverLabelContent([ + [['x: 1', 'y: 3', 'z: 2'], 'two'], + null + ]); }) .then(function() { _hover(gd, 250, 300); - _assert('one', ['x: 1', 'y: 1', 'z: 2']); + assertHoverLabelContent([ + [['x: 1', 'y: 1', 'z: 2'], 'one'], + null + ]); }) .catch(fail) .then(done); @@ -516,7 +513,6 @@ describe('hover info', function() { }); describe('hoverformat', function() { - var data = [{ x: [1, 2, 3], y: [0.12345, 0.23456, 0.34567] @@ -535,10 +531,10 @@ describe('hover info', function() { Plotly.plot(this.gd, data, layout); mouseEvent('mousemove', 303, 213); - var hovers = d3.selectAll('g.hovertext'); - - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('0.23'); + assertHoverLabelContent([ + ['0.23', null], + '2' + ]); }); it('should display the correct format when ticklabels false', function() { @@ -546,15 +542,14 @@ describe('hover info', function() { Plotly.plot(this.gd, data, layout); mouseEvent('mousemove', 303, 213); - var hovers = d3.selectAll('g.hovertext'); - - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('0.23'); + assertHoverLabelContent([ + ['0.23', null], + '2' + ]); }); }); describe('textmode', function() { - var data = [{ x: [1, 2, 3, 4], y: [2, 3, 4, 5], @@ -573,28 +568,28 @@ describe('hover info', function() { it('should show text labels', function() { mouseEvent('mousemove', 108, 303); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('test'); + assertHoverLabelContent([ + ['test', null], + null + ]); }); it('should show number labels', function() { mouseEvent('mousemove', 363, 173); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('42'); + assertHoverLabelContent([ + ['42', null], + null + ]); }); it('should not show null text labels', function() { mouseEvent('mousemove', 229, 239); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(0); + assertHoverLabelContent([null, null]); }); it('should not show undefined text labels', function() { mouseEvent('mousemove', 493, 108); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(0); + assertHoverLabelContent([null, null]); }); }); @@ -688,18 +683,13 @@ describe('hover info on stacked subplots', function() { y: 1000 })); - // There should be a single label on the x-axis with the shared x value, 3. - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('3'); - - // There should be two points being hovered over, in two different traces, one in each plot. - expect(d3.selectAll('g.hovertext').size()).toEqual(2); - var textNodes = d3.selectAll('g.hovertext').selectAll('text'); - - expect(textNodes[0][0].innerHTML).toEqual('trace 1'); - expect(textNodes[0][1].innerHTML).toEqual('110'); - expect(textNodes[1][0].innerHTML).toEqual('trace 2'); - expect(textNodes[1][1].innerHTML).toEqual('1000'); + assertHoverLabelContent([ + // There should be 2 pts being hovered over, + // in two different traces, one in each plot. + [['110', 'trace 1'], ['1000', 'trace 2']], + // There should be a single label on the x-axis with the shared x value, 3' + '3' + ]); }); }); @@ -796,7 +786,6 @@ describe('hover info on overlaid subplots', function() { }); describe('hover after resizing', function() { - 'use strict'; var gd; afterEach(destroyGraphDiv); @@ -811,51 +800,48 @@ describe('hover after resizing', function() { }); } - function assertLabelCount(pos, cnt, msg) { + function check(pos, expectation, msg) { Lib.clearThrottle(); mouseEvent('mousemove', pos[0], pos[1]); - - var hoverText = d3.selectAll('g.hovertext'); - expect(hoverText.size()).toBe(cnt, msg); + assertHoverLabelContent(expectation, msg); } it('should work', function(done) { - var data = [{ y: [2, 1, 2] }], - layout = { width: 600, height: 500 }; gd = createGraphDiv(); - var pos0 = [305, 403], - pos1 = [401, 122]; + var data = [{ y: [2, 1, 2] }]; + var layout = { width: 600, height: 500 }; - Plotly.plot(gd, data, layout).then(function() { + var pos0 = [305, 403]; + var pos1 = [401, 122]; + Plotly.plot(gd, data, layout).then(function() { // to test https://github.com/plotly/plotly.js/issues/1044 - return _click(pos0); }) .then(function() { - return assertLabelCount(pos0, 1, 'before resize, showing pt label'); + return check(pos0, [['1', null], '1'], 'before resize, showing pt label'); }) .then(function() { - return assertLabelCount(pos1, 0, 'before resize, not showing blank spot'); + return check(pos1, [null, null], 'before resize, not showing blank spot'); }) .then(function() { return Plotly.relayout(gd, 'width', 500); }) .then(function() { - return assertLabelCount(pos0, 0, 'after resize, not showing blank spot'); + return check(pos0, [null, null], 'after resize, not showing blank spot'); }) .then(function() { - return assertLabelCount(pos1, 1, 'after resize, showing pt label'); + return check(pos1, [['2', null], '2'], 'after resize, showing pt label'); }) .then(function() { return Plotly.relayout(gd, 'width', 600); }) .then(function() { - return assertLabelCount(pos0, 1, 'back to initial, showing pt label'); + return check(pos0, [['1', null], '1'], 'back to initial, showing pt label'); }) .then(function() { - return assertLabelCount(pos1, 0, 'back to initial, not showing blank spot'); + return check(pos1, [null, null], 'back to initial, not showing blank spot'); }) .then(done); }); diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js index 1075b0574ce..d40efef9bef 100644 --- a/test/jasmine/tests/mapbox_test.js +++ b/test/jasmine/tests/mapbox_test.js @@ -11,6 +11,10 @@ var destroyGraphDiv = require('../assets/destroy_graph_div'); var mouseEvent = require('../assets/mouse_event'); var failTest = require('../assets/fail_test'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + var MAPBOX_ACCESS_TOKEN = require('@build/credentials.json').MAPBOX_ACCESS_TOKEN; var TRANSITION_DELAY = 500; var MOUSE_DELAY = 100; @@ -724,11 +728,16 @@ describe('@noCI, mapbox plots', function() { return assertMouseMove(pointPos, 1); }) .then(function() { - var path = d3.select('g.hovertext').select('path').node(); - var text = d3.select('g.hovertext').select('text.nums').node(); - - expect(path.style.fill).toEqual('rgb(255, 255, 0)', 'bgcolor'); - expect(text.style.fontSize).toEqual('20px', 'font.size[0]'); + assertHoverLabelStyle(d3.select('g.hovertext'), { + bgcolor: 'rgb(255, 255, 0)', + bordercolor: 'rgb(68, 68, 68)', + fontSize: 20, + fontFamily: 'Arial', + fontColor: 'rgb(68, 68, 68)' + }); + assertHoverLabelContent([ + [['(10°, 10°)', ''], 'trace 0'], null + ]); }) .catch(failTest) .then(done); diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index 0e138d1fc56..80ba76f532b 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -8,7 +8,10 @@ var failTest = require('../assets/fail_test'); var click = require('../assets/click'); var getClientPosition = require('../assets/get_client_position'); var mouseEvent = require('../assets/mouse_event'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; describe('Pie traces:', function() { 'use strict'; @@ -245,17 +248,13 @@ describe('pie hovering', function() { } function assertLabel(content, style, msg) { - var g = d3.selectAll('.hovertext'); - var lines = g.selectAll('.nums .line'); - - expect(lines.size()).toBe(content.length); - - lines.each(function(_, i) { - expect(d3.select(this).text()).toBe(content[i]); - }); + assertHoverLabelContent([ + [content, null], + null + ], msg); if(style) { - assertHoverLabelStyle(g, { + assertHoverLabelStyle(d3.select('.hovertext'), { bgcolor: style[0], bordercolor: style[1], fontSize: style[2], diff --git a/test/jasmine/tests/scattergeo_test.js b/test/jasmine/tests/scattergeo_test.js index 95b62af55d5..15ae301d90e 100644 --- a/test/jasmine/tests/scattergeo_test.js +++ b/test/jasmine/tests/scattergeo_test.js @@ -10,6 +10,10 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var mouseEvent = require('../assets/mouse_event'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + describe('Test scattergeo defaults', function() { var traceIn, traceOut; @@ -233,10 +237,6 @@ describe('Test scattergeo calc', function() { describe('Test scattergeo hover', function() { var gd; - // we can't mock ScatterGeo.hoverPoints - // because geo hover relies on mouse event - // to set the c2p conversion functions - beforeEach(function(done) { gd = createGraphDiv(); @@ -251,39 +251,42 @@ describe('Test scattergeo hover', function() { afterEach(destroyGraphDiv); - function assertHoverLabels(expected) { - var hoverText = d3.selectAll('g.hovertext').selectAll('tspan'); + function check(pos, content, style) { + mouseEvent('mousemove', pos[0], pos[1]); - hoverText.each(function(_, i) { - expect(this.innerHTML).toEqual(expected[i]); - }); + style = style || { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(255, 255, 255)', + fontColor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial' + }; + + assertHoverLabelContent([content, null]); + assertHoverLabelStyle(d3.select('g.hovertext'), style); } it('should generate hover label info (base case)', function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'A']); + check([381, 221], [['(10°, 10°)', 'A'], null]); }); it('should generate hover label info (\'text\' single value case)', function(done) { Plotly.restyle(gd, 'text', 'text').then(function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'text']); + check([381, 221], [['(10°, 10°)', 'text'], null]); }) .then(done); }); it('should generate hover label info (\'hovertext\' single value case)', function(done) { Plotly.restyle(gd, 'hovertext', 'hovertext').then(function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'hovertext']); + check([381, 221], [['(10°, 10°)', 'hovertext'], null]); }) .then(done); }); it('should generate hover label info (\'hovertext\' array case)', function(done) { Plotly.restyle(gd, 'hovertext', ['Apple', 'Banana', 'Orange']).then(function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'Apple']); + check([381, 221], [['(10°, 10°)', 'Apple'], null]); }) .then(done); }); diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 387e582a70c..6f4ee022c02 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -12,6 +12,10 @@ var click = require('../assets/click'); var doubleClick = require('../assets/double_click'); var getClientPosition = require('../assets/get_client_position'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + describe('ternary plots', function() { 'use strict'; @@ -102,35 +106,47 @@ describe('ternary plots', function() { }); it('should display to hover labels', function(done) { - var hoverLabels; - mouseEvent('mousemove', blankPos[0], blankPos[1]); - hoverLabels = findHoverLabels(); - expect(hoverLabels.size()).toEqual(0, 'only on data points'); + assertHoverLabelContent([null, null], 'only on data points'); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - hoverLabels = findHoverLabels(); - expect(hoverLabels.size()).toEqual(1, 'one per data point'); + function check(content, style, msg) { + Lib.clearThrottle(); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + assertHoverLabelContent([[content, null], null], msg); + assertHoverLabelStyle(d3.select('g.hovertext'), style, msg); + } - var rows = hoverLabels.selectAll('tspan'); - expect(rows[0][0].innerHTML).toEqual('Component A: 0.5', 'with correct text'); - expect(rows[0][1].innerHTML).toEqual('B: 0.25', 'with correct text'); - expect(rows[0][2].innerHTML).toEqual('Component C: 0.25', 'with correct text'); + check([ + 'Component A: 0.5', + 'B: 0.25', + 'Component C: 0.25' + ], { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(255, 255, 255)', + fontColor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial' + }, 'one label per data pt'); Plotly.restyle(gd, { 'hoverlabel.bordercolor': 'blue', 'hoverlabel.font.family': [['Gravitas', 'Arial', 'Roboto']] }) .then(function() { - Lib.clearThrottle(); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - - var pathStyle = window.getComputedStyle(d3.select('g.hovertext path').node()); - var textStyle = window.getComputedStyle(d3.select('g.hovertext text.nums').node()); - - expect(pathStyle.stroke).toEqual('rgb(0, 0, 255)', 'bordercolor'); - expect(textStyle.fontFamily).toEqual('Gravitas', 'font.family[0]'); + check([ + 'Component A: 0.5', + 'B: 0.25', + 'Component C: 0.25' + ], { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(0, 0, 255)', + fontColor: 'rgb(0, 0, 255)', + fontSize: 13, + fontFamily: 'Gravitas' + }, 'after hoverlabel styling restyle call'); }) + .catch(fail) .then(done); }); @@ -319,10 +335,6 @@ describe('ternary plots', function() { return d3.selectAll('.ternary').selectAll('g.trace.' + type).size(); } - function findHoverLabels() { - return d3.select('.hoverlayer').selectAll('g'); - } - function drag(path) { var len = path.length; From c89c89542f3554e7a8b31f6a6e4da29eaedb63d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 3 Oct 2017 16:24:43 -0400 Subject: [PATCH 03/13] move scattergeo & choropleth hover label test trace module suits --- test/jasmine/tests/choropleth_test.js | 80 ++++++++++++++++++++++++ test/jasmine/tests/geo_test.js | 90 --------------------------- test/jasmine/tests/scattergeo_test.js | 25 ++++++++ 3 files changed, 105 insertions(+), 90 deletions(-) diff --git a/test/jasmine/tests/choropleth_test.js b/test/jasmine/tests/choropleth_test.js index 1cdcdcd28aa..d6c7c561dba 100644 --- a/test/jasmine/tests/choropleth_test.js +++ b/test/jasmine/tests/choropleth_test.js @@ -1,6 +1,17 @@ var Choropleth = require('@src/traces/choropleth'); + +var Plotly = require('@lib'); var Plots = require('@src/plots/plots'); +var Lib = require('@src/lib'); + +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); +var mouseEvent = require('../assets/mouse_event'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; describe('Test choropleth', function() { 'use strict'; @@ -45,7 +56,76 @@ describe('Test choropleth', function() { Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout); expect(traceOut.visible).toBe(false); }); + }); +}); + +describe('Test choropleth hover:', function() { + var gd; + + afterEach(destroyGraphDiv); + + function run(pos, fig, content, style) { + gd = createGraphDiv(); + + style = style || { + bgcolor: 'rgb(68, 68, 68)', + bordercolor: 'rgb(255, 255, 255)', + fontColor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial' + }; + + return Plotly.plot(gd, fig).then(function() { + mouseEvent('mousemove', pos[0], pos[1]); + assertHoverLabelContent([content, null]); + assertHoverLabelStyle(d3.select('g.hovertext'), style); + }); + } + + it('should generate hover label info (base)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + + run( + [400, 160], + fig, + [['RUS', '10', ''], 'trace 1'] + ) + .then(done); + }); + + it('should generate hover label info (\'text\' array case)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].text = ['tExT', 'TeXt', '-text-']; + fig.data[1].hoverinfo = 'text'; + run( + [400, 160], + fig, + ['-text-', null] + ) + .then(done); }); + it('should generate hover label with custom styling', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].hoverlabel = { + bgcolor: 'red', + bordercolor: ['blue', 'black', 'green'], + font: {family: 'Roboto'} + }; + + run( + [400, 160], + fig, + [['RUS', '10', ''], 'trace 1'], + { + bgcolor: 'rgb(255, 0, 0)', + bordercolor: 'rgb(0, 128, 0)', + fontColor: 'rgb(0, 128, 0)', + fontSize: 13, + fontFamily: 'Roboto' + } + ) + .then(done); + }); }); diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 12d39069e89..bca4661146e 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -553,72 +553,6 @@ describe('Test geo interactions', function() { Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); }); - describe('scattergeo hover labels', function() { - it('should show one hover text group', function() { - mouseEventScatterGeo('mousemove'); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - }); - - it('should show longitude and latitude values', function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][0]; - expect(node.innerHTML).toEqual('(0°, 0°)'); - }); - - it('should show the trace name', function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('text')[0][0]; - expect(node.innerHTML).toEqual('trace 0'); - }); - - it('should show *text* (case 1)', function(done) { - Plotly.restyle(gd, 'text', [['A', 'B']]).then(function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][1]; - expect(node.innerHTML).toEqual('A'); - }) - .then(done); - }); - - it('should show *text* (case 2)', function(done) { - Plotly.restyle(gd, 'text', [[null, 'B']]).then(function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][1]; - expect(node).toBeUndefined(); - }) - .then(done); - }); - - it('should show *text* (case 3)', function(done) { - Plotly.restyle(gd, 'text', [['', 'B']]).then(function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][1]; - expect(node).toBeUndefined(); - }) - .then(done); - }); - - it('should show custom \`hoverlabel\' settings', function(done) { - Plotly.restyle(gd, { - 'hoverlabel.bgcolor': 'red', - 'hoverlabel.bordercolor': [['blue', 'black', 'green']] - }) - .then(function() { - mouseEventScatterGeo('mousemove'); - - var pathStyle = window.getComputedStyle(d3.select('g.hovertext path').node()); - expect(pathStyle.fill).toEqual('rgb(255, 0, 0)', 'bgcolor'); - expect(pathStyle.stroke).toEqual('rgb(0, 0, 255)', 'bordecolor[0]'); - }) - .then(done); - }); - }); - describe('scattergeo hover events', function() { var ptData, cnt; @@ -745,30 +679,6 @@ describe('Test geo interactions', function() { }); }); - describe('choropleth hover labels', function() { - beforeEach(function() { - mouseEventChoropleth('mouseover'); - mouseEventChoropleth('mousemove'); - }); - - it('should show one hover text group', function() { - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - }); - - it('should show location and z values', function() { - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0]; - - expect(node[0].innerHTML).toEqual('RUS'); - expect(node[1].innerHTML).toEqual('10'); - }); - - it('should show the trace name', function() { - var node = d3.selectAll('g.hovertext').selectAll('text')[0][0]; - - expect(node.innerHTML).toEqual('trace 1'); - }); - }); - describe('choropleth hover events', function() { var ptData; diff --git a/test/jasmine/tests/scattergeo_test.js b/test/jasmine/tests/scattergeo_test.js index 15ae301d90e..480dff5346d 100644 --- a/test/jasmine/tests/scattergeo_test.js +++ b/test/jasmine/tests/scattergeo_test.js @@ -270,6 +270,13 @@ describe('Test scattergeo hover', function() { check([381, 221], [['(10°, 10°)', 'A'], null]); }); + it('should generate hover label info (with trace name)', function(done) { + Plotly.restyle(gd, 'hoverinfo', 'lon+lat+text+name').then(function() { + check([381, 221], [['(10°, 10°)', 'A'], 'trace 0']); + }) + .then(done); + }); + it('should generate hover label info (\'text\' single value case)', function(done) { Plotly.restyle(gd, 'text', 'text').then(function() { check([381, 221], [['(10°, 10°)', 'text'], null]); @@ -290,4 +297,22 @@ describe('Test scattergeo hover', function() { }) .then(done); }); + + it('should generate hover label with custom styling', function(done) { + Plotly.restyle(gd, { + 'hoverlabel.bgcolor': 'red', + 'hoverlabel.bordercolor': [['blue', 'black', 'green']] + }) + .then(function() { + check([381, 221], [['(10°, 10°)', 'A'], null], { + bgcolor: 'rgb(255, 0, 0)', + bordercolor: 'rgb(0, 0, 255)', + fontColor: 'rgb(0, 0, 255)', + fontSize: 13, + fontFamily: 'Arial' + }); + }) + .then(done); + }); + }); From b24759d97b912bae127f99c6d9d7c2416f92d5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 3 Oct 2017 16:26:17 -0400 Subject: [PATCH 04/13] fix scattercarpet 'y' hover format + add tests --- src/traces/scattercarpet/hover.js | 2 +- test/jasmine/tests/carpet_test.js | 39 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js index 980072cb12e..91ca66cd49f 100644 --- a/src/traces/scattercarpet/hover.js +++ b/src/traces/scattercarpet/hover.js @@ -67,7 +67,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var tj = ij[1] - j0; var xy = carpet.evalxy([], i0, j0, ti, tj); - text.push('y: ' + xy[1].toFixed(3)); + text.push('y = ' + xy[1].toFixed(3)); newPointData.extraText = text.join('
'); diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js index a302de085e5..c63439ac42f 100644 --- a/test/jasmine/tests/carpet_test.js +++ b/test/jasmine/tests/carpet_test.js @@ -11,6 +11,9 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var fail = require('../assets/fail_test'); +var mouseEvent = require('../assets/mouse_event'); +var assertHoverLabelContent = require('../assets/custom_assertions').assertHoverLabelContent; + describe('carpet supplyDefaults', function() { 'use strict'; @@ -565,3 +568,39 @@ describe('scattercarpet array attributes', function() { .then(done); }); }); + +describe('scattercarpet hover labels', function() { + var gd; + + afterEach(destroyGraphDiv); + + function run(pos, fig, content) { + gd = createGraphDiv(); + + return Plotly.plot(gd, fig).then(function() { + mouseEvent('mousemove', pos[0], pos[1]); + assertHoverLabelContent([content, null]); + }); + } + + it('should generate hover label (base)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json')); + + run( + [200, 200], fig, + [['a = 0.200', 'b = 3.500', 'y = 2.900'], 'a = 0.2'] + ) + .then(done); + }); + + it('should generate hover label with \'hoverinfo\' set', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json')); + fig.data[5].hoverinfo = 'a+y'; + + run( + [200, 200], fig, + [['a = 0.200', 'y = 2.900'], null] + ) + .then(done); + }); +}); From 0a772397436c74b5b700beda21f2dac732da886c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 3 Oct 2017 16:30:52 -0400 Subject: [PATCH 05/13] fixes #2044 - add hoverinfo arrayOk support to ... ... scattergeo, choropleth, scatterternary and scattermapbox - these are all the trace type that rely on `extraText` built in their _module.hoverPoints method - this commit should've been part of PR #1761 --- src/traces/choropleth/hover.js | 12 +++++------ src/traces/scattercarpet/hover.js | 15 +++++++------- src/traces/scattergeo/hover.js | 24 ++++++++++++---------- src/traces/scattermapbox/hover.js | 21 +++++++++++-------- src/traces/scatterternary/hover.js | 17 ++++++++-------- test/jasmine/tests/carpet_test.js | 11 ++++++++++ test/jasmine/tests/choropleth_test.js | 12 +++++++++++ test/jasmine/tests/scattergeo_test.js | 6 ++++++ test/jasmine/tests/scattermapbox_test.js | 26 ++++++++++++++++++++++++ test/jasmine/tests/ternary_test.js | 11 ++++++++++ 10 files changed, 114 insertions(+), 41 deletions(-) diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js index 71799f22385..7ee444e244e 100644 --- a/src/traces/choropleth/hover.js +++ b/src/traces/choropleth/hover.js @@ -53,17 +53,17 @@ module.exports = function hoverPoints(pointData, xval, yval) { }; function makeHoverInfo(pointData, trace, pt, axis) { - var hoverinfo = trace.hoverinfo; + var hoverinfo = pt.hi || trace.hoverinfo; var parts = (hoverinfo === 'all') ? attributes.hoverinfo.flags : hoverinfo.split('+'); - var hasName = (parts.indexOf('name') !== -1), - hasLocation = (parts.indexOf('location') !== -1), - hasZ = (parts.indexOf('z') !== -1), - hasText = (parts.indexOf('text') !== -1), - hasIdAsNameLabel = !hasName && hasLocation; + var hasName = (parts.indexOf('name') !== -1); + var hasLocation = (parts.indexOf('location') !== -1); + var hasZ = (parts.indexOf('z') !== -1); + var hasText = (parts.indexOf('text') !== -1); + var hasIdAsNameLabel = !hasName && hasLocation; var text = []; diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js index 91ca66cd49f..d37283fc868 100644 --- a/src/traces/scattercarpet/hover.js +++ b/src/traces/scattercarpet/hover.js @@ -46,18 +46,19 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { newPointData.yLabelVal = undefined; // TODO: nice formatting, and label by axis title, for a, b, and c? - var trace = newPointData.trace, - carpet = trace._carpet, - hoverinfo = trace.hoverinfo.split('+'), - text = []; + var trace = newPointData.trace; + var carpet = trace._carpet; + var hoverinfo = cdi.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var text = []; function textPart(ax, val) { text.push(((ax.labelprefix && ax.labelprefix.length > 0) ? ax.labelprefix : (ax._hovertitle + ': ')) + val.toFixed(3) + ax.labelsuffix); } - if(hoverinfo.indexOf('all') !== -1) hoverinfo = ['a', 'b']; - if(hoverinfo.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a); - if(hoverinfo.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b); + if(parts.indexOf('all') !== -1) parts = ['a', 'b']; + if(parts.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a); + if(parts.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b); var ij = carpet.ab2ij([cdi.a, cdi.b]); var i0 = Math.floor(ij[0]); diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js index 4d3135d5d2a..681604494c0 100644 --- a/src/traces/scattergeo/hover.js +++ b/src/traces/scattergeo/hover.js @@ -71,29 +71,31 @@ module.exports = function hoverPoints(pointData, xval, yval) { }; function getExtraText(trace, pt, axis) { - var hoverinfo = trace.hoverinfo; + var hoverinfo = pt.hi || trace.hoverinfo; - var parts = (hoverinfo === 'all') ? + var parts = hoverinfo === 'all' ? attributes.hoverinfo.flags : hoverinfo.split('+'); - var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations), - hasLon = (parts.indexOf('lon') !== -1), - hasLat = (parts.indexOf('lat') !== -1), - hasText = (parts.indexOf('text') !== -1); - + var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations); + var hasLon = (parts.indexOf('lon') !== -1); + var hasLat = (parts.indexOf('lat') !== -1); + var hasText = (parts.indexOf('text') !== -1); var text = []; function format(val) { return Axes.tickText(axis, axis.c2l(val), 'hover').text + '\u00B0'; } - if(hasLocation) text.push(pt.loc); - else if(hasLon && hasLat) { + if(hasLocation) { + text.push(pt.loc); + } else if(hasLon && hasLat) { text.push('(' + format(pt.lonlat[0]) + ', ' + format(pt.lonlat[1]) + ')'); + } else if(hasLon) { + text.push('lon: ' + format(pt.lonlat[0])); + } else if(hasLat) { + text.push('lat: ' + format(pt.lonlat[1])); } - else if(hasLon) text.push('lon: ' + format(pt.lonlat[0])); - else if(hasLat) text.push('lat: ' + format(pt.lonlat[1])); if(hasText) { var tx; diff --git a/src/traces/scattermapbox/hover.js b/src/traces/scattermapbox/hover.js index 3b3f5434634..491d325f0a7 100644 --- a/src/traces/scattermapbox/hover.js +++ b/src/traces/scattermapbox/hover.js @@ -66,13 +66,14 @@ module.exports = function hoverPoints(pointData, xval, yval) { }; function getExtraText(trace, di) { - var hoverinfo = trace.hoverinfo.split('+'), - isAll = (hoverinfo.indexOf('all') !== -1), - hasLon = (hoverinfo.indexOf('lon') !== -1), - hasLat = (hoverinfo.indexOf('lat') !== -1); + var hoverinfo = di.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var isAll = parts.indexOf('all') !== -1; + var hasLon = parts.indexOf('lon') !== -1; + var hasLat = parts.indexOf('lat') !== -1; - var lonlat = di.lonlat, - text = []; + var lonlat = di.lonlat; + var text = []; // TODO should we use a mock axis to format hover? // If so, we'll need to make precision be zoom-level dependent @@ -82,11 +83,13 @@ function getExtraText(trace, di) { if(isAll || (hasLon && hasLat)) { text.push('(' + format(lonlat[0]) + ', ' + format(lonlat[1]) + ')'); + } else if(hasLon) { + text.push('lon: ' + format(lonlat[0])); + } else if(hasLat) { + text.push('lat: ' + format(lonlat[1])); } - else if(hasLon) text.push('lon: ' + format(lonlat[0])); - else if(hasLat) text.push('lat: ' + format(lonlat[1])); - if(isAll || hoverinfo.indexOf('text') !== -1) { + if(isAll || parts.indexOf('text') !== -1) { var tx; if(di.htx) tx = di.htx; diff --git a/src/traces/scatterternary/hover.js b/src/traces/scatterternary/hover.js index cdf459d2609..eed099c832d 100644 --- a/src/traces/scatterternary/hover.js +++ b/src/traces/scatterternary/hover.js @@ -49,19 +49,20 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { newPointData.yLabelVal = undefined; // TODO: nice formatting, and label by axis title, for a, b, and c? - var trace = newPointData.trace, - ternary = trace._ternary, - hoverinfo = trace.hoverinfo.split('+'), - text = []; + var trace = newPointData.trace; + var ternary = trace._ternary; + var hoverinfo = cdi.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var text = []; function textPart(ax, val) { text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); } - if(hoverinfo.indexOf('all') !== -1) hoverinfo = ['a', 'b', 'c']; - if(hoverinfo.indexOf('a') !== -1) textPart(ternary.aaxis, cdi.a); - if(hoverinfo.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b); - if(hoverinfo.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c); + if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c']; + if(parts.indexOf('a') !== -1) textPart(ternary.aaxis, cdi.a); + if(parts.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b); + if(parts.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c); newPointData.extraText = text.join('
'); diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js index c63439ac42f..62b62fef847 100644 --- a/test/jasmine/tests/carpet_test.js +++ b/test/jasmine/tests/carpet_test.js @@ -603,4 +603,15 @@ describe('scattercarpet hover labels', function() { ) .then(done); }); + + it('should generate hover label with arrayOk \'hoverinfo\' settings', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json')); + fig.data[5].hoverinfo = ['a+b', 'a+b', 'a+b', 'b+y']; + + run( + [200, 200], fig, + [['b = 3.500', 'y = 2.900'], null] + ) + .then(done); + }); }); diff --git a/test/jasmine/tests/choropleth_test.js b/test/jasmine/tests/choropleth_test.js index d6c7c561dba..ec470e07f55 100644 --- a/test/jasmine/tests/choropleth_test.js +++ b/test/jasmine/tests/choropleth_test.js @@ -128,4 +128,16 @@ describe('Test choropleth hover:', function() { ) .then(done); }); + + it('should generate hover label with arrayOk \'hoverinfo\' settings', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].hoverinfo = ['location', 'z', 'location+name']; + + run( + [400, 160], + fig, + ['RUS', 'trace 1'] + ) + .then(done); + }); }); diff --git a/test/jasmine/tests/scattergeo_test.js b/test/jasmine/tests/scattergeo_test.js index 480dff5346d..d1a2a7987ef 100644 --- a/test/jasmine/tests/scattergeo_test.js +++ b/test/jasmine/tests/scattergeo_test.js @@ -315,4 +315,10 @@ describe('Test scattergeo hover', function() { .then(done); }); + it('should generate hover label with arrayOk \'hoverinfo\' settings', function(done) { + Plotly.restyle(gd, 'hoverinfo', [['lon', null, 'lat+name']]).then(function() { + check([381, 221], ['lon: 10°', null]); + }) + .then(done); + }); }); diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 931f946326c..3c8b98e12d0 100644 --- a/test/jasmine/tests/scattermapbox_test.js +++ b/test/jasmine/tests/scattermapbox_test.js @@ -7,6 +7,7 @@ var convert = require('@src/traces/scattermapbox/convert'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var click = require('../assets/click'); @@ -604,6 +605,31 @@ describe('@noCI scattermapbox hover', function() { }) .then(done); }); + + it('should generate hover label (\'hoverinfo\' array case)', function(done) { + function check(expected) { + var out = hoverPoints(getPointData(gd), 11, 11)[0]; + expect(out.extraText).toEqual(expected); + } + + Plotly.restyle(gd, 'hoverinfo', [['lon', 'lat', 'lon+lat+name']]).then(function() { + check('lon: 10°'); + return Plotly.restyle(gd, 'hoverinfo', [['lat', 'lon', 'name']]); + }) + .then(function() { + check('lat: 10°'); + return Plotly.restyle(gd, 'hoverinfo', [['text', 'lon', 'name']]); + }) + .then(function() { + check('Apple'); + return Plotly.restyle(gd, 'hoverinfo', [[null, 'lon', 'name']]); + }) + .then(function() { + check('(10°, 10°)
Apple'); + }) + .catch(fail) + .then(done); + }); }); describe('@noCI Test plotly events on a scattermapbox plot:', function() { diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 6f4ee022c02..6b90f2f244b 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -145,6 +145,17 @@ describe('ternary plots', function() { fontSize: 13, fontFamily: 'Gravitas' }, 'after hoverlabel styling restyle call'); + + return Plotly.restyle(gd, 'hoverinfo', [['a', 'b+c', 'b']]); + }) + .then(function() { + check('Component A: 0.5', { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(0, 0, 255)', + fontColor: 'rgb(0, 0, 255)', + fontSize: 13, + fontFamily: 'Gravitas' + }, 'after hoverlabel styling restyle call'); }) .catch(fail) .then(done); From 85d706150893f0a16613aa43eae805cb0b260ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 3 Oct 2017 16:33:00 -0400 Subject: [PATCH 06/13] allow choropleth trace to have (trace-wide) string 'text' values --- src/traces/choropleth/attributes.js | 6 ++---- src/traces/choropleth/hover.js | 9 ++++++++- test/jasmine/tests/choropleth_test.js | 13 +++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/traces/choropleth/attributes.js b/src/traces/choropleth/attributes.js index 8c29ff7c384..cab58297206 100644 --- a/src/traces/choropleth/attributes.js +++ b/src/traces/choropleth/attributes.js @@ -34,11 +34,9 @@ module.exports = extendFlat({ editType: 'calc', description: 'Sets the color values.' }, - text: { - valType: 'data_array', - editType: 'calc', + text: extendFlat({}, ScatterGeoAttrs.text, { description: 'Sets the text elements associated with each location.' - }, + }), marker: { line: { color: ScatterGeoMarkerLineAttrs.color, diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js index 7ee444e244e..0acf1c27b23 100644 --- a/src/traces/choropleth/hover.js +++ b/src/traces/choropleth/hover.js @@ -79,7 +79,14 @@ function makeHoverInfo(pointData, trace, pt, axis) { } if(hasZ) text.push(formatter(pt.z)); - if(hasText) text.push(pt.tx); + if(hasText) { + var tx; + + if(pt.tx) tx = pt.tx; + else if(trace.text) tx = trace.text; + + if(!Array.isArray(tx)) text.push(tx); + } pointData.extraText = text.join('
'); } diff --git a/test/jasmine/tests/choropleth_test.js b/test/jasmine/tests/choropleth_test.js index ec470e07f55..066dc07c6d5 100644 --- a/test/jasmine/tests/choropleth_test.js +++ b/test/jasmine/tests/choropleth_test.js @@ -93,6 +93,19 @@ describe('Test choropleth hover:', function() { .then(done); }); + it('should generate hover label info (\'text\' single value case)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].text = 'tExT'; + fig.data[1].hoverinfo = 'text'; + + run( + [400, 160], + fig, + ['tExT', null] + ) + .then(done); + }); + it('should generate hover label info (\'text\' array case)', function(done) { var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); fig.data[1].text = ['tExT', 'TeXt', '-text-']; From bc8294ddb96a1a0f0d7827df768c56bb6114cce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 14:22:01 -0400 Subject: [PATCH 07/13] add Lib.extractOption - abstracting out calcdata-to-global-trace-fallback - use it in Drawing! - use it in hover label style logic - not super happy with the name - not super happy about it being on Lib --- src/components/drawing/index.js | 8 +++----- src/components/fx/hover.js | 26 ++++++++++++-------------- src/lib/index.js | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index c6213e9f219..ebaff9a83c1 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -443,12 +443,10 @@ drawing.tryColorscale = function(marker, prefix) { var TEXTOFFSETSIGN = {start: 1, end: -1, middle: 0, bottom: 1, top: -1}; drawing.textPointStyle = function(s, trace, gd) { s.each(function(d) { - var p = d3.select(this), - text = d.tx || trace.text; + var p = d3.select(this); + var text = Lib.extractOption(d, trace, 'tx', 'text'); - if(!text || Array.isArray(text)) { - // isArray test handles the case of (intentionally) missing - // or empty text within a text array + if(!text) { p.remove(); return; } diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 612ce0200c4..bc5866198bd 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1028,24 +1028,22 @@ function alignHoverText(hoverLabels, rotateLabels) { } function cleanPoint(d, hovermode) { + var index = d.index; var trace = d.trace || {}; var cd0 = d.cd[0]; - var cd = d.cd[d.index] || {}; + var cd = d.cd[index] || {}; + + var getVal = Array.isArray(index) ? + function(calcKey, traceKey) { + return Lib.castOption(cd0, index, calcKey) || + Lib.extractOption({}, trace, '', traceKey); + } : + function(calcKey, traceKey) { + return Lib.extractOption(cd, trace, calcKey, traceKey); + }; function fill(key, calcKey, traceKey) { - var val; - - if(cd[calcKey]) { - val = cd[calcKey]; - } else if(cd0[calcKey]) { - var arr = cd0[calcKey]; - if(Array.isArray(arr) && Array.isArray(arr[d.index[0]])) { - val = arr[d.index[0]][d.index[1]]; - } - } else { - val = Lib.nestedProperty(trace, traceKey).get(); - } - + var val = getVal(calcKey, traceKey); if(val) d[key] = val; } diff --git a/src/lib/index.js b/src/lib/index.js index 8e0e2abe17f..a7f683f03e9 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -437,6 +437,27 @@ lib.castOption = function(trace, ptNumber, astr, fn) { } }; +/** Extract option from calcdata item, correctly falling back to + * trace value if not found. + * + * @param {object} calcPt : calcdata[i][j] item + * @param {object} trace : (full) trace object + * @param {string} calcKey : calcdata key + * @param {string} traceKey : aka trace attribute string + * @return {any} + */ +lib.extractOption = function(calcPt, trace, calcKey, traceKey) { + var calcVal = calcPt[calcKey]; + if(calcVal || calcVal === 0) return calcVal; + + // fallback to trace value, + // must check if value isn't itself an array + // which means the trace attribute has a corresponding + // calcdata key, but its value is falsy + var traceVal = lib.nestedProperty(trace, traceKey).get(); + if(!Array.isArray(traceVal)) return traceVal; +}; + /** Returns target as set by 'target' transform attribute * * @param {object} trace : full trace object From b637179d57accf0338cab709c582f9868d6c0769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 14:23:26 -0400 Subject: [PATCH 08/13] add fillHoverText routine - which handles calc/trace-wide 'hovertext'/'text' precedence logic - use it in hover.js modules --- src/traces/bar/hover.js | 8 ++---- src/traces/scatter/fill_hover_text.js | 36 +++++++++++++++++++++++++++ src/traces/scatter/hover.js | 8 ++---- src/traces/scattergeo/hover.js | 11 ++------ src/traces/scattermapbox/hover.js | 10 ++------ 5 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 src/traces/scatter/fill_hover_text.js diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js index b718dc4b139..971798d6334 100644 --- a/src/traces/bar/hover.js +++ b/src/traces/bar/hover.js @@ -12,7 +12,7 @@ var Fx = require('../../components/fx'); var ErrorBars = require('../../components/errorbars'); var Color = require('../../components/color'); - +var fillHoverText = require('../scatter/fill_hover_text'); module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var cd = pointData.cd; @@ -99,11 +99,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { pointData.xLabelVal = di.p; } - if(di.htx) pointData.text = di.htx; - else if(trace.hovertext) pointData.text = trace.hovertext; - else if(di.tx) pointData.text = di.tx; - else if(trace.text) pointData.text = trace.text; - + fillHoverText(di, trace, pointData); ErrorBars.hoverInfo(di, trace, pointData); return [pointData]; diff --git a/src/traces/scatter/fill_hover_text.js b/src/traces/scatter/fill_hover_text.js new file mode 100644 index 00000000000..24587f84afd --- /dev/null +++ b/src/traces/scatter/fill_hover_text.js @@ -0,0 +1,36 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = require('../../lib'); + +/** Fill hover 'pointData' container with 'correct' hover text value + * + * - If trace hoverinfo contains a 'text' flag and hovertext is not set, + * the text elements will be seen in the hover labels. + * + * - If trace hoverinfo contains a 'text' flag and hovertext is set, + * hovertext takes precedence over text + * i.e. the hoverinfo elements will be seen in the hover labels + * + * @param {object} calcPt + * @param {object} trace + * @param {object || array} contOut (mutated here) + */ +module.exports = function fillHoverText(calcPt, trace, contOut) { + var fill = Array.isArray(contOut) ? + function(v) { contOut.push(v); } : + function(v) { contOut.text = v; }; + + var htx = Lib.extractOption(calcPt, trace, 'htx', 'hovertext'); + if(htx && htx !== '') return fill(htx); + + var tx = Lib.extractOption(calcPt, trace, 'tx', 'text'); + if(tx && tx !== '') return fill(tx); +}; diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index ee968926c64..601a30c2e47 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Lib = require('../../lib'); @@ -14,6 +13,7 @@ var Fx = require('../../components/fx'); var ErrorBars = require('../../components/errorbars'); var getTraceColor = require('./get_trace_color'); var Color = require('../../components/color'); +var fillHoverText = require('./fill_hover_text'); var MAXDIST = Fx.constants.MAXDIST; @@ -73,11 +73,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { yLabelVal: di.y }); - if(di.htx) pointData.text = di.htx; - else if(trace.hovertext) pointData.text = trace.hovertext; - else if(di.tx) pointData.text = di.tx; - else if(trace.text) pointData.text = trace.text; - + fillHoverText(di, trace, pointData); ErrorBars.hoverInfo(di, trace, pointData); return [pointData]; diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js index 681604494c0..56aa35c359d 100644 --- a/src/traces/scattergeo/hover.js +++ b/src/traces/scattergeo/hover.js @@ -14,9 +14,9 @@ var Axes = require('../../plots/cartesian/axes'); var BADNUM = require('../../constants/numerical').BADNUM; var getTraceColor = require('../scatter/get_trace_color'); +var fillHoverText = require('../scatter/fill_hover_text'); var attributes = require('./attributes'); - module.exports = function hoverPoints(pointData, xval, yval) { var cd = pointData.cd; var trace = cd[0].trace; @@ -98,14 +98,7 @@ function getExtraText(trace, pt, axis) { } if(hasText) { - var tx; - - if(pt.htx) tx = pt.htx; - else if(trace.hovertext) tx = trace.hovertext; - else if(pt.tx) tx = pt.tx; - else if(trace.text) tx = trace.text; - - if(!Array.isArray(tx)) text.push(tx); + fillHoverText(pt, trace, text); } return text.join('
'); diff --git a/src/traces/scattermapbox/hover.js b/src/traces/scattermapbox/hover.js index 491d325f0a7..d9805ed152a 100644 --- a/src/traces/scattermapbox/hover.js +++ b/src/traces/scattermapbox/hover.js @@ -11,6 +11,7 @@ var Fx = require('../../components/fx'); var getTraceColor = require('../scatter/get_trace_color'); +var fillHoverText = require('../scatter/fill_hover_text'); var BADNUM = require('../../constants/numerical').BADNUM; module.exports = function hoverPoints(pointData, xval, yval) { @@ -90,14 +91,7 @@ function getExtraText(trace, di) { } if(isAll || parts.indexOf('text') !== -1) { - var tx; - - if(di.htx) tx = di.htx; - else if(trace.hovertext) tx = trace.hovertext; - else if(di.tx) tx = di.tx; - else if(trace.text) tx = trace.text; - - if(!Array.isArray(tx)) text.push(tx); + fillHoverText(di, trace, text); } return text.join('
'); From 294714b7585bf585f115285577ebdb0f21290e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 15:44:32 -0400 Subject: [PATCH 09/13] improve assertHoverLabelContent API --- test/jasmine/assets/custom_assertions.js | 73 +++--- test/jasmine/tests/carpet_test.js | 5 +- test/jasmine/tests/choropleth_test.js | 14 +- test/jasmine/tests/gl2d_click_test.js | 5 +- test/jasmine/tests/gl_plot_interact_test.js | 2 +- test/jasmine/tests/hover_label_test.js | 234 +++++++++----------- test/jasmine/tests/mapbox_test.js | 7 +- test/jasmine/tests/pie_test.js | 37 +++- test/jasmine/tests/scattergeo_test.js | 22 +- test/jasmine/tests/ternary_test.js | 6 +- 10 files changed, 213 insertions(+), 192 deletions(-) diff --git a/test/jasmine/assets/custom_assertions.js b/test/jasmine/assets/custom_assertions.js index a2d90df5844..aa594999e82 100644 --- a/test/jasmine/assets/custom_assertions.js +++ b/test/jasmine/assets/custom_assertions.js @@ -60,88 +60,99 @@ exports.assertHoverLabelStyle = function(g, expectation, msg, textSelector) { }; function assertLabelContent(label, expectation, msg) { - var lines = label.selectAll('tspan'); - var lineCnt = lines.size(); - var expectMultiLine = Array.isArray(expectation); + if(!expectation) expectation = ''; - function extract(sel) { - return sel.node() ? sel.html() : null; - } + var lines = label.selectAll('tspan.line'); + var content = []; - if(lineCnt > 0) { - if(expectMultiLine) { - expect(lines.size()).toBe(expectation.length, msg + ': # of lines'); - lines.each(function(_, i) { - var l = d3.select(this); - expect(extract(l)).toBe(expectation[i], msg + ': tspan line ' + i); - }); - } else { - fail('Expected a single-line label, found multiple lines'); + function fill(sel) { + if(sel.node()) { + var html = sel.html(); + if(html) content.push(html); } + } + + if(lines.size()) { + lines.each(function() { fill(d3.select(this)); }); } else { - if(!expectMultiLine) { - expect(extract(label)).toBe(expectation, msg + ': text content'); - } else { - fail('Expected a multi-line label, found single'); - } + fill(label); } + + expect(content.join('\n')).toBe(expectation, msg + ': text content'); } function count(selector) { return d3.selectAll(selector).size(); } +/** + * @param {object} expectation + * - nums {string || array of strings} + * - name {string || array of strings} + * - axis {string} + * @param {string} msg + */ exports.assertHoverLabelContent = function(expectation, msg) { if(!msg) msg = ''; var ptSelector = 'g.hovertext'; - var ptExpectation = expectation[0]; var ptMsg = msg + ' point hover label'; var ptCnt = count(ptSelector); var axSelector = 'g.axistext'; - var axExpectation = expectation[1]; var axMsg = 'common axis hover label'; var axCnt = count(axSelector); if(ptCnt === 1) { assertLabelContent( d3.select(ptSelector + '> text.nums'), - ptExpectation[0], + expectation.nums, ptMsg + ' (nums)' ); assertLabelContent( d3.select(ptSelector + '> text.name'), - ptExpectation[1], + expectation.name, ptMsg + ' (name)' ); } else if(ptCnt > 1) { - expect(ptCnt).toBe(ptExpectation.length, ptMsg + ' # of visible nodes'); + if(!Array.isArray(expectation.nums) || !Array.isArray(expectation.name)) { + fail(ptMsg + ': expecting more than 1 labels.'); + } + + expect(ptCnt) + .toBe(expectation.name.length, ptMsg + ' # of visible labels'); d3.selectAll(ptSelector).each(function(_, i) { assertLabelContent( d3.select(this).select('text.nums'), - ptExpectation[i][0], + expectation.nums[i], ptMsg + ' (nums ' + i + ')' ); assertLabelContent( d3.select(this).select('text.name'), - ptExpectation[i][1], + expectation.name[i], ptMsg + ' (name ' + i + ')' ); }); } else { - expect(ptExpectation).toBe(null, ptMsg + ' should not be displayed'); + if(expectation.nums) { + fail(ptMsg + ': expecting *nums* labels, did not find any.'); + } + if(expectation.name) { + fail(ptMsg + ': expecting *nums* labels, did not find any.'); + } } - if(axCnt > 0) { + if(axCnt) { assertLabelContent( d3.select(axSelector + '> text'), - axExpectation, + expectation.axis, axMsg ); } else { - expect(axExpectation).toBe(null, axMsg + ' should not be displayed'); + if(expectation.axis) { + fail(axMsg + ': expecting label, did not find any.'); + } } }; diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js index 62b62fef847..9837bca563f 100644 --- a/test/jasmine/tests/carpet_test.js +++ b/test/jasmine/tests/carpet_test.js @@ -579,7 +579,10 @@ describe('scattercarpet hover labels', function() { return Plotly.plot(gd, fig).then(function() { mouseEvent('mousemove', pos[0], pos[1]); - assertHoverLabelContent([content, null]); + assertHoverLabelContent({ + nums: content[0].join('\n'), + name: content[1] + }); }); } diff --git a/test/jasmine/tests/choropleth_test.js b/test/jasmine/tests/choropleth_test.js index 066dc07c6d5..1612bde7ae2 100644 --- a/test/jasmine/tests/choropleth_test.js +++ b/test/jasmine/tests/choropleth_test.js @@ -77,8 +77,14 @@ describe('Test choropleth hover:', function() { return Plotly.plot(gd, fig).then(function() { mouseEvent('mousemove', pos[0], pos[1]); - assertHoverLabelContent([content, null]); - assertHoverLabelStyle(d3.select('g.hovertext'), style); + assertHoverLabelContent({ + nums: content[0], + name: content[1] + }); + assertHoverLabelStyle( + d3.select('g.hovertext'), + style + ); }); } @@ -88,7 +94,7 @@ describe('Test choropleth hover:', function() { run( [400, 160], fig, - [['RUS', '10', ''], 'trace 1'] + ['RUS\n10', 'trace 1'] ) .then(done); }); @@ -130,7 +136,7 @@ describe('Test choropleth hover:', function() { run( [400, 160], fig, - [['RUS', '10', ''], 'trace 1'], + ['RUS\n10', 'trace 1'], { bgcolor: 'rgb(255, 0, 0)', bordercolor: 'rgb(0, 128, 0)', diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index e05f3682b9b..54dd23ee1aa 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -158,7 +158,10 @@ describe('Test hover and click interactions', function() { assertHoverLabelStyle(g, expected, opts.msg); } if(expected.label) { - assertHoverLabelContent([expected.label, null]); + assertHoverLabelContent({ + nums: expected.label[0], + name: expected.label[1] + }); } }) .then(_click) diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index 776a2e4c2d1..8d2cf6a11c3 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -38,7 +38,7 @@ describe('Test gl3d plots', function() { function assertHoverText(xLabel, yLabel, zLabel, textLabel) { var content = [xLabel, yLabel, zLabel]; if(textLabel) content.push(textLabel); - assertHoverLabelContent([[content, null], null]); + assertHoverLabelContent({nums: content.join('\n')}); } function assertEventData(x, y, z, curveNumber, pointNumber, extra) { diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index c413116fd31..b6be796bdbe 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -43,10 +43,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - ['1', null], - '0.388' - ]); + assertHoverLabelContent({ + nums: '1', + axis: '0.388' + }); }); }); @@ -70,10 +70,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - null, - '0.388' - ]); + assertHoverLabelContent({axis: '0.388'}); }); }); @@ -97,10 +94,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - ['1', null], - null - ]); + assertHoverLabelContent({nums: '1'}); }); }); @@ -128,10 +122,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - ['hover text with spaces not newlines', null], - null - ]); + assertHoverLabelContent({ + nums: 'hover text with spaces not newlines' + }); }); }); @@ -157,10 +150,11 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - [['1', 'hover text'], 'PV learning ...'], - '0.388' - ]); + assertHoverLabelContent({ + nums: '1\nhover text', + name: 'PV learning ...', + axis: '0.388' + }); }); }); @@ -193,10 +187,11 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - [['1', 'hover text'], '<img src=x o...'], - '0.388' - ]); + assertHoverLabelContent({ + nums: '1\nhover text', + name: '<img src=x o...', + axis: '0.388' + }); }); }); @@ -216,10 +211,7 @@ describe('hover info', function() { it('responds to hover y', function() { Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent([ - ['1e+9', null], - null - ]); + assertHoverLabelContent({nums: '1e+9'}); }); }); @@ -245,10 +237,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - [['1', 'hover text'], null], - null - ]); + assertHoverLabelContent({ + nums: '1\nhover text' + }); }); }); @@ -274,10 +265,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - ['hover text', null], - '0.388' - ]); + assertHoverLabelContent({ + nums: 'hover text', + axis: '0.388' + }); }); }); @@ -294,10 +285,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent([ - ['1', null], - '0.388 ± 1' - ]); + assertHoverLabelContent({ + nums: '1', + axis: '0.388 ± 1' + }); }); }); @@ -314,10 +305,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent([ - ['1', null], - '0.388' - ]); + assertHoverLabelContent({ + nums: '1', + axis: '0.388' + }); }); }); @@ -334,10 +325,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent([ - ['1', null], - '0.388' - ]); + assertHoverLabelContent({ + nums: '1', + axis: '0.388' + }); }); }); @@ -363,10 +354,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([ - [['hover', 'text'], null], - null - ]); + assertHoverLabelContent({ + nums: 'hover\ntext' + }); }); }); @@ -384,7 +374,7 @@ describe('hover info', function() { Fx.hover('graph', evt, 'xy'); expect(gd._hoverdata, undefined); - assertHoverLabelContent([null, null]); + assertHoverLabelContent({}); }); }); @@ -408,7 +398,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent([null, null]); + assertHoverLabelContent({}); }); }); @@ -439,10 +429,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.33); expect(hoverTrace.y).toEqual(1.25); - assertHoverLabelContent([ - ['(0.33, 1.25)', 'PV learning ...'], - null - ]); + assertHoverLabelContent({ + nums: '(0.33, 1.25)', + name: 'PV learning ...' + }); }); it('render only non-hoverinfo \'none\' hover labels', function(done) { @@ -460,10 +450,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.33); expect(hoverTrace.y).toEqual(1.25); - assertHoverLabelContent([ - ['PV learning ...', null], - null - ]); + assertHoverLabelContent({ + nums: 'PV learning ...' + }); }) .then(done); }); @@ -495,17 +484,17 @@ describe('hover info', function() { }) .then(function() { _hover(gd, 250, 100); - assertHoverLabelContent([ - [['x: 1', 'y: 3', 'z: 2'], 'two'], - null - ]); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); }) .then(function() { _hover(gd, 250, 300); - assertHoverLabelContent([ - [['x: 1', 'y: 1', 'z: 2'], 'one'], - null - ]); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 2', + name: 'one' + }); }) .catch(fail) .then(done); @@ -531,10 +520,10 @@ describe('hover info', function() { Plotly.plot(this.gd, data, layout); mouseEvent('mousemove', 303, 213); - assertHoverLabelContent([ - ['0.23', null], - '2' - ]); + assertHoverLabelContent({ + nums: '0.23', + axis: '2' + }); }); it('should display the correct format when ticklabels false', function() { @@ -542,10 +531,10 @@ describe('hover info', function() { Plotly.plot(this.gd, data, layout); mouseEvent('mousemove', 303, 213); - assertHoverLabelContent([ - ['0.23', null], - '2' - ]); + assertHoverLabelContent({ + nums: '0.23', + axis: '2' + }); }); }); @@ -568,28 +557,26 @@ describe('hover info', function() { it('should show text labels', function() { mouseEvent('mousemove', 108, 303); - assertHoverLabelContent([ - ['test', null], - null - ]); + assertHoverLabelContent({ + nums: 'test' + }); }); it('should show number labels', function() { mouseEvent('mousemove', 363, 173); - assertHoverLabelContent([ - ['42', null], - null - ]); + assertHoverLabelContent({ + nums: '42' + }); }); it('should not show null text labels', function() { mouseEvent('mousemove', 229, 239); - assertHoverLabelContent([null, null]); + assertHoverLabelContent({}); }); it('should not show undefined text labels', function() { mouseEvent('mousemove', 493, 108); - assertHoverLabelContent([null, null]); + assertHoverLabelContent({}); }); }); @@ -683,13 +670,14 @@ describe('hover info on stacked subplots', function() { y: 1000 })); - assertHoverLabelContent([ + assertHoverLabelContent({ // There should be 2 pts being hovered over, // in two different traces, one in each plot. - [['110', 'trace 1'], ['1000', 'trace 2']], + nums: ['110', '1000'], + name: ['trace 1', 'trace 2'], // There should be a single label on the x-axis with the shared x value, 3' - '3' - ]); + axis: '3' + }); }); }); @@ -736,20 +724,15 @@ describe('hover info on stacked subplots', function() { y: 0 })); - // There should be a single label on the y-axis with the shared y value, 0. - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0'); - // There should be three points being hovered over, in three different traces, one in each plot. - expect(d3.selectAll('g.hovertext').size()).toEqual(3); - var textNodes = d3.selectAll('g.hovertext').selectAll('text'); - - expect(textNodes[0][0].innerHTML).toEqual('Who put the bomp in the bomp bah bomp bah bomp'); - expect(textNodes[0][1].innerHTML).toEqual('1'); - expect(textNodes[1][0].innerHTML).toEqual('Wh'); - expect(textNodes[1][1].innerHTML).toEqual('2.1'); - expect(textNodes[2][0].innerHTML).toEqual('Who put...'); - expect(textNodes[2][1].innerHTML).toEqual('3'); + assertHoverLabelContent({ + // There should be three points being hovered over, in three different traces, + // one in each plot. + nums: ['1', '2.1', '3'], + name: ['Who put the bomp in the bomp bah bomp bah bomp', 'Wh', 'Who put...'], + // There should be a single label on the y-axis with the shared y value, 0. + axis: '0' + }); }); }); }); @@ -766,22 +749,13 @@ describe('hover info on overlaid subplots', function() { Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(function() { mouseEvent('mousemove', 768, 345); - var axisText = d3.selectAll('g.axistext'), - hoverText = d3.selectAll('g.hovertext'); - - expect(axisText.size()).toEqual(1, 'with 1 label on axis'); - expect(hoverText.size()).toEqual(2, 'with 2 labels on the overlaid pts'); - - expect(axisText.select('text').html()).toEqual('1', 'with correct axis label'); - - var textNodes = hoverText.selectAll('text'); - - expect(textNodes[0][0].innerHTML).toEqual('Take Rate', 'with correct hover labels'); - expect(textNodes[0][1].innerHTML).toEqual('0.35', 'with correct hover labels'); - expect(textNodes[1][0].innerHTML).toEqual('Revenue', 'with correct hover labels'); - expect(textNodes[1][1].innerHTML).toEqual('2,352.5', 'with correct hover labels'); - - }).then(done); + assertHoverLabelContent({ + nums: ['0.35', '2,352.5'], + name: ['Take Rate', 'Revenue'], + axis: '1' + }); + }) + .then(done); }); }); @@ -803,7 +777,11 @@ describe('hover after resizing', function() { function check(pos, expectation, msg) { Lib.clearThrottle(); mouseEvent('mousemove', pos[0], pos[1]); - assertHoverLabelContent(expectation, msg); + assertHoverLabelContent({ + nums: expectation[0], + name: expectation[1], + axis: expectation[2] + }, msg); } it('should work', function(done) { @@ -820,28 +798,28 @@ describe('hover after resizing', function() { return _click(pos0); }) .then(function() { - return check(pos0, [['1', null], '1'], 'before resize, showing pt label'); + return check(pos0, ['1', null, '1'], 'before resize, showing pt label'); }) .then(function() { - return check(pos1, [null, null], 'before resize, not showing blank spot'); + return check(pos1, [null, null, null], 'before resize, not showing blank spot'); }) .then(function() { return Plotly.relayout(gd, 'width', 500); }) .then(function() { - return check(pos0, [null, null], 'after resize, not showing blank spot'); + return check(pos0, [null, null, null], 'after resize, not showing blank spot'); }) .then(function() { - return check(pos1, [['2', null], '2'], 'after resize, showing pt label'); + return check(pos1, ['2', null, '2'], 'after resize, showing pt label'); }) .then(function() { return Plotly.relayout(gd, 'width', 600); }) .then(function() { - return check(pos0, [['1', null], '1'], 'back to initial, showing pt label'); + return check(pos0, ['1', null, '1'], 'back to initial, showing pt label'); }) .then(function() { - return check(pos1, [null, null], 'back to initial, not showing blank spot'); + return check(pos1, [null, null, null], 'back to initial, not showing blank spot'); }) .then(done); }); diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js index d40efef9bef..ae7ec7a9cf3 100644 --- a/test/jasmine/tests/mapbox_test.js +++ b/test/jasmine/tests/mapbox_test.js @@ -735,9 +735,10 @@ describe('@noCI, mapbox plots', function() { fontFamily: 'Arial', fontColor: 'rgb(68, 68, 68)' }); - assertHoverLabelContent([ - [['(10°, 10°)', ''], 'trace 0'], null - ]); + assertHoverLabelContent({ + nums: '(10°, 10°)', + name: 'trace 0' + }); }) .catch(failTest) .then(done); diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index 80ba76f532b..4a9f675cf72 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -248,10 +248,7 @@ describe('pie hovering', function() { } function assertLabel(content, style, msg) { - assertHoverLabelContent([ - [content, null], - null - ], msg); + assertHoverLabelContent({nums: content}, msg); if(style) { assertHoverLabelStyle(d3.select('.hovertext'), { @@ -269,7 +266,7 @@ describe('pie hovering', function() { .then(_hover) .then(function() { assertLabel( - ['4', '5', '33.3%'], + ['4', '5', '33.3%'].join('\n'), ['rgb(31, 119, 180)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)'], 'initial' ); @@ -278,7 +275,11 @@ describe('pie hovering', function() { }) .then(_hover) .then(function() { - assertLabel(['4', 'E', '5', '33.3%'], null, 'added text'); + assertLabel( + ['4', 'E', '5', '33.3%'].join('\n'), + null, + 'added text' + ); return Plotly.restyle(gd, 'hovertext', [[ 'Apple', 'Banana', 'Clementine', 'Dragon Fruit', 'Eggplant' @@ -286,13 +287,21 @@ describe('pie hovering', function() { }) .then(_hover) .then(function() { - assertLabel(['4', 'Eggplant', '5', '33.3%'], null, 'added hovertext'); + assertLabel( + ['4', 'Eggplant', '5', '33.3%'].join('\n'), + null, + 'added hovertext' + ); return Plotly.restyle(gd, 'hovertext', 'SUP'); }) .then(_hover) .then(function() { - assertLabel(['4', 'SUP', '5', '33.3%'], null, 'constant hovertext'); + assertLabel( + ['4', 'SUP', '5', '33.3%'].join('\n'), + null, + 'constant hovertext' + ); return Plotly.restyle(gd, { 'hoverlabel.bgcolor': [['red', 'green', 'blue', 'yellow', 'red']], @@ -305,7 +314,7 @@ describe('pie hovering', function() { .then(_hover) .then(function() { assertLabel( - ['4', 'SUP', '5', '33.3%'], + ['4', 'SUP', '5', '33.3%'].join('\n'), ['rgb(255, 0, 0)', 'rgb(255, 255, 0)', 15, 'Roboto', 'rgb(0, 0, 255)'], 'new styles' ); @@ -314,13 +323,17 @@ describe('pie hovering', function() { }) .then(_hover) .then(function() { - assertLabel(['4', '33.3%'], null, 'new hoverinfo'); + assertLabel(['4', '33.3%'].join('\n'), null, 'new hoverinfo'); return Plotly.restyle(gd, 'hoverinfo', [[null, null, null, null, 'dont+know+what+im-doing']]); }) .then(_hover) .then(function() { - assertLabel(['4', 'SUP', '5', '33.3%'], null, 'garbage hoverinfo'); + assertLabel( + ['4', 'SUP', '5', '33.3%'].join('\n'), + null, + 'garbage hoverinfo' + ); }) .catch(fail) .then(done); @@ -334,7 +347,7 @@ describe('pie hovering', function() { Plotly.plot(gd, mockCopy.data, mockCopy.layout) .then(_hover) .then(function() { - assertLabel(['0', '12|345|678@91', '99@9%']); + assertLabel('0\n12|345|678@91\n99@9%'); }) .then(done); }); diff --git a/test/jasmine/tests/scattergeo_test.js b/test/jasmine/tests/scattergeo_test.js index d1a2a7987ef..e8ab62a8a87 100644 --- a/test/jasmine/tests/scattergeo_test.js +++ b/test/jasmine/tests/scattergeo_test.js @@ -262,38 +262,44 @@ describe('Test scattergeo hover', function() { fontFamily: 'Arial' }; - assertHoverLabelContent([content, null]); - assertHoverLabelStyle(d3.select('g.hovertext'), style); + assertHoverLabelContent({ + nums: content[0], + name: content[1] + }); + assertHoverLabelStyle( + d3.select('g.hovertext'), + style + ); } it('should generate hover label info (base case)', function() { - check([381, 221], [['(10°, 10°)', 'A'], null]); + check([381, 221], ['(10°, 10°)\nA', null]); }); it('should generate hover label info (with trace name)', function(done) { Plotly.restyle(gd, 'hoverinfo', 'lon+lat+text+name').then(function() { - check([381, 221], [['(10°, 10°)', 'A'], 'trace 0']); + check([381, 221], ['(10°, 10°)\nA', 'trace 0']); }) .then(done); }); it('should generate hover label info (\'text\' single value case)', function(done) { Plotly.restyle(gd, 'text', 'text').then(function() { - check([381, 221], [['(10°, 10°)', 'text'], null]); + check([381, 221], ['(10°, 10°)\ntext', null]); }) .then(done); }); it('should generate hover label info (\'hovertext\' single value case)', function(done) { Plotly.restyle(gd, 'hovertext', 'hovertext').then(function() { - check([381, 221], [['(10°, 10°)', 'hovertext'], null]); + check([381, 221], ['(10°, 10°)\nhovertext', null]); }) .then(done); }); it('should generate hover label info (\'hovertext\' array case)', function(done) { Plotly.restyle(gd, 'hovertext', ['Apple', 'Banana', 'Orange']).then(function() { - check([381, 221], [['(10°, 10°)', 'Apple'], null]); + check([381, 221], ['(10°, 10°)\nApple', null]); }) .then(done); }); @@ -304,7 +310,7 @@ describe('Test scattergeo hover', function() { 'hoverlabel.bordercolor': [['blue', 'black', 'green']] }) .then(function() { - check([381, 221], [['(10°, 10°)', 'A'], null], { + check([381, 221], ['(10°, 10°)\nA', null], { bgcolor: 'rgb(255, 0, 0)', bordercolor: 'rgb(0, 0, 255)', fontColor: 'rgb(0, 0, 255)', diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 6b90f2f244b..45a4ee75c86 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -113,7 +113,7 @@ describe('ternary plots', function() { Lib.clearThrottle(); mouseEvent('mousemove', pointPos[0], pointPos[1]); - assertHoverLabelContent([[content, null], null], msg); + assertHoverLabelContent({nums: content}, msg); assertHoverLabelStyle(d3.select('g.hovertext'), style, msg); } @@ -121,7 +121,7 @@ describe('ternary plots', function() { 'Component A: 0.5', 'B: 0.25', 'Component C: 0.25' - ], { + ].join('\n'), { bgcolor: 'rgb(31, 119, 180)', bordercolor: 'rgb(255, 255, 255)', fontColor: 'rgb(255, 255, 255)', @@ -138,7 +138,7 @@ describe('ternary plots', function() { 'Component A: 0.5', 'B: 0.25', 'Component C: 0.25' - ], { + ].join('\n'), { bgcolor: 'rgb(31, 119, 180)', bordercolor: 'rgb(0, 0, 255)', fontColor: 'rgb(0, 0, 255)', From 2fe641d8045de9eb55497cd2ff294f63a0a01fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 15:53:17 -0400 Subject: [PATCH 10/13] replace ' = ' for ': ' in scattercarpet hover label prefixes --- src/traces/scattercarpet/hover.js | 12 ++++++++++-- test/jasmine/tests/carpet_test.js | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js index d37283fc868..5e0bf79d29f 100644 --- a/src/traces/scattercarpet/hover.js +++ b/src/traces/scattercarpet/hover.js @@ -53,7 +53,15 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var text = []; function textPart(ax, val) { - text.push(((ax.labelprefix && ax.labelprefix.length > 0) ? ax.labelprefix : (ax._hovertitle + ': ')) + val.toFixed(3) + ax.labelsuffix); + var prefix; + + if(ax.labelprefix && ax.labelprefix.length > 0) { + prefix = ax.labelprefix.replace(/ = $/, ''); + } else { + prefix = ax._hovertitle; + } + + text.push(prefix + ': ' + val.toFixed(3) + ax.labelsuffix); } if(parts.indexOf('all') !== -1) parts = ['a', 'b']; @@ -68,7 +76,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var tj = ij[1] - j0; var xy = carpet.evalxy([], i0, j0, ti, tj); - text.push('y = ' + xy[1].toFixed(3)); + text.push('y: ' + xy[1].toFixed(3)); newPointData.extraText = text.join('
'); diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js index 9837bca563f..4e2164188da 100644 --- a/test/jasmine/tests/carpet_test.js +++ b/test/jasmine/tests/carpet_test.js @@ -591,7 +591,7 @@ describe('scattercarpet hover labels', function() { run( [200, 200], fig, - [['a = 0.200', 'b = 3.500', 'y = 2.900'], 'a = 0.2'] + [['a: 0.200', 'b: 3.500', 'y: 2.900'], 'a = 0.2'] ) .then(done); }); @@ -602,7 +602,7 @@ describe('scattercarpet hover labels', function() { run( [200, 200], fig, - [['a = 0.200', 'y = 2.900'], null] + [['a: 0.200', 'y: 2.900'], null] ) .then(done); }); @@ -613,7 +613,7 @@ describe('scattercarpet hover labels', function() { run( [200, 200], fig, - [['b = 3.500', 'y = 2.900'], null] + [['b: 3.500', 'y: 2.900'], null] ) .then(done); }); From 7f2604a6987925e0f211fe1f35b0821f0832dafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 16:33:44 -0400 Subject: [PATCH 11/13] use extractOption in choropleth hover --- src/traces/choropleth/hover.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js index 0acf1c27b23..ccd327d3891 100644 --- a/src/traces/choropleth/hover.js +++ b/src/traces/choropleth/hover.js @@ -9,6 +9,7 @@ 'use strict'; +var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); var attributes = require('./attributes'); @@ -80,12 +81,8 @@ function makeHoverInfo(pointData, trace, pt, axis) { if(hasZ) text.push(formatter(pt.z)); if(hasText) { - var tx; - - if(pt.tx) tx = pt.tx; - else if(trace.text) tx = trace.text; - - if(!Array.isArray(tx)) text.push(tx); + var tx = Lib.extractOption(pt, trace, 'tx', 'text'); + if(tx && tx !== '') text.push(tx); } pointData.extraText = text.join('
'); From 6a44a9ae079b4306779092d73526c08d98a539e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 18:22:49 -0400 Subject: [PATCH 12/13] fix panning on text chart with invalid array values --- src/components/drawing/index.js | 3 +++ test/jasmine/tests/drawing_test.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index ebaff9a83c1..b949d8d8393 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -887,6 +887,9 @@ drawing.setTextPointsScale = function(selection, xScale, yScale) { var transforms; var el = d3.select(this); var text = el.select('text'); + + if(!text.node()) return; + var x = parseFloat(text.attr('x') || 0); var y = parseFloat(text.attr('y') || 0); diff --git a/test/jasmine/tests/drawing_test.js b/test/jasmine/tests/drawing_test.js index 899ecef6527..5b5049857f5 100644 --- a/test/jasmine/tests/drawing_test.js +++ b/test/jasmine/tests/drawing_test.js @@ -345,6 +345,11 @@ describe('Drawing', function() { Drawing.setTextPointsScale(g, 4, 5); expect(g.attr('transform')).toEqual('translate(8,9) scale(4,5) translate(-8,-9) translate(1, 2)'); }); + + it('should not break when is not present', function() { + text.remove(); + expect(function() { Drawing.setTextPointsScale(g, 4, 5); }).not.toThrow(); + }); }); describe('bBox', function() { From 3931273b331971085d39ce5bbb1fa7e9d4720c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 4 Oct 2017 23:04:47 -0400 Subject: [PATCH 13/13] make extractOption less opinionated - by returning falsy calcdata values - use fillHoverText in choropleth/hover, for consistency --- src/lib/index.js | 3 +-- src/traces/choropleth/hover.js | 5 ++--- src/traces/scatter/fill_hover_text.js | 9 +++++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib/index.js b/src/lib/index.js index a7f683f03e9..cbfc190adb3 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -447,8 +447,7 @@ lib.castOption = function(trace, ptNumber, astr, fn) { * @return {any} */ lib.extractOption = function(calcPt, trace, calcKey, traceKey) { - var calcVal = calcPt[calcKey]; - if(calcVal || calcVal === 0) return calcVal; + if(calcKey in calcPt) return calcPt[calcKey]; // fallback to trace value, // must check if value isn't itself an array diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js index ccd327d3891..deda1b54b7e 100644 --- a/src/traces/choropleth/hover.js +++ b/src/traces/choropleth/hover.js @@ -9,9 +9,9 @@ 'use strict'; -var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); var attributes = require('./attributes'); +var fillHoverText = require('../scatter/fill_hover_text'); module.exports = function hoverPoints(pointData, xval, yval) { var cd = pointData.cd; @@ -81,8 +81,7 @@ function makeHoverInfo(pointData, trace, pt, axis) { if(hasZ) text.push(formatter(pt.z)); if(hasText) { - var tx = Lib.extractOption(pt, trace, 'tx', 'text'); - if(tx && tx !== '') text.push(tx); + fillHoverText(pt, trace, text); } pointData.extraText = text.join('
'); diff --git a/src/traces/scatter/fill_hover_text.js b/src/traces/scatter/fill_hover_text.js index 24587f84afd..945f8e8960e 100644 --- a/src/traces/scatter/fill_hover_text.js +++ b/src/traces/scatter/fill_hover_text.js @@ -29,8 +29,13 @@ module.exports = function fillHoverText(calcPt, trace, contOut) { function(v) { contOut.text = v; }; var htx = Lib.extractOption(calcPt, trace, 'htx', 'hovertext'); - if(htx && htx !== '') return fill(htx); + if(isValid(htx)) return fill(htx); var tx = Lib.extractOption(calcPt, trace, 'tx', 'text'); - if(tx && tx !== '') return fill(tx); + if(isValid(tx)) return fill(tx); }; + +// accept all truthy values and 0 (which gets cast to '0' in the hover labels) +function isValid(v) { + return v || v === 0; +}